aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDmitry Shmidt <dimitrysh@google.com>2011-05-09 14:06:53 -0700
committerDmitry Shmidt <dimitrysh@google.com>2011-05-09 14:24:08 -0700
commit8d520ff1dc2da35cdca849e982051b86468016d8 (patch)
tree3e33700a20ffe9064c3de3e9efa3a9dfcebb7b03 /src
parent7acd11a82b3521a1ec0ef3976af4786b02642e7e (diff)
downloadexternal_wpa_supplicant_8-8d520ff1dc2da35cdca849e982051b86468016d8.zip
external_wpa_supplicant_8-8d520ff1dc2da35cdca849e982051b86468016d8.tar.gz
external_wpa_supplicant_8-8d520ff1dc2da35cdca849e982051b86468016d8.tar.bz2
wpa_supplicant: Initial Revision 0.8.X
Based on: commit 0725cc7b7efc434910e89865c42eda7ce61bbf08 Author: Jouni Malinen <j@w1.fi> Date: Thu Apr 21 20:41:01 2011 +0300 Enable CONFIG_DRIVER_NL80211=y in the default configuration nl80211 should be preferred over WEXT with any recent Linux kernel version. Change-Id: I26aec5afbbd4f4a1f5fd900912545b6f5050de64 Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile11
-rw-r--r--src/ap/Makefile8
-rw-r--r--src/ap/accounting.c505
-rw-r--r--src/ap/accounting.h45
-rw-r--r--src/ap/ap_config.c627
-rw-r--r--src/ap/ap_config.h417
-rw-r--r--src/ap/ap_drv_ops.c632
-rw-r--r--src/ap/ap_drv_ops.h197
-rw-r--r--src/ap/ap_list.c399
-rw-r--r--src/ap/ap_list.h78
-rw-r--r--src/ap/ap_mlme.c184
-rw-r--r--src/ap/ap_mlme.h40
-rw-r--r--src/ap/authsrv.c217
-rw-r--r--src/ap/authsrv.h21
-rw-r--r--src/ap/beacon.c535
-rw-r--r--src/ap/beacon.h36
-rw-r--r--src/ap/ctrl_iface_ap.c108
-rw-r--r--src/ap/ctrl_iface_ap.h25
-rw-r--r--src/ap/drv_callbacks.c539
-rw-r--r--src/ap/hostapd.c929
-rw-r--r--src/ap/hostapd.h262
-rw-r--r--src/ap/hw_features.c751
-rw-r--r--src/ap/hw_features.h70
-rw-r--r--src/ap/iapp.c535
-rw-r--r--src/ap/iapp.h45
-rw-r--r--src/ap/ieee802_11.c1882
-rw-r--r--src/ap/ieee802_11.h68
-rw-r--r--src/ap/ieee802_11_auth.c524
-rw-r--r--src/ap/ieee802_11_auth.h31
-rw-r--r--src/ap/ieee802_11_ht.c267
-rw-r--r--src/ap/ieee802_1x.c2085
-rw-r--r--src/ap/ieee802_1x.h89
-rw-r--r--src/ap/p2p_hostapd.c120
-rw-r--r--src/ap/p2p_hostapd.h41
-rw-r--r--src/ap/peerkey_auth.c402
-rw-r--r--src/ap/pmksa_cache_auth.c425
-rw-r--r--src/ap/pmksa_cache_auth.h64
-rw-r--r--src/ap/preauth_auth.c279
-rw-r--r--src/ap/preauth_auth.h58
-rw-r--r--src/ap/sta_info.c796
-rw-r--r--src/ap/sta_info.h165
-rw-r--r--src/ap/tkip_countermeasures.c94
-rw-r--r--src/ap/tkip_countermeasures.h20
-rw-r--r--src/ap/utils.c88
-rw-r--r--src/ap/vlan_init.c905
-rw-r--r--src/ap/vlan_init.h59
-rw-r--r--src/ap/wmm.c327
-rw-r--r--src/ap/wmm.h29
-rw-r--r--src/ap/wpa_auth.c2838
-rw-r--r--src/ap/wpa_auth.h285
-rw-r--r--src/ap/wpa_auth_ft.c1779
-rw-r--r--src/ap/wpa_auth_glue.c571
-rw-r--r--src/ap/wpa_auth_glue.h22
-rw-r--r--src/ap/wpa_auth_i.h234
-rw-r--r--src/ap/wpa_auth_ie.c824
-rw-r--r--src/ap/wpa_auth_ie.h56
-rw-r--r--src/ap/wps_hostapd.c1380
-rw-r--r--src/ap/wps_hostapd.h72
-rw-r--r--src/common/Makefile8
-rw-r--r--src/common/defs.h270
-rw-r--r--src/common/eapol_common.h47
-rw-r--r--src/common/ieee802_11_common.c347
-rw-r--r--src/common/ieee802_11_common.h81
-rw-r--r--src/common/ieee802_11_defs.h800
-rw-r--r--src/common/privsep_commands.h75
-rw-r--r--src/common/version.h10
-rw-r--r--src/common/wpa_common.c927
-rw-r--r--src/common/wpa_common.h361
-rw-r--r--src/common/wpa_ctrl.c500
-rw-r--r--src/common/wpa_ctrl.h274
-rw-r--r--src/crypto/.gitignore1
-rw-r--r--src/crypto/Makefile56
-rw-r--r--src/crypto/aes-cbc.c86
-rw-r--r--src/crypto/aes-ctr.c61
-rw-r--r--src/crypto/aes-eax.c151
-rw-r--r--src/crypto/aes-encblock.c38
-rw-r--r--src/crypto/aes-internal-dec.c151
-rw-r--r--src/crypto/aes-internal-enc.c121
-rw-r--r--src/crypto/aes-internal.c805
-rw-r--r--src/crypto/aes-omac1.c124
-rw-r--r--src/crypto/aes-unwrap.c79
-rw-r--r--src/crypto/aes-wrap.c76
-rw-r--r--src/crypto/aes.h27
-rw-r--r--src/crypto/aes_i.h122
-rw-r--r--src/crypto/aes_wrap.h48
-rw-r--r--src/crypto/crypto.h469
-rw-r--r--src/crypto/crypto_cryptoapi.c789
-rw-r--r--src/crypto/crypto_gnutls.c305
-rw-r--r--src/crypto/crypto_internal-cipher.c256
-rw-r--r--src/crypto/crypto_internal-modexp.c55
-rw-r--r--src/crypto/crypto_internal-rsa.c115
-rw-r--r--src/crypto/crypto_internal.c205
-rw-r--r--src/crypto/crypto_libtomcrypt.c732
-rw-r--r--src/crypto/crypto_none.c29
-rw-r--r--src/crypto/crypto_nss.c213
-rw-r--r--src/crypto/crypto_openssl.c505
-rw-r--r--src/crypto/des-internal.c499
-rw-r--r--src/crypto/des_i.h31
-rw-r--r--src/crypto/dh_group5.c40
-rw-r--r--src/crypto/dh_group5.h23
-rw-r--r--src/crypto/dh_groups.c633
-rw-r--r--src/crypto/dh_groups.h32
-rw-r--r--src/crypto/fips_prf_cryptoapi.c25
-rw-r--r--src/crypto/fips_prf_gnutls.c26
-rw-r--r--src/crypto/fips_prf_internal.c74
-rw-r--r--src/crypto/fips_prf_nss.c25
-rw-r--r--src/crypto/fips_prf_openssl.c83
-rw-r--r--src/crypto/md4-internal.c278
-rw-r--r--src/crypto/md5-internal.c293
-rw-r--r--src/crypto/md5-non-fips.c113
-rw-r--r--src/crypto/md5.c111
-rw-r--r--src/crypto/md5.h35
-rw-r--r--src/crypto/md5_i.h29
-rw-r--r--src/crypto/milenage.c329
-rw-r--r--src/crypto/milenage.h33
-rw-r--r--src/crypto/ms_funcs.c476
-rw-r--r--src/crypto/ms_funcs.h64
-rw-r--r--src/crypto/random.c337
-rw-r--r--src/crypto/random.h34
-rw-r--r--src/crypto/rc4.c60
-rw-r--r--src/crypto/sha1-internal.c308
-rw-r--r--src/crypto/sha1-pbkdf2.c100
-rw-r--r--src/crypto/sha1-tlsprf.c109
-rw-r--r--src/crypto/sha1-tprf.c76
-rw-r--r--src/crypto/sha1.c163
-rw-r--r--src/crypto/sha1.h33
-rw-r--r--src/crypto/sha1_i.h29
-rw-r--r--src/crypto/sha256-internal.c243
-rw-r--r--src/crypto/sha256.c157
-rw-r--r--src/crypto/sha256.h27
-rw-r--r--src/crypto/tls.h569
-rw-r--r--src/crypto/tls_gnutls.c1457
-rw-r--r--src/crypto/tls_internal.c651
-rw-r--r--src/crypto/tls_none.c229
-rw-r--r--src/crypto/tls_nss.c680
-rw-r--r--src/crypto/tls_openssl.c2992
-rw-r--r--src/crypto/tls_schannel.c767
-rw-r--r--src/drivers/.gitignore2
-rw-r--r--src/drivers/Apple80211.h156
-rw-r--r--src/drivers/Makefile9
-rw-r--r--src/drivers/MobileApple80211.c189
-rw-r--r--src/drivers/MobileApple80211.h43
-rw-r--r--src/drivers/driver.h3230
-rw-r--r--src/drivers/driver_atheros.c1381
-rw-r--r--src/drivers/driver_broadcom.c599
-rw-r--r--src/drivers/driver_bsd.c1573
-rw-r--r--src/drivers/driver_hostap.c1648
-rw-r--r--src/drivers/driver_hostap.h216
-rw-r--r--src/drivers/driver_iphone.m466
-rw-r--r--src/drivers/driver_madwifi.c1856
-rw-r--r--src/drivers/driver_ndis.c3331
-rw-r--r--src/drivers/driver_ndis.h65
-rw-r--r--src/drivers/driver_ndis_.c105
-rw-r--r--src/drivers/driver_nl80211.c6550
-rw-r--r--src/drivers/driver_none.c99
-rw-r--r--src/drivers/driver_osx.m459
-rw-r--r--src/drivers/driver_privsep.c758
-rw-r--r--src/drivers/driver_ralink.c1498
-rw-r--r--src/drivers/driver_ralink.h383
-rw-r--r--src/drivers/driver_roboswitch.c480
-rw-r--r--src/drivers/driver_test.c3391
-rw-r--r--src/drivers/driver_wext.c2320
-rw-r--r--src/drivers/driver_wext.h87
-rw-r--r--src/drivers/driver_wired.c629
-rw-r--r--src/drivers/drivers.c114
-rw-r--r--src/drivers/drivers.mak180
-rw-r--r--src/drivers/drivers.mk183
-rw-r--r--src/drivers/linux_ioctl.c198
-rw-r--r--src/drivers/linux_ioctl.h27
-rw-r--r--src/drivers/ndis_events.c808
-rw-r--r--src/drivers/netlink.c204
-rw-r--r--src/drivers/netlink.h34
-rw-r--r--src/drivers/nl80211_copy.h1939
-rw-r--r--src/drivers/priv_netlink.h113
-rw-r--r--src/drivers/rfkill.c194
-rw-r--r--src/drivers/rfkill.h31
-rw-r--r--src/drivers/wireless_copy.h1185
-rw-r--r--src/eap_common/Makefile8
-rw-r--r--src/eap_common/chap.c34
-rw-r--r--src/eap_common/chap.h23
-rw-r--r--src/eap_common/eap_common.c184
-rw-r--r--src/eap_common/eap_common.h28
-rw-r--r--src/eap_common/eap_defs.h86
-rw-r--r--src/eap_common/eap_fast_common.c304
-rw-r--r--src/eap_common/eap_fast_common.h113
-rw-r--r--src/eap_common/eap_gpsk_common.c423
-rw-r--r--src/eap_common/eap_gpsk_common.h66
-rw-r--r--src/eap_common/eap_ikev2_common.c132
-rw-r--r--src/eap_common/eap_ikev2_common.h42
-rw-r--r--src/eap_common/eap_pax_common.c150
-rw-r--r--src/eap_common/eap_pax_common.h97
-rw-r--r--src/eap_common/eap_peap_common.c88
-rw-r--r--src/eap_common/eap_peap_common.h22
-rw-r--r--src/eap_common/eap_psk_common.c74
-rw-r--r--src/eap_common/eap_psk_common.h78
-rw-r--r--src/eap_common/eap_pwd_common.c312
-rw-r--r--src/eap_common/eap_pwd_common.h79
-rw-r--r--src/eap_common/eap_sake_common.c393
-rw-r--r--src/eap_common/eap_sake_common.h102
-rw-r--r--src/eap_common/eap_sim_common.c1215
-rw-r--r--src/eap_common/eap_sim_common.h235
-rw-r--r--src/eap_common/eap_tlv_common.h118
-rw-r--r--src/eap_common/eap_ttls.h71
-rw-r--r--src/eap_common/eap_wsc_common.c39
-rw-r--r--src/eap_common/eap_wsc_common.h33
-rw-r--r--src/eap_common/ikev2_common.c797
-rw-r--r--src/eap_common/ikev2_common.h344
-rw-r--r--src/eap_peer/Makefile11
-rw-r--r--src/eap_peer/eap.c2159
-rw-r--r--src/eap_peer/eap.h292
-rw-r--r--src/eap_peer/eap_aka.c1389
-rw-r--r--src/eap_peer/eap_config.h669
-rw-r--r--src/eap_peer/eap_fast.c1712
-rw-r--r--src/eap_peer/eap_fast_pac.c923
-rw-r--r--src/eap_peer/eap_fast_pac.h56
-rw-r--r--src/eap_peer/eap_gpsk.c738
-rw-r--r--src/eap_peer/eap_gtc.c151
-rw-r--r--src/eap_peer/eap_i.h356
-rw-r--r--src/eap_peer/eap_ikev2.c506
-rw-r--r--src/eap_peer/eap_leap.c416
-rw-r--r--src/eap_peer/eap_md5.c120
-rw-r--r--src/eap_peer/eap_methods.c373
-rw-r--r--src/eap_peer/eap_methods.h114
-rw-r--r--src/eap_peer/eap_mschapv2.c883
-rw-r--r--src/eap_peer/eap_otp.c107
-rw-r--r--src/eap_peer/eap_pax.c531
-rw-r--r--src/eap_peer/eap_peap.c1288
-rw-r--r--src/eap_peer/eap_psk.c483
-rw-r--r--src/eap_peer/eap_pwd.c744
-rw-r--r--src/eap_peer/eap_sake.c500
-rw-r--r--src/eap_peer/eap_sim.c1101
-rw-r--r--src/eap_peer/eap_tls.c289
-rw-r--r--src/eap_peer/eap_tls_common.c1021
-rw-r--r--src/eap_peer/eap_tls_common.h126
-rw-r--r--src/eap_peer/eap_tnc.c434
-rw-r--r--src/eap_peer/eap_ttls.c1986
-rw-r--r--src/eap_peer/eap_vendor_test.c195
-rw-r--r--src/eap_peer/eap_wsc.c553
-rw-r--r--src/eap_peer/ikev2.c1304
-rw-r--r--src/eap_peer/ikev2.h65
-rw-r--r--src/eap_peer/mschapv2.c123
-rw-r--r--src/eap_peer/mschapv2.h34
-rw-r--r--src/eap_peer/tncc.c1369
-rw-r--r--src/eap_peer/tncc.h42
-rw-r--r--src/eap_server/Makefile8
-rw-r--r--src/eap_server/eap.h128
-rw-r--r--src/eap_server/eap_i.h201
-rw-r--r--src/eap_server/eap_methods.h54
-rw-r--r--src/eap_server/eap_server.c1384
-rw-r--r--src/eap_server/eap_server_aka.c1278
-rw-r--r--src/eap_server/eap_server_fast.c1620
-rw-r--r--src/eap_server/eap_server_gpsk.c634
-rw-r--r--src/eap_server/eap_server_gtc.c230
-rw-r--r--src/eap_server/eap_server_identity.c180
-rw-r--r--src/eap_server/eap_server_ikev2.c539
-rw-r--r--src/eap_server/eap_server_md5.c177
-rw-r--r--src/eap_server/eap_server_methods.c175
-rw-r--r--src/eap_server/eap_server_mschapv2.c575
-rw-r--r--src/eap_server/eap_server_pax.c570
-rw-r--r--src/eap_server/eap_server_peap.c1387
-rw-r--r--src/eap_server/eap_server_psk.c518
-rw-r--r--src/eap_server/eap_server_pwd.c844
-rw-r--r--src/eap_server/eap_server_sake.c543
-rw-r--r--src/eap_server/eap_server_sim.c798
-rw-r--r--src/eap_server/eap_server_tls.c286
-rw-r--r--src/eap_server/eap_server_tls_common.c400
-rw-r--r--src/eap_server/eap_server_tnc.c582
-rw-r--r--src/eap_server/eap_server_ttls.c1430
-rw-r--r--src/eap_server/eap_server_vendor_test.c198
-rw-r--r--src/eap_server/eap_server_wsc.c517
-rw-r--r--src/eap_server/eap_sim_db.c1338
-rw-r--r--src/eap_server/eap_sim_db.h91
-rw-r--r--src/eap_server/eap_tls_common.h91
-rw-r--r--src/eap_server/ikev2.c1206
-rw-r--r--src/eap_server/ikev2.h67
-rw-r--r--src/eap_server/tncs.c1273
-rw-r--r--src/eap_server/tncs.h49
-rw-r--r--src/eapol_auth/Makefile8
-rw-r--r--src/eapol_auth/eapol_auth_dump.c231
-rw-r--r--src/eapol_auth/eapol_auth_sm.c1145
-rw-r--r--src/eapol_auth/eapol_auth_sm.h92
-rw-r--r--src/eapol_auth/eapol_auth_sm_i.h183
-rw-r--r--src/eapol_supp/Makefile8
-rw-r--r--src/eapol_supp/eapol_supp_sm.c1913
-rw-r--r--src/eapol_supp/eapol_supp_sm.h352
-rw-r--r--src/l2_packet/Makefile8
-rw-r--r--src/l2_packet/l2_packet.h130
-rw-r--r--src/l2_packet/l2_packet_freebsd.c316
-rw-r--r--src/l2_packet/l2_packet_linux.c210
-rw-r--r--src/l2_packet/l2_packet_ndis.c522
-rw-r--r--src/l2_packet/l2_packet_none.c123
-rw-r--r--src/l2_packet/l2_packet_pcap.c386
-rw-r--r--src/l2_packet/l2_packet_privsep.c267
-rw-r--r--src/l2_packet/l2_packet_winpcap.c341
-rw-r--r--src/lib.rules21
-rw-r--r--src/p2p/Makefile9
-rw-r--r--src/p2p/p2p.c3490
-rw-r--r--src/p2p/p2p.h1473
-rw-r--r--src/p2p/p2p_build.c431
-rw-r--r--src/p2p/p2p_dev_disc.c365
-rw-r--r--src/p2p/p2p_go_neg.c1127
-rw-r--r--src/p2p/p2p_group.c673
-rw-r--r--src/p2p/p2p_i.h638
-rw-r--r--src/p2p/p2p_invitation.c489
-rw-r--r--src/p2p/p2p_parse.c718
-rw-r--r--src/p2p/p2p_pd.c347
-rw-r--r--src/p2p/p2p_sd.c951
-rw-r--r--src/p2p/p2p_utils.c271
-rw-r--r--src/radius/.gitignore1
-rw-r--r--src/radius/Makefile22
-rw-r--r--src/radius/radius.c1317
-rw-r--r--src/radius/radius.h273
-rw-r--r--src/radius/radius_client.c1499
-rw-r--r--src/radius/radius_client.h265
-rw-r--r--src/radius/radius_server.c1527
-rw-r--r--src/radius/radius_server.h217
-rw-r--r--src/rsn_supp/Makefile8
-rw-r--r--src/rsn_supp/peerkey.c1186
-rw-r--r--src/rsn_supp/peerkey.h87
-rw-r--r--src/rsn_supp/pmksa_cache.c476
-rw-r--r--src/rsn_supp/pmksa_cache.h127
-rw-r--r--src/rsn_supp/preauth.c518
-rw-r--r--src/rsn_supp/preauth.h85
-rw-r--r--src/rsn_supp/tdls.c2069
-rw-r--r--src/rsn_supp/wpa.c2644
-rw-r--r--src/rsn_supp/wpa.h351
-rw-r--r--src/rsn_supp/wpa_ft.c1039
-rw-r--r--src/rsn_supp/wpa_i.h290
-rw-r--r--src/rsn_supp/wpa_ie.c447
-rw-r--r--src/rsn_supp/wpa_ie.h60
-rw-r--r--src/tls/.gitignore1
-rw-r--r--src/tls/Makefile37
-rw-r--r--src/tls/asn1.c212
-rw-r--r--src/tls/asn1.h72
-rw-r--r--src/tls/bignum.c230
-rw-r--r--src/tls/bignum.h38
-rw-r--r--src/tls/libtommath.c3381
-rw-r--r--src/tls/pkcs1.c201
-rw-r--r--src/tls/pkcs1.h28
-rw-r--r--src/tls/pkcs5.c238
-rw-r--r--src/tls/pkcs5.h22
-rw-r--r--src/tls/pkcs8.c193
-rw-r--r--src/tls/pkcs8.h22
-rw-r--r--src/tls/rsa.c358
-rw-r--r--src/tls/rsa.h29
-rw-r--r--src/tls/tlsv1_client.c667
-rw-r--r--src/tls/tlsv1_client.h59
-rw-r--r--src/tls/tlsv1_client_i.h87
-rw-r--r--src/tls/tlsv1_client_read.c976
-rw-r--r--src/tls/tlsv1_client_write.c798
-rw-r--r--src/tls/tlsv1_common.c241
-rw-r--r--src/tls/tlsv1_common.h216
-rw-r--r--src/tls/tlsv1_cred.c493
-rw-r--r--src/tls/tlsv1_cred.h46
-rw-r--r--src/tls/tlsv1_record.c409
-rw-r--r--src/tls/tlsv1_record.h74
-rw-r--r--src/tls/tlsv1_server.c592
-rw-r--r--src/tls/tlsv1_server.h54
-rw-r--r--src/tls/tlsv1_server_i.h77
-rw-r--r--src/tls/tlsv1_server_read.c1134
-rw-r--r--src/tls/tlsv1_server_write.c791
-rw-r--r--src/tls/x509v3.c1985
-rw-r--r--src/tls/x509v3.h129
-rw-r--r--src/utils/.gitignore1
-rw-r--r--src/utils/Makefile39
-rw-r--r--src/utils/base64.c154
-rw-r--r--src/utils/base64.h23
-rw-r--r--src/utils/build_config.h105
-rw-r--r--src/utils/common.c387
-rw-r--r--src/utils/common.h502
-rw-r--r--src/utils/edit.c1161
-rw-r--r--src/utils/edit.h27
-rw-r--r--src/utils/edit_readline.c184
-rw-r--r--src/utils/edit_simple.c96
-rw-r--r--src/utils/eloop.c627
-rw-r--r--src/utils/eloop.h316
-rw-r--r--src/utils/eloop_none.c401
-rw-r--r--src/utils/eloop_win.c623
-rw-r--r--src/utils/includes.h59
-rw-r--r--src/utils/ip_addr.c83
-rw-r--r--src/utils/ip_addr.h34
-rw-r--r--src/utils/list.h98
-rw-r--r--src/utils/os.h508
-rw-r--r--src/utils/os_internal.c471
-rw-r--r--src/utils/os_none.c226
-rw-r--r--src/utils/os_unix.c474
-rw-r--r--src/utils/os_win32.c222
-rw-r--r--src/utils/pcsc_funcs.c1238
-rw-r--r--src/utils/pcsc_funcs.h68
-rw-r--r--src/utils/radiotap.c287
-rw-r--r--src/utils/radiotap.h242
-rw-r--r--src/utils/radiotap_iter.h41
-rw-r--r--src/utils/state_machine.h144
-rw-r--r--src/utils/trace.c329
-rw-r--r--src/utils/trace.h74
-rw-r--r--src/utils/uuid.c77
-rw-r--r--src/utils/uuid.h24
-rw-r--r--src/utils/wpa_debug.c484
-rw-r--r--src/utils/wpa_debug.h307
-rw-r--r--src/utils/wpabuf.c304
-rw-r--r--src/utils/wpabuf.h168
-rw-r--r--src/wps/Makefile8
-rw-r--r--src/wps/http.h29
-rw-r--r--src/wps/http_client.c374
-rw-r--r--src/wps/http_client.h46
-rw-r--r--src/wps/http_server.c312
-rw-r--r--src/wps/http_server.h39
-rw-r--r--src/wps/httpread.c861
-rw-r--r--src/wps/httpread.h123
-rw-r--r--src/wps/ndef.c175
-rw-r--r--src/wps/upnp_xml.c252
-rw-r--r--src/wps/upnp_xml.h23
-rw-r--r--src/wps/wps.c627
-rw-r--r--src/wps/wps.h964
-rw-r--r--src/wps/wps_attr_build.c422
-rw-r--r--src/wps/wps_attr_parse.c630
-rw-r--r--src/wps/wps_attr_process.c335
-rw-r--r--src/wps/wps_common.c704
-rw-r--r--src/wps/wps_defs.h336
-rw-r--r--src/wps/wps_dev_attr.c444
-rw-r--r--src/wps/wps_dev_attr.h44
-rw-r--r--src/wps/wps_enrollee.c1350
-rw-r--r--src/wps/wps_er.c1959
-rw-r--r--src/wps/wps_er.h117
-rw-r--r--src/wps/wps_er_ssdp.c211
-rw-r--r--src/wps/wps_i.h301
-rw-r--r--src/wps/wps_nfc.c117
-rw-r--r--src/wps/wps_nfc_pn531.c113
-rw-r--r--src/wps/wps_registrar.c3273
-rw-r--r--src/wps/wps_ufd.c235
-rw-r--r--src/wps/wps_upnp.c1210
-rw-r--r--src/wps/wps_upnp.h48
-rw-r--r--src/wps/wps_upnp_ap.c91
-rw-r--r--src/wps/wps_upnp_event.c423
-rw-r--r--src/wps/wps_upnp_i.h193
-rw-r--r--src/wps/wps_upnp_ssdp.c938
-rw-r--r--src/wps/wps_upnp_web.c1324
-rw-r--r--src/wps/wps_validate.c1981
438 files changed, 207520 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..d73a175
--- /dev/null
+++ b/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
+
+all:
+ for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
+
+clean:
+ for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
+ rm -f *~
+
+install:
+ for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done
diff --git a/src/ap/Makefile b/src/ap/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/ap/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
new file mode 100644
index 0000000..dbfb058
--- /dev/null
+++ b/src/ap/accounting.c
@@ -0,0 +1,505 @@
+/*
+ * hostapd / RADIUS Accounting
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "drivers/driver.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "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_get_id(struct hostapd_data *hapd,
+ struct sta_info *sta);
+
+
+static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ int status_type)
+{
+ struct radius_msg *msg;
+ char buf[128];
+ u8 *val;
+ size_t len;
+ int i;
+
+ msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
+ radius_client_get_id(hapd->radius));
+ if (msg == NULL) {
+ printf("Could not create net RADIUS packet\n");
+ return NULL;
+ }
+
+ if (sta) {
+ radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
+
+ os_snprintf(buf, sizeof(buf), "%08X-%08X",
+ sta->acct_session_id_hi, sta->acct_session_id_lo);
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ printf("Could not add Acct-Session-Id\n");
+ goto fail;
+ }
+ } else {
+ radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd));
+ }
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
+ status_type)) {
+ printf("Could not add Acct-Status-Type\n");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
+ hapd->conf->ieee802_1x ?
+ RADIUS_ACCT_AUTHENTIC_RADIUS :
+ RADIUS_ACCT_AUTHENTIC_LOCAL)) {
+ printf("Could not add Acct-Authentic\n");
+ goto fail;
+ }
+
+ if (sta) {
+ val = ieee802_1x_get_identity(sta->eapol_sm, &len);
+ if (!val) {
+ os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
+ MAC2STR(sta->addr));
+ val = (u8 *) buf;
+ len = os_strlen(buf);
+ }
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
+ len)) {
+ printf("Could not add User-Name\n");
+ goto fail;
+ }
+ }
+
+ if (hapd->conf->own_ip_addr.af == AF_INET &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
+ printf("Could not add NAS-IP-Address\n");
+ goto fail;
+ }
+
+#ifdef CONFIG_IPV6
+ if (hapd->conf->own_ip_addr.af == AF_INET6 &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
+ printf("Could not add NAS-IPv6-Address\n");
+ goto fail;
+ }
+#endif /* CONFIG_IPV6 */
+
+ if (hapd->conf->nas_identifier &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+ (u8 *) hapd->conf->nas_identifier,
+ os_strlen(hapd->conf->nas_identifier))) {
+ printf("Could not add NAS-Identifier\n");
+ goto fail;
+ }
+
+ if (sta &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+ printf("Could not add NAS-Port\n");
+ goto fail;
+ }
+
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+ MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ printf("Could not add Called-Station-Id\n");
+ goto fail;
+ }
+
+ if (sta) {
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(sta->addr));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ printf("Could not add Calling-Station-Id\n");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr_int32(
+ msg, RADIUS_ATTR_NAS_PORT_TYPE,
+ RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+ printf("Could not add NAS-Port-Type\n");
+ goto fail;
+ }
+
+ os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
+ radius_sta_rate(hapd, sta) / 2,
+ (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
+ radius_mode_txt(hapd));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+ (u8 *) buf, os_strlen(buf))) {
+ printf("Could not add Connect-Info\n");
+ goto fail;
+ }
+
+ for (i = 0; ; i++) {
+ val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
+ i);
+ if (val == NULL)
+ break;
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
+ val, len)) {
+ printf("Could not add Class\n");
+ goto fail;
+ }
+ }
+ }
+
+ return msg;
+
+ fail:
+ radius_msg_free(msg);
+ return NULL;
+}
+
+
+static int accounting_sta_update_stats(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct hostap_sta_driver_data *data)
+{
+ if (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;
+ int interval;
+
+ if (sta->acct_session_started)
+ return;
+
+ accounting_sta_get_id(hapd, sta);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "starting accounting session %08X-%08X",
+ sta->acct_session_id_hi, sta->acct_session_id_lo);
+
+ time(&sta->acct_session_start);
+ sta->last_rx_bytes = sta->last_tx_bytes = 0;
+ sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
+ hostapd_drv_sta_clear_stats(hapd, sta->addr);
+
+ if (!hapd->conf->radius->acct_server)
+ return;
+
+ if (sta->acct_interim_interval)
+ interval = sta->acct_interim_interval;
+ else
+ interval = ACCT_DEFAULT_UPDATE_INTERVAL;
+ eloop_register_timeout(interval, 0, accounting_interim_update,
+ hapd, sta);
+
+ msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
+ if (msg)
+ radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr);
+
+ sta->acct_session_started = 1;
+}
+
+
+static void accounting_sta_report(struct hostapd_data *hapd,
+ struct sta_info *sta, int stop)
+{
+ struct radius_msg *msg;
+ int cause = sta->acct_terminate_cause;
+ struct hostap_sta_driver_data data;
+ 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) {
+ printf("Could not create RADIUS Accounting message\n");
+ return;
+ }
+
+ os_get_time(&now);
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
+ now.sec - sta->acct_session_start)) {
+ printf("Could not add Acct-Session-Time\n");
+ goto fail;
+ }
+
+ if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_INPUT_PACKETS,
+ data.rx_packets)) {
+ printf("Could not add Acct-Input-Packets\n");
+ goto fail;
+ }
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
+ data.tx_packets)) {
+ printf("Could not add Acct-Output-Packets\n");
+ goto fail;
+ }
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_INPUT_OCTETS,
+ data.rx_bytes)) {
+ printf("Could not add Acct-Input-Octets\n");
+ goto fail;
+ }
+ gigawords = sta->acct_input_gigawords;
+#if __WORDSIZE == 64
+ gigawords += data.rx_bytes >> 32;
+#endif
+ if (gigawords &&
+ !radius_msg_add_attr_int32(
+ msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
+ gigawords)) {
+ printf("Could not add Acct-Input-Gigawords\n");
+ goto fail;
+ }
+ if (!radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
+ data.tx_bytes)) {
+ printf("Could not add Acct-Output-Octets\n");
+ goto fail;
+ }
+ gigawords = sta->acct_output_gigawords;
+#if __WORDSIZE == 64
+ gigawords += data.tx_bytes >> 32;
+#endif
+ if (gigawords &&
+ !radius_msg_add_attr_int32(
+ msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
+ gigawords)) {
+ printf("Could not add Acct-Output-Gigawords\n");
+ goto fail;
+ }
+ }
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+ now.sec)) {
+ printf("Could not add Event-Timestamp\n");
+ goto fail;
+ }
+
+ if (eloop_terminated())
+ cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
+
+ if (stop && cause &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+ cause)) {
+ printf("Could not add Acct-Terminate-Cause\n");
+ goto fail;
+ }
+
+ radius_client_send(hapd->radius, msg,
+ stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
+ sta->addr);
+ return;
+
+ fail:
+ radius_msg_free(msg);
+}
+
+
+/**
+ * accounting_sta_interim - Send a interim STA accounting report
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (sta->acct_session_started)
+ accounting_sta_report(hapd, sta, 0);
+}
+
+
+/**
+ * accounting_sta_stop - Stop STA accounting
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (sta->acct_session_started) {
+ accounting_sta_report(hapd, sta, 1);
+ eloop_cancel_timeout(accounting_interim_update, hapd, sta);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "stopped accounting session %08X-%08X",
+ sta->acct_session_id_hi,
+ sta->acct_session_id_lo);
+ sta->acct_session_started = 0;
+ }
+}
+
+
+static void accounting_sta_get_id(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ sta->acct_session_id_lo = hapd->acct_session_id_lo++;
+ if (hapd->acct_session_id_lo == 0) {
+ hapd->acct_session_id_hi++;
+ }
+ sta->acct_session_id_hi = hapd->acct_session_id_hi;
+}
+
+
+/**
+ * accounting_receive - Process the RADIUS frames from Accounting Server
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: Processing status
+ */
+static RadiusRxResult
+accounting_receive(struct radius_msg *msg, struct radius_msg *req,
+ const u8 *shared_secret, size_t shared_secret_len,
+ void *data)
+{
+ if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
+ printf("Unknown RADIUS message code\n");
+ return RADIUS_RX_UNKNOWN;
+ }
+
+ if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
+ printf("Incoming RADIUS packet did not have correct "
+ "Authenticator - dropped\n");
+ return RADIUS_RX_INVALID_AUTHENTICATOR;
+ }
+
+ return RADIUS_RX_PROCESSED;
+}
+
+
+static void accounting_report_state(struct hostapd_data *hapd, int on)
+{
+ struct radius_msg *msg;
+
+ if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
+ return;
+
+ /* Inform RADIUS server that accounting will start/stop so that the
+ * server can close old accounting sessions. */
+ msg = accounting_msg(hapd, NULL,
+ on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
+ RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
+ if (!msg)
+ return;
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+ RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
+ {
+ printf("Could not add Acct-Terminate-Cause\n");
+ radius_msg_free(msg);
+ return;
+ }
+
+ radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL);
+}
+
+
+/**
+ * accounting_init: Initialize accounting
+ * @hapd: hostapd BSS data
+ * Returns: 0 on success, -1 on failure
+ */
+int accounting_init(struct hostapd_data *hapd)
+{
+ 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/src/ap/accounting.h b/src/ap/accounting.h
new file mode 100644
index 0000000..f3d60f0
--- /dev/null
+++ b/src/ap/accounting.h
@@ -0,0 +1,45 @@
+/*
+ * hostapd / RADIUS Accounting
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef ACCOUNTING_H
+#define ACCOUNTING_H
+
+void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta);
+#ifdef CONFIG_NO_ACCOUNTING
+static inline void accounting_sta_start(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+}
+
+static inline void accounting_sta_stop(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+}
+
+static inline int accounting_init(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline void accounting_deinit(struct hostapd_data *hapd)
+{
+}
+#else /* CONFIG_NO_ACCOUNTING */
+void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
+void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
+int accounting_init(struct hostapd_data *hapd);
+void accounting_deinit(struct hostapd_data *hapd);
+#endif /* CONFIG_NO_ACCOUNTING */
+
+#endif /* ACCOUNTING_H */
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
new file mode 100644
index 0000000..e77716b
--- /dev/null
+++ b/src/ap/ap_config.c
@@ -0,0 +1,627 @@
+/*
+ * hostapd / Configuration helper functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/sha1.h"
+#include "radius/radius_client.h"
+#include "common/ieee802_11_defs.h"
+#include "common/eapol_common.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_server/eap.h"
+#include "wpa_auth.h"
+#include "sta_info.h"
+#include "ap_config.h"
+
+
+static void hostapd_config_free_vlan(struct hostapd_bss_config *bss)
+{
+ struct hostapd_vlan *vlan, *prev;
+
+ vlan = bss->vlan;
+ prev = NULL;
+ while (vlan) {
+ prev = vlan;
+ vlan = vlan->next;
+ os_free(prev);
+ }
+
+ bss->vlan = NULL;
+}
+
+
+void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
+{
+ bss->logger_syslog_level = HOSTAPD_LEVEL_INFO;
+ bss->logger_stdout_level = HOSTAPD_LEVEL_INFO;
+ bss->logger_syslog = (unsigned int) -1;
+ bss->logger_stdout = (unsigned int) -1;
+
+ bss->auth_algs = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED;
+
+ bss->wep_rekeying_period = 300;
+ /* use key0 in individual key and key1 in broadcast key */
+ bss->broadcast_key_idx_min = 1;
+ bss->broadcast_key_idx_max = 2;
+ bss->eap_reauth_period = 3600;
+
+ bss->wpa_group_rekey = 600;
+ bss->wpa_gmk_rekey = 86400;
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+ bss->wpa_pairwise = WPA_CIPHER_TKIP;
+ bss->wpa_group = WPA_CIPHER_TKIP;
+ bss->rsn_pairwise = 0;
+
+ bss->max_num_sta = MAX_STA_COUNT;
+
+ bss->dtim_period = 2;
+
+ bss->radius_server_auth_port = 1812;
+ bss->ap_max_inactivity = AP_MAX_INACTIVITY;
+ bss->eapol_version = EAPOL_VERSION;
+
+ bss->max_listen_interval = 65535;
+
+ 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 */
+}
+
+
+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, 1 };
+ const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+ { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 1 };
+ 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;
+ }
+
+ bss->radius = os_zalloc(sizeof(*bss->radius));
+ if (bss->radius == NULL) {
+ os_free(conf);
+ os_free(bss);
+ return NULL;
+ }
+
+ hostapd_config_defaults_bss(bss);
+
+ conf->num_bss = 1;
+ conf->bss = bss;
+
+ conf->beacon_int = 100;
+ conf->rts_threshold = -1; /* use driver default: 2347 */
+ conf->fragm_threshold = -1; /* user driver default: 2346 */
+ conf->send_probe_response = 1;
+
+ conf->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;
+
+ return conf;
+}
+
+
+int hostapd_mac_comp(const void *a, const void *b)
+{
+ return os_memcmp(a, b, sizeof(macaddr));
+}
+
+
+int hostapd_mac_comp_empty(const void *a)
+{
+ macaddr empty = { 0 };
+ return os_memcmp(a, empty, sizeof(macaddr));
+}
+
+
+static int hostapd_config_read_wpa_psk(const char *fname,
+ struct hostapd_ssid *ssid)
+{
+ FILE *f;
+ char buf[128], *pos;
+ int line = 0, ret = 0, len, ok;
+ u8 addr[ETH_ALEN];
+ struct hostapd_wpa_psk *psk;
+
+ if (!fname)
+ return 0;
+
+ f = fopen(fname, "r");
+ if (!f) {
+ wpa_printf(MSG_ERROR, "WPA PSK file '%s' not found.", fname);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ line++;
+
+ if (buf[0] == '#')
+ continue;
+ pos = buf;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+ if (buf[0] == '\0')
+ continue;
+
+ if (hwaddr_aton(buf, addr)) {
+ wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on "
+ "line %d in '%s'", buf, line, fname);
+ ret = -1;
+ break;
+ }
+
+ psk = os_zalloc(sizeof(*psk));
+ if (psk == NULL) {
+ wpa_printf(MSG_ERROR, "WPA PSK allocation failed");
+ ret = -1;
+ break;
+ }
+ if (is_zero_ether_addr(addr))
+ psk->group = 1;
+ else
+ os_memcpy(psk->addr, addr, ETH_ALEN);
+
+ pos = buf + 17;
+ if (*pos == '\0') {
+ wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'",
+ line, fname);
+ os_free(psk);
+ ret = -1;
+ break;
+ }
+ pos++;
+
+ ok = 0;
+ len = os_strlen(pos);
+ if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0)
+ ok = 1;
+ else if (len >= 8 && len < 64) {
+ pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len,
+ 4096, psk->psk, PMK_LEN);
+ ok = 1;
+ }
+ if (!ok) {
+ wpa_printf(MSG_ERROR, "Invalid PSK '%s' on line %d in "
+ "'%s'", pos, line, fname);
+ os_free(psk);
+ ret = -1;
+ break;
+ }
+
+ psk->next = ssid->wpa_psk;
+ ssid->wpa_psk = psk;
+ }
+
+ fclose(f);
+
+ return ret;
+}
+
+
+static int hostapd_derive_psk(struct hostapd_ssid *ssid)
+{
+ ssid->wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
+ if (ssid->wpa_psk == NULL) {
+ wpa_printf(MSG_ERROR, "Unable to alloc space for PSK");
+ return -1;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "SSID",
+ (u8 *) ssid->ssid, ssid->ssid_len);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "PSK (ASCII passphrase)",
+ (u8 *) ssid->wpa_passphrase,
+ os_strlen(ssid->wpa_passphrase));
+ pbkdf2_sha1(ssid->wpa_passphrase,
+ ssid->ssid, ssid->ssid_len,
+ 4096, ssid->wpa_psk->psk, PMK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "PSK (from passphrase)",
+ ssid->wpa_psk->psk, PMK_LEN);
+ return 0;
+}
+
+
+int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
+{
+ struct hostapd_ssid *ssid = &conf->ssid;
+
+ if (ssid->wpa_passphrase != NULL) {
+ if (ssid->wpa_psk != NULL) {
+ wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK "
+ "instead of passphrase");
+ } else {
+ wpa_printf(MSG_DEBUG, "Deriving WPA PSK based on "
+ "passphrase");
+ if (hostapd_derive_psk(ssid) < 0)
+ return -1;
+ }
+ ssid->wpa_psk->group = 1;
+ }
+
+ if (ssid->wpa_psk_file) {
+ if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file,
+ &conf->ssid))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b)
+{
+ int i;
+
+ if (a->idx != b->idx || a->default_len != b->default_len)
+ return 1;
+ for (i = 0; i < NUM_WEP_KEYS; i++)
+ if (a->len[i] != b->len[i] ||
+ os_memcmp(a->key[i], b->key[i], a->len[i]) != 0)
+ return 1;
+ return 0;
+}
+
+
+static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
+ int num_servers)
+{
+ int i;
+
+ for (i = 0; i < num_servers; i++) {
+ os_free(servers[i].shared_secret);
+ }
+ os_free(servers);
+}
+
+
+static void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
+{
+ os_free(user->identity);
+ os_free(user->password);
+ os_free(user);
+}
+
+
+static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
+{
+ int i;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ os_free(keys->key[i]);
+ keys->key[i] = NULL;
+ }
+}
+
+
+static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
+{
+ struct hostapd_wpa_psk *psk, *prev;
+ struct hostapd_eap_user *user, *prev_user;
+
+ if (conf == NULL)
+ return;
+
+ psk = conf->ssid.wpa_psk;
+ while (psk) {
+ prev = psk;
+ psk = psk->next;
+ os_free(prev);
+ }
+
+ os_free(conf->ssid.wpa_passphrase);
+ os_free(conf->ssid.wpa_psk_file);
+ hostapd_config_free_wep(&conf->ssid.wep);
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ os_free(conf->ssid.vlan_tagged_interface);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+ user = conf->eap_user;
+ while (user) {
+ prev_user = user;
+ user = user->next;
+ hostapd_config_free_eap_user(prev_user);
+ }
+
+ os_free(conf->dump_log_name);
+ os_free(conf->eap_req_id_text);
+ os_free(conf->accept_mac);
+ os_free(conf->deny_mac);
+ os_free(conf->nas_identifier);
+ hostapd_config_free_radius(conf->radius->auth_servers,
+ conf->radius->num_auth_servers);
+ hostapd_config_free_radius(conf->radius->acct_servers,
+ conf->radius->num_acct_servers);
+ os_free(conf->rsn_preauth_interfaces);
+ os_free(conf->ctrl_interface);
+ os_free(conf->ca_cert);
+ os_free(conf->server_cert);
+ os_free(conf->private_key);
+ os_free(conf->private_key_passwd);
+ os_free(conf->dh_file);
+ os_free(conf->pac_opaque_encr_key);
+ os_free(conf->eap_fast_a_id);
+ os_free(conf->eap_fast_a_id_info);
+ os_free(conf->eap_sim_db);
+ os_free(conf->radius_server_clients);
+ os_free(conf->test_socket);
+ os_free(conf->radius);
+ hostapd_config_free_vlan(conf);
+ if (conf->ssid.dyn_vlan_keys) {
+ struct hostapd_ssid *ssid = &conf->ssid;
+ size_t i;
+ for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) {
+ if (ssid->dyn_vlan_keys[i] == NULL)
+ continue;
+ hostapd_config_free_wep(ssid->dyn_vlan_keys[i]);
+ os_free(ssid->dyn_vlan_keys[i]);
+ }
+ os_free(ssid->dyn_vlan_keys);
+ ssid->dyn_vlan_keys = NULL;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ {
+ struct ft_remote_r0kh *r0kh, *r0kh_prev;
+ struct ft_remote_r1kh *r1kh, *r1kh_prev;
+
+ r0kh = conf->r0kh_list;
+ conf->r0kh_list = NULL;
+ while (r0kh) {
+ r0kh_prev = r0kh;
+ r0kh = r0kh->next;
+ os_free(r0kh_prev);
+ }
+
+ r1kh = conf->r1kh_list;
+ conf->r1kh_list = NULL;
+ while (r1kh) {
+ r1kh_prev = r1kh;
+ r1kh = r1kh->next;
+ os_free(r1kh_prev);
+ }
+ }
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_WPS
+ os_free(conf->wps_pin_requests);
+ os_free(conf->device_name);
+ os_free(conf->manufacturer);
+ os_free(conf->model_name);
+ os_free(conf->model_number);
+ os_free(conf->serial_number);
+ os_free(conf->config_methods);
+ os_free(conf->ap_pin);
+ os_free(conf->extra_cred);
+ os_free(conf->ap_settings);
+ os_free(conf->upnp_iface);
+ os_free(conf->friendly_name);
+ os_free(conf->manufacturer_url);
+ os_free(conf->model_description);
+ os_free(conf->model_url);
+ os_free(conf->upc);
+#endif /* CONFIG_WPS */
+}
+
+
+/**
+ * hostapd_config_free - Free hostapd configuration
+ * @conf: Configuration data from hostapd_config_read().
+ */
+void hostapd_config_free(struct hostapd_config *conf)
+{
+ size_t i;
+
+ if (conf == NULL)
+ return;
+
+ for (i = 0; i < conf->num_bss; i++)
+ hostapd_config_free_bss(&conf->bss[i]);
+ os_free(conf->bss);
+ os_free(conf->supported_rates);
+ os_free(conf->basic_rates);
+
+ os_free(conf);
+}
+
+
+/**
+ * hostapd_maclist_found - Find a MAC address from a list
+ * @list: MAC address list
+ * @num_entries: Number of addresses in the list
+ * @addr: Address to search for
+ * @vlan_id: Buffer for returning VLAN ID or %NULL if not needed
+ * Returns: 1 if address is in the list or 0 if not.
+ *
+ * Perform a binary search for given MAC address from a pre-sorted list.
+ */
+int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
+ const u8 *addr, int *vlan_id)
+{
+ int start, end, middle, res;
+
+ start = 0;
+ end = num_entries - 1;
+
+ while (start <= end) {
+ middle = (start + end) / 2;
+ res = os_memcmp(list[middle].addr, addr, ETH_ALEN);
+ if (res == 0) {
+ if (vlan_id)
+ *vlan_id = list[middle].vlan_id;
+ return 1;
+ }
+ if (res < 0)
+ start = middle + 1;
+ else
+ end = middle - 1;
+ }
+
+ return 0;
+}
+
+
+int hostapd_rate_found(int *list, int rate)
+{
+ int i;
+
+ if (list == NULL)
+ return 0;
+
+ for (i = 0; list[i] >= 0; i++)
+ if (list[i] == rate)
+ return 1;
+
+ return 0;
+}
+
+
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
+{
+ struct hostapd_vlan *v = vlan;
+ while (v) {
+ if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD)
+ return v->ifname;
+ v = v->next;
+ }
+ return NULL;
+}
+
+
+const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+ const u8 *addr, const u8 *prev_psk)
+{
+ struct hostapd_wpa_psk *psk;
+ int next_ok = prev_psk == NULL;
+
+ for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) {
+ if (next_ok &&
+ (psk->group || os_memcmp(psk->addr, addr, ETH_ALEN) == 0))
+ return psk->psk;
+
+ if (psk->psk == prev_psk)
+ next_ok = 1;
+ }
+
+ return NULL;
+}
+
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
+ size_t identity_len, int phase2)
+{
+ struct hostapd_eap_user *user = conf->eap_user;
+
+#ifdef CONFIG_WPS
+ if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
+ os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
+ static struct hostapd_eap_user wsc_enrollee;
+ os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
+ wsc_enrollee.methods[0].method = eap_server_get_type(
+ "WSC", &wsc_enrollee.methods[0].vendor);
+ return &wsc_enrollee;
+ }
+
+ if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN &&
+ os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
+ static struct hostapd_eap_user wsc_registrar;
+ os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
+ wsc_registrar.methods[0].method = eap_server_get_type(
+ "WSC", &wsc_registrar.methods[0].vendor);
+ wsc_registrar.password = (u8 *) conf->ap_pin;
+ wsc_registrar.password_len = conf->ap_pin ?
+ os_strlen(conf->ap_pin) : 0;
+ return &wsc_registrar;
+ }
+#endif /* CONFIG_WPS */
+
+ while (user) {
+ if (!phase2 && user->identity == NULL) {
+ /* Wildcard match */
+ break;
+ }
+
+ if (user->phase2 == !!phase2 && user->wildcard_prefix &&
+ identity_len >= user->identity_len &&
+ os_memcmp(user->identity, identity, user->identity_len) ==
+ 0) {
+ /* Wildcard prefix match */
+ break;
+ }
+
+ if (user->phase2 == !!phase2 &&
+ user->identity_len == identity_len &&
+ os_memcmp(user->identity, identity, identity_len) == 0)
+ break;
+ user = user->next;
+ }
+
+ return user;
+}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
new file mode 100644
index 0000000..25720b8
--- /dev/null
+++ b/src/ap/ap_config.h
@@ -0,0 +1,417 @@
+/*
+ * hostapd / Configuration definitions and helpers functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef HOSTAPD_CONFIG_H
+#define HOSTAPD_CONFIG_H
+
+#include "common/defs.h"
+#include "ip_addr.h"
+#include "common/wpa_common.h"
+#include "wps/wps.h"
+
+#define MAX_STA_COUNT 2007
+#define MAX_VLAN_ID 4094
+
+typedef u8 macaddr[ETH_ALEN];
+
+struct mac_acl_entry {
+ macaddr addr;
+ int vlan_id;
+};
+
+struct hostapd_radius_servers;
+struct ft_remote_r0kh;
+struct ft_remote_r1kh;
+
+#define HOSTAPD_MAX_SSID_LEN 32
+
+#define NUM_WEP_KEYS 4
+struct hostapd_wep_keys {
+ u8 idx;
+ u8 *key[NUM_WEP_KEYS];
+ size_t len[NUM_WEP_KEYS];
+ int keys_set;
+ size_t default_len; /* key length used for dynamic key generation */
+};
+
+typedef enum hostap_security_policy {
+ SECURITY_PLAINTEXT = 0,
+ SECURITY_STATIC_WEP = 1,
+ SECURITY_IEEE_802_1X = 2,
+ SECURITY_WPA_PSK = 3,
+ SECURITY_WPA = 4
+} secpolicy;
+
+struct hostapd_ssid {
+ char ssid[HOSTAPD_MAX_SSID_LEN + 1];
+ size_t ssid_len;
+ int ssid_set;
+
+ char vlan[IFNAMSIZ + 1];
+ secpolicy security_policy;
+
+ struct hostapd_wpa_psk *wpa_psk;
+ char *wpa_passphrase;
+ char *wpa_psk_file;
+
+ struct hostapd_wep_keys wep;
+
+#define DYNAMIC_VLAN_DISABLED 0
+#define DYNAMIC_VLAN_OPTIONAL 1
+#define DYNAMIC_VLAN_REQUIRED 2
+ int dynamic_vlan;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ char *vlan_tagged_interface;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+ struct hostapd_wep_keys **dyn_vlan_keys;
+ size_t max_dyn_vlan_keys;
+};
+
+
+#define VLAN_ID_WILDCARD -1
+
+struct hostapd_vlan {
+ struct hostapd_vlan *next;
+ int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
+ char ifname[IFNAMSIZ + 1];
+ int dynamic_vlan;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
+#define DVLAN_CLEAN_BR 0x1
+#define DVLAN_CLEAN_VLAN 0x2
+#define DVLAN_CLEAN_VLAN_PORT 0x4
+#define DVLAN_CLEAN_WLAN_PORT 0x8
+ int clean;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+};
+
+#define PMK_LEN 32
+struct hostapd_wpa_psk {
+ struct hostapd_wpa_psk *next;
+ int group;
+ u8 psk[PMK_LEN];
+ u8 addr[ETH_ALEN];
+};
+
+#define EAP_USER_MAX_METHODS 8
+struct hostapd_eap_user {
+ struct hostapd_eap_user *next;
+ u8 *identity;
+ size_t identity_len;
+ struct {
+ int vendor;
+ u32 method;
+ } methods[EAP_USER_MAX_METHODS];
+ u8 *password;
+ size_t password_len;
+ int phase2;
+ int force_version;
+ unsigned int wildcard_prefix:1;
+ unsigned int password_hash:1; /* whether password is hashed with
+ * nt_password_hash() */
+ int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
+};
+
+
+#define NUM_TX_QUEUES 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 */
+};
+
+struct hostapd_wmm_ac_params {
+ int cwmin;
+ int cwmax;
+ int aifs;
+ int txop_limit; /* in units of 32us */
+ int admission_control_mandatory;
+};
+
+
+/**
+ * struct hostapd_bss_config - Per-BSS configuration
+ */
+struct hostapd_bss_config {
+ char iface[IFNAMSIZ + 1];
+ char bridge[IFNAMSIZ + 1];
+ 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_sim_db;
+ struct hostapd_ip_addr own_ip_addr;
+ char *nas_identifier;
+ struct hostapd_radius_servers *radius;
+ int acct_interim_interval;
+
+ struct hostapd_ssid ssid;
+
+ char *eap_req_id_text; /* optional displayable message sent with
+ * EAP Request-Identity */
+ size_t eap_req_id_text_len;
+ int eapol_key_index_workaround;
+
+ size_t default_wep_key_len;
+ int individual_wep_key_len;
+ int wep_rekeying_period;
+ int broadcast_key_idx_min, broadcast_key_idx_max;
+ int eap_reauth_period;
+
+ int ieee802_11f; /* use IEEE 802.11f (IAPP) */
+ char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
+ * frames */
+
+ enum {
+ ACCEPT_UNLESS_DENIED = 0,
+ DENY_UNLESS_ACCEPTED = 1,
+ USE_EXTERNAL_RADIUS_AUTH = 2
+ } macaddr_acl;
+ struct mac_acl_entry *accept_mac;
+ int num_accept_mac;
+ struct mac_acl_entry *deny_mac;
+ int num_deny_mac;
+ int wds_sta;
+ int isolate;
+
+ int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
+ * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
+
+ int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
+ int wpa_key_mgmt;
+#ifdef CONFIG_IEEE80211W
+ enum mfp_options ieee80211w;
+ /* dot11AssociationSAQueryMaximumTimeout (in TUs) */
+ unsigned int assoc_sa_query_max_timeout;
+ /* dot11AssociationSAQueryRetryTimeout (in TUs) */
+ int assoc_sa_query_retry_timeout;
+#endif /* CONFIG_IEEE80211W */
+ int wpa_pairwise;
+ int wpa_group;
+ int wpa_group_rekey;
+ int wpa_strict_rekey;
+ int wpa_gmk_rekey;
+ int wpa_ptk_rekey;
+ int rsn_pairwise;
+ int rsn_preauth;
+ char *rsn_preauth_interfaces;
+ int peerkey;
+
+#ifdef CONFIG_IEEE80211R
+ /* IEEE 802.11r - Fast BSS Transition */
+ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+ u8 r1_key_holder[FT_R1KH_ID_LEN];
+ u32 r0_key_lifetime;
+ u32 reassociation_deadline;
+ struct ft_remote_r0kh *r0kh_list;
+ struct ft_remote_r1kh *r1kh_list;
+ int pmk_r1_push;
+ 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 *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, *vlan_tail;
+
+ macaddr bssid;
+
+ /*
+ * Maximum listen interval that STAs can use when associating with this
+ * BSS. If a STA tries to use larger value, the association will be
+ * denied with status code 51.
+ */
+ u16 max_listen_interval;
+
+ int okc; /* Opportunistic Key Caching */
+
+ int wps_state;
+#ifdef CONFIG_WPS
+ int ap_setup_locked;
+ u8 uuid[16];
+ char *wps_pin_requests;
+ char *device_name;
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+ 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;
+ 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];
+#endif /* CONFIG_WPS */
+
+#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;
+
+#define TDLS_PROHIBIT BIT(0)
+#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
+ int tdls;
+ int disable_11n;
+};
+
+
+/**
+ * struct hostapd_config - Per-radio interface configuration
+ */
+struct hostapd_config {
+ struct hostapd_bss_config *bss, *last_bss;
+ size_t num_bss;
+
+ u16 beacon_int;
+ int rts_threshold;
+ int fragm_threshold;
+ u8 send_probe_response;
+ u8 channel;
+ enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
+ enum {
+ LONG_PREAMBLE = 0,
+ SHORT_PREAMBLE = 1
+ } preamble;
+ enum {
+ CTS_PROTECTION_AUTOMATIC = 0,
+ CTS_PROTECTION_FORCE_ENABLED = 1,
+ CTS_PROTECTION_FORCE_DISABLED = 2,
+ CTS_PROTECTION_AUTOMATIC_NO_OLBC = 3,
+ } cts_protection_type;
+
+ int *supported_rates;
+ int *basic_rates;
+
+ const struct wpa_driver_ops *driver;
+
+ int ap_table_max_size;
+ int ap_table_expiration_time;
+
+ char country[3]; /* first two octets: country code as described in
+ * ISO/IEC 3166-1. Third octet:
+ * ' ' (ascii 32): all environments
+ * 'O': Outdoor environemnt only
+ * 'I': Indoor environment only
+ */
+
+ int ieee80211d;
+
+ struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];
+
+ /*
+ * WMM AC parameters, in same order as 802.1D, i.e.
+ * 0 = BE (best effort)
+ * 1 = BK (background)
+ * 2 = VI (video)
+ * 3 = VO (voice)
+ */
+ struct hostapd_wmm_ac_params wmm_ac_params[4];
+
+ int ht_op_mode_fixed;
+ u16 ht_capab;
+ int ieee80211n;
+ int secondary_channel;
+ int require_ht;
+};
+
+
+int hostapd_mac_comp(const void *a, const void *b);
+int hostapd_mac_comp_empty(const void *a);
+struct hostapd_config * hostapd_config_defaults(void);
+void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
+void hostapd_config_free(struct hostapd_config *conf);
+int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
+ const u8 *addr, int *vlan_id);
+int hostapd_rate_found(int *list, int rate);
+int hostapd_wep_key_cmp(struct hostapd_wep_keys *a,
+ struct hostapd_wep_keys *b);
+const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+ const u8 *addr, const u8 *prev_psk);
+int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
+ int vlan_id);
+const struct hostapd_eap_user *
+hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
+ size_t identity_len, int phase2);
+
+#endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
new file mode 100644
index 0000000..0b6836c
--- /dev/null
+++ b/src/ap/ap_drv_ops.c
@@ -0,0 +1,632 @@
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "drivers/driver.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "p2p_hostapd.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_set_ap_wps_ie(struct hostapd_data *hapd)
+{
+ struct wpabuf *beacon, *proberesp, *assocresp = NULL;
+ int ret;
+
+ if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+ return 0;
+
+ beacon = hapd->wps_beacon_ie;
+ proberesp = hapd->wps_probe_resp_ie;
+
+#ifdef CONFIG_P2P
+ if (hapd->wps_beacon_ie == NULL && hapd->p2p_beacon_ie == NULL)
+ beacon = NULL;
+ else {
+ beacon = wpabuf_alloc((hapd->wps_beacon_ie ?
+ wpabuf_len(hapd->wps_beacon_ie) : 0) +
+ (hapd->p2p_beacon_ie ?
+ wpabuf_len(hapd->p2p_beacon_ie) : 0));
+ if (beacon == NULL)
+ return -1;
+ if (hapd->wps_beacon_ie)
+ wpabuf_put_buf(beacon, hapd->wps_beacon_ie);
+ if (hapd->p2p_beacon_ie)
+ wpabuf_put_buf(beacon, hapd->p2p_beacon_ie);
+ }
+
+ if (hapd->wps_probe_resp_ie == NULL && hapd->p2p_probe_resp_ie == NULL)
+ proberesp = NULL;
+ else {
+ proberesp = wpabuf_alloc(
+ (hapd->wps_probe_resp_ie ?
+ wpabuf_len(hapd->wps_probe_resp_ie) : 0) +
+ (hapd->p2p_probe_resp_ie ?
+ wpabuf_len(hapd->p2p_probe_resp_ie) : 0));
+ if (proberesp == NULL) {
+ wpabuf_free(beacon);
+ return -1;
+ }
+ if (hapd->wps_probe_resp_ie)
+ wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie);
+ if (hapd->p2p_probe_resp_ie)
+ wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie);
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE) {
+ struct wpabuf *a;
+
+ a = wpabuf_alloc(100 + (beacon ? wpabuf_len(beacon) : 0));
+ if (a) {
+ u8 *start, *p;
+ if (beacon)
+ wpabuf_put_buf(a, beacon);
+ if (beacon != hapd->wps_beacon_ie)
+ wpabuf_free(beacon);
+ start = wpabuf_put(a, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(a, p - start);
+ beacon = a;
+ }
+
+ a = wpabuf_alloc(100 + (proberesp ? wpabuf_len(proberesp) :
+ 0));
+ if (a) {
+ u8 *start, *p;
+ if (proberesp)
+ wpabuf_put_buf(a, proberesp);
+ if (proberesp != hapd->wps_probe_resp_ie)
+ wpabuf_free(proberesp);
+ start = wpabuf_put(a, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(a, p - start);
+ proberesp = a;
+ }
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_WPS2
+ if (hapd->conf->wps_state)
+ assocresp = wps_build_assoc_resp_ie();
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE) {
+ struct wpabuf *a;
+ a = wpabuf_alloc(100 + (assocresp ? wpabuf_len(assocresp) :
+ 0));
+ if (a) {
+ u8 *start, *p;
+ start = wpabuf_put(a, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(a, p - start);
+ if (assocresp) {
+ wpabuf_put_buf(a, assocresp);
+ wpabuf_free(assocresp);
+ }
+ assocresp = a;
+ }
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+ ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
+ assocresp);
+
+ if (beacon != hapd->wps_beacon_ie)
+ wpabuf_free(beacon);
+ if (proberesp != hapd->wps_probe_resp_ie)
+ wpabuf_free(proberesp);
+ wpabuf_free(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(&params, 0, sizeof(params));
+ params.ifname = ifname;
+ params.enabled = enabled;
+ if (enabled) {
+ params.wpa = hapd->conf->wpa;
+ params.ieee802_1x = hapd->conf->ieee802_1x;
+ params.wpa_group = hapd->conf->wpa_group;
+ params.wpa_pairwise = hapd->conf->wpa_pairwise;
+ params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+ params.rsn_preauth = hapd->conf->rsn_preauth;
+#ifdef CONFIG_IEEE80211W
+ params.ieee80211w = hapd->conf->ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+ }
+ return hostapd_set_ieee8021x(hapd, &params);
+}
+
+
+static int hostapd_set_ap_isolate(struct hostapd_data *hapd, int value)
+{
+ if (hapd->driver == NULL || hapd->driver->set_intra_bss == NULL)
+ return 0;
+ return hapd->driver->set_intra_bss(hapd->drv_priv, !value);
+}
+
+
+int hostapd_set_bss_params(struct hostapd_data *hapd, int use_protection)
+{
+ int ret = 0;
+ int preamble;
+#ifdef CONFIG_IEEE80211N
+ u8 buf[60], *ht_capab, *ht_oper, *pos;
+
+ pos = buf;
+ ht_capab = pos;
+ pos = hostapd_eid_ht_capabilities(hapd, pos);
+ ht_oper = pos;
+ pos = hostapd_eid_ht_operation(hapd, pos);
+ if (pos > ht_oper && ht_oper > ht_capab &&
+ hostapd_set_ht_params(hapd, ht_capab + 2, ht_capab[1],
+ ht_oper + 2, ht_oper[1])) {
+ wpa_printf(MSG_ERROR, "Could not set HT capabilities "
+ "for kernel driver");
+ ret = -1;
+ }
+
+#endif /* CONFIG_IEEE80211N */
+
+ if (hostapd_set_cts_protect(hapd, use_protection)) {
+ wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel "
+ "driver");
+ ret = -1;
+ }
+
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
+ hostapd_set_short_slot_time(hapd,
+ hapd->iface->num_sta_no_short_slot_time
+ > 0 ? 0 : 1)) {
+ wpa_printf(MSG_ERROR, "Failed to set Short Slot Time option "
+ "in kernel driver");
+ ret = -1;
+ }
+
+ if (hapd->iface->num_sta_no_short_preamble == 0 &&
+ hapd->iconf->preamble == SHORT_PREAMBLE)
+ preamble = SHORT_PREAMBLE;
+ else
+ preamble = LONG_PREAMBLE;
+ if (hostapd_set_preamble(hapd, preamble)) {
+ wpa_printf(MSG_ERROR, "Could not set preamble for kernel "
+ "driver");
+ ret = -1;
+ }
+
+ if (hostapd_set_ap_isolate(hapd, hapd->conf->isolate) &&
+ hapd->conf->isolate) {
+ wpa_printf(MSG_ERROR, "Could not enable AP isolation in "
+ "kernel driver");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+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);
+}
+
+
+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, const u8 *addr, int aid,
+ int val)
+{
+ const char *bridge = NULL;
+
+ if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
+ return 0;
+ 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);
+}
+
+
+int hostapd_sta_add(struct hostapd_data *hapd,
+ const u8 *addr, u16 aid, u16 capability,
+ const u8 *supp_rates, size_t supp_rates_len,
+ u16 listen_interval,
+ const struct ieee80211_ht_capabilities *ht_capab)
+{
+ struct hostapd_sta_add_params params;
+
+ if (hapd->driver == NULL)
+ return 0;
+ if (hapd->driver->sta_add == NULL)
+ return 0;
+
+ os_memset(&params, 0, sizeof(params));
+ params.addr = addr;
+ params.aid = aid;
+ params.capability = capability;
+ params.supp_rates = supp_rates;
+ params.supp_rates_len = supp_rates_len;
+ params.listen_interval = listen_interval;
+ params.ht_capabilities = ht_capab;
+ return hapd->driver->sta_add(hapd->drv_priv, &params);
+}
+
+
+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)
+{
+ 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);
+}
+
+
+int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname)
+{
+ if (hapd->driver == NULL || hapd->driver->if_remove == NULL)
+ return -1;
+ return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
+}
+
+
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+ struct wpa_bss_params *params)
+{
+ if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
+ return 0;
+ return hapd->driver->set_ieee8021x(hapd->drv_priv, params);
+}
+
+
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+ const u8 *addr, int idx, u8 *seq)
+{
+ if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
+ return 0;
+ return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx,
+ seq);
+}
+
+
+int hostapd_flush(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->flush == NULL)
+ return 0;
+ return hapd->driver->flush(hapd->drv_priv);
+}
+
+
+int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
+ int channel, int ht_enabled, int sec_channel_offset)
+{
+ struct hostapd_freq_params data;
+ if (hapd->driver == NULL)
+ return 0;
+ if (hapd->driver->set_freq == NULL)
+ return 0;
+ os_memset(&data, 0, sizeof(data));
+ data.mode = mode;
+ data.freq = freq;
+ data.channel = channel;
+ data.ht_enabled = ht_enabled;
+ data.sec_channel_offset = sec_channel_offset;
+ return hapd->driver->set_freq(hapd->drv_priv, &data);
+}
+
+int hostapd_set_rts(struct hostapd_data *hapd, int rts)
+{
+ if (hapd->driver == NULL || hapd->driver->set_rts == NULL)
+ return 0;
+ return hapd->driver->set_rts(hapd->drv_priv, rts);
+}
+
+
+int hostapd_set_frag(struct hostapd_data *hapd, int frag)
+{
+ if (hapd->driver == NULL || hapd->driver->set_frag == NULL)
+ return 0;
+ return hapd->driver->set_frag(hapd->drv_priv, frag);
+}
+
+
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+ int total_flags, int flags_or, int flags_and)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL)
+ return 0;
+ return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags,
+ flags_or, flags_and);
+}
+
+
+int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates,
+ int *basic_rates, int mode)
+{
+ if (hapd->driver == NULL || hapd->driver->set_rate_sets == NULL)
+ return 0;
+ return hapd->driver->set_rate_sets(hapd->drv_priv, supp_rates,
+ basic_rates, mode);
+}
+
+
+int hostapd_set_country(struct hostapd_data *hapd, const char *country)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->set_country == NULL)
+ return 0;
+ return hapd->driver->set_country(hapd->drv_priv, country);
+}
+
+
+int hostapd_set_cts_protect(struct hostapd_data *hapd, int value)
+{
+ if (hapd->driver == NULL || hapd->driver->set_cts_protect == NULL)
+ return 0;
+ return hapd->driver->set_cts_protect(hapd->drv_priv, value);
+}
+
+
+int hostapd_set_preamble(struct hostapd_data *hapd, int value)
+{
+ if (hapd->driver == NULL || hapd->driver->set_preamble == NULL)
+ return 0;
+ return hapd->driver->set_preamble(hapd->drv_priv, value);
+}
+
+
+int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value)
+{
+ if (hapd->driver == NULL || hapd->driver->set_short_slot_time == NULL)
+ return 0;
+ return hapd->driver->set_short_slot_time(hapd->drv_priv, value);
+}
+
+
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+ int cw_min, int cw_max, int burst_time)
+{
+ if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL)
+ return 0;
+ return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs,
+ cw_min, cw_max, burst_time);
+}
+
+
+int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *mask)
+{
+ if (hapd->driver == NULL || hapd->driver->valid_bss_mask == NULL)
+ return 1;
+ return hapd->driver->valid_bss_mask(hapd->drv_priv, addr, mask);
+}
+
+
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+ u16 *flags)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->get_hw_feature_data == NULL)
+ return NULL;
+ return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
+ flags);
+}
+
+
+int hostapd_driver_commit(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->commit == NULL)
+ return 0;
+ return hapd->driver->commit(hapd->drv_priv);
+}
+
+
+int hostapd_set_ht_params(struct hostapd_data *hapd,
+ const u8 *ht_capab, size_t ht_capab_len,
+ const u8 *ht_oper, size_t ht_oper_len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_ht_params == NULL ||
+ ht_capab == NULL || ht_oper == NULL)
+ return 0;
+ return hapd->driver->set_ht_params(hapd->drv_priv,
+ ht_capab, ht_capab_len,
+ ht_oper, ht_oper_len);
+}
+
+
+int hostapd_drv_none(struct hostapd_data *hapd)
+{
+ return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
+}
+
+
+int hostapd_driver_scan(struct hostapd_data *hapd,
+ struct wpa_driver_scan_params *params)
+{
+ if (hapd->driver && hapd->driver->scan2)
+ return hapd->driver->scan2(hapd->drv_priv, params);
+ return -1;
+}
+
+
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+ struct hostapd_data *hapd)
+{
+ if (hapd->driver && hapd->driver->get_scan_results2)
+ return hapd->driver->get_scan_results2(hapd->drv_priv);
+ return NULL;
+}
+
+
+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)
+{
+ if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+ return 0;
+ return hapd->driver->send_mlme(hapd->drv_priv, msg, len);
+}
+
+
+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);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
new file mode 100644
index 0000000..36bb826
--- /dev/null
+++ b/src/ap/ap_drv_ops.h
@@ -0,0 +1,197 @@
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef AP_DRV_OPS
+#define AP_DRV_OPS
+
+enum wpa_driver_if_type;
+struct wpa_bss_params;
+struct wpa_driver_scan_params;
+struct ieee80211_ht_capabilities;
+
+u32 hostapd_sta_flags_to_drv(u32 flags);
+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_set_bss_params(struct hostapd_data *hapd, int use_protection);
+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, 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);
+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 hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+ const char *ifname);
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+ struct wpa_bss_params *params);
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+ const u8 *addr, int idx, u8 *seq);
+int hostapd_flush(struct hostapd_data *hapd);
+int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
+ int channel, int ht_enabled, int sec_channel_offset);
+int hostapd_set_rts(struct hostapd_data *hapd, int rts);
+int hostapd_set_frag(struct hostapd_data *hapd, int frag);
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+ int total_flags, int flags_or, int flags_and);
+int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates,
+ int *basic_rates, int mode);
+int hostapd_set_country(struct hostapd_data *hapd, const char *country);
+int hostapd_set_cts_protect(struct hostapd_data *hapd, int value);
+int hostapd_set_preamble(struct hostapd_data *hapd, int value);
+int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value);
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+ int cw_min, int cw_max, int burst_time);
+int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *mask);
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+ u16 *flags);
+int hostapd_driver_commit(struct hostapd_data *hapd);
+int hostapd_set_ht_params(struct hostapd_data *hapd,
+ const u8 *ht_capab, size_t ht_capab_len,
+ const u8 *ht_oper, size_t ht_oper_len);
+int hostapd_drv_none(struct hostapd_data *hapd);
+int hostapd_driver_scan(struct hostapd_data *hapd,
+ struct wpa_driver_scan_params *params);
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+ struct hostapd_data *hapd);
+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 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);
+
+
+#include "drivers/driver.h"
+
+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_beacon(struct hostapd_data *hapd,
+ const u8 *head, size_t head_len,
+ const u8 *tail, size_t tail_len,
+ int dtim_period, int beacon_int)
+{
+ if (hapd->driver == NULL || hapd->driver->set_beacon == NULL)
+ return 0;
+ return hapd->driver->set_beacon(hapd->drv_priv,
+ head, head_len, tail, tail_len,
+ dtim_period, beacon_int);
+}
+
+static 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);
+}
+
+#endif /* AP_DRV_OPS */
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
new file mode 100644
index 0000000..9b9fc9e
--- /dev/null
+++ b/src/ap/ap_list.c
@@ -0,0 +1,399 @@
+/*
+ * hostapd / AP table
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2006, Devicescape Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ap_list.h"
+
+
+/* AP list is a double linked list with head->prev pointing to the end of the
+ * list and tail->next = NULL. Entries are moved to the head of the list
+ * whenever a beacon has been received from the AP in question. The tail entry
+ * in this link will thus be the least recently used entry. */
+
+
+static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
+{
+ int i;
+
+ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
+ iface->conf->channel != ap->channel)
+ return 0;
+
+ if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT))
+ return 1;
+
+ for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
+ int rate = (ap->supported_rates[i] & 0x7f) * 5;
+ if (rate == 60 || rate == 90 || rate > 110)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
+{
+ struct ap_info *s;
+
+ s = iface->ap_hash[STA_HASH(ap)];
+ while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
+ s = s->hnext;
+ return s;
+}
+
+
+static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap)
+{
+ if (iface->ap_list) {
+ ap->prev = iface->ap_list->prev;
+ iface->ap_list->prev = ap;
+ } else
+ ap->prev = ap;
+ ap->next = iface->ap_list;
+ iface->ap_list = ap;
+}
+
+
+static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
+{
+ if (iface->ap_list == ap)
+ iface->ap_list = ap->next;
+ else
+ ap->prev->next = ap->next;
+
+ if (ap->next)
+ ap->next->prev = ap->prev;
+ else if (iface->ap_list)
+ iface->ap_list->prev = ap->prev;
+}
+
+
+static void ap_ap_iter_list_add(struct hostapd_iface *iface,
+ struct ap_info *ap)
+{
+ if (iface->ap_iter_list) {
+ ap->iter_prev = iface->ap_iter_list->iter_prev;
+ iface->ap_iter_list->iter_prev = ap;
+ } else
+ ap->iter_prev = ap;
+ ap->iter_next = iface->ap_iter_list;
+ iface->ap_iter_list = ap;
+}
+
+
+static void ap_ap_iter_list_del(struct hostapd_iface *iface,
+ struct ap_info *ap)
+{
+ if (iface->ap_iter_list == ap)
+ iface->ap_iter_list = ap->iter_next;
+ else
+ ap->iter_prev->iter_next = ap->iter_next;
+
+ if (ap->iter_next)
+ ap->iter_next->iter_prev = ap->iter_prev;
+ else if (iface->ap_iter_list)
+ iface->ap_iter_list->iter_prev = ap->iter_prev;
+}
+
+
+static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
+{
+ ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
+ iface->ap_hash[STA_HASH(ap->addr)] = ap;
+}
+
+
+static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
+{
+ struct ap_info *s;
+
+ s = iface->ap_hash[STA_HASH(ap->addr)];
+ if (s == NULL) return;
+ if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
+ iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
+ return;
+ }
+
+ while (s->hnext != NULL &&
+ os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
+ s = s->hnext;
+ if (s->hnext != NULL)
+ s->hnext = s->hnext->hnext;
+ else
+ printf("AP: could not remove AP " MACSTR " from hash table\n",
+ MAC2STR(ap->addr));
+}
+
+
+static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
+{
+ ap_ap_hash_del(iface, ap);
+ ap_ap_list_del(iface, ap);
+ ap_ap_iter_list_del(iface, ap);
+
+ iface->num_ap--;
+ os_free(ap);
+}
+
+
+static void hostapd_free_aps(struct hostapd_iface *iface)
+{
+ struct ap_info *ap, *prev;
+
+ ap = iface->ap_list;
+
+ while (ap) {
+ prev = ap;
+ ap = ap->next;
+ ap_free_ap(iface, prev);
+ }
+
+ iface->ap_list = NULL;
+}
+
+
+int ap_ap_for_each(struct hostapd_iface *iface,
+ int (*func)(struct ap_info *s, void *data), void *data)
+{
+ struct ap_info *s;
+ int ret = 0;
+
+ s = iface->ap_list;
+
+ while (s) {
+ ret = func(s, data);
+ if (ret)
+ break;
+ s = s->next;
+ }
+
+ return ret;
+}
+
+
+static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
+{
+ struct ap_info *ap;
+
+ ap = os_zalloc(sizeof(struct ap_info));
+ if (ap == NULL)
+ return NULL;
+
+ /* initialize AP info data */
+ os_memcpy(ap->addr, addr, ETH_ALEN);
+ ap_ap_list_add(iface, ap);
+ iface->num_ap++;
+ ap_ap_hash_add(iface, ap);
+ ap_ap_iter_list_add(iface, ap);
+
+ if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
+ wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
+ MACSTR " from AP table", MAC2STR(ap->prev->addr));
+ ap_free_ap(iface, ap->prev);
+ }
+
+ return ap;
+}
+
+
+void ap_list_process_beacon(struct hostapd_iface *iface,
+ const struct ieee80211_mgmt *mgmt,
+ struct ieee802_11_elems *elems,
+ struct hostapd_frame_info *fi)
+{
+ struct ap_info *ap;
+ struct os_time now;
+ int new_ap = 0;
+ size_t len;
+ int set_beacon = 0;
+
+ if (iface->conf->ap_table_max_size < 1)
+ return;
+
+ ap = ap_get_ap(iface, mgmt->bssid);
+ if (!ap) {
+ ap = ap_ap_add(iface, mgmt->bssid);
+ if (!ap) {
+ printf("Failed to allocate AP information entry\n");
+ return;
+ }
+ new_ap = 1;
+ }
+
+ ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int);
+ ap->capability = le_to_host16(mgmt->u.beacon.capab_info);
+
+ if (elems->ssid) {
+ len = elems->ssid_len;
+ if (len >= sizeof(ap->ssid))
+ len = sizeof(ap->ssid) - 1;
+ os_memcpy(ap->ssid, elems->ssid, len);
+ ap->ssid[len] = '\0';
+ ap->ssid_len = len;
+ }
+
+ os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX);
+ len = 0;
+ if (elems->supp_rates) {
+ len = elems->supp_rates_len;
+ if (len > WLAN_SUPP_RATES_MAX)
+ len = WLAN_SUPP_RATES_MAX;
+ os_memcpy(ap->supported_rates, elems->supp_rates, len);
+ }
+ if (elems->ext_supp_rates) {
+ int len2;
+ if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX)
+ len2 = WLAN_SUPP_RATES_MAX - len;
+ else
+ len2 = elems->ext_supp_rates_len;
+ os_memcpy(ap->supported_rates + len, elems->ext_supp_rates,
+ len2);
+ }
+
+ ap->wpa = elems->wpa_ie != NULL;
+
+ if (elems->erp_info && elems->erp_info_len == 1)
+ ap->erp = elems->erp_info[0];
+ else
+ ap->erp = -1;
+
+ if (elems->ds_params && elems->ds_params_len == 1)
+ ap->channel = elems->ds_params[0];
+ else if (fi)
+ ap->channel = fi->channel;
+
+ if (elems->ht_capabilities)
+ ap->ht_support = 1;
+ else
+ ap->ht_support = 0;
+
+ ap->num_beacons++;
+ os_get_time(&now);
+ ap->last_beacon = now.sec;
+ if (fi) {
+ ap->ssi_signal = fi->ssi_signal;
+ ap->datarate = fi->datarate;
+ }
+
+ if (!new_ap && ap != iface->ap_list) {
+ /* move AP entry into the beginning of the list so that the
+ * oldest entry is always in the end of the list */
+ ap_ap_list_del(iface, ap);
+ ap_ap_list_add(iface, ap);
+ }
+
+ if (!iface->olbc &&
+ ap_list_beacon_olbc(iface, ap)) {
+ iface->olbc = 1;
+ wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable "
+ "protection", MAC2STR(ap->addr));
+ set_beacon++;
+ }
+
+#ifdef CONFIG_IEEE80211N
+ if (!iface->olbc_ht && !ap->ht_support) {
+ iface->olbc_ht = 1;
+ hostapd_ht_operation_update(iface);
+ wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
+ " - enable protection", MAC2STR(ap->addr));
+ set_beacon++;
+ }
+#endif /* CONFIG_IEEE80211N */
+
+ if (set_beacon)
+ ieee802_11_set_beacons(iface);
+}
+
+
+static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_iface *iface = eloop_ctx;
+ 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_set_beacons(iface);
+}
+
+
+int ap_list_init(struct hostapd_iface *iface)
+{
+ eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
+ return 0;
+}
+
+
+void ap_list_deinit(struct hostapd_iface *iface)
+{
+ eloop_cancel_timeout(ap_list_timer, iface, NULL);
+ hostapd_free_aps(iface);
+}
diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h
new file mode 100644
index 0000000..6df8981
--- /dev/null
+++ b/src/ap/ap_list.h
@@ -0,0 +1,78 @@
+/*
+ * hostapd / AP table
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2006, Devicescape Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef AP_LIST_H
+#define AP_LIST_H
+
+struct ap_info {
+ /* Note: next/prev pointers are updated whenever a new beacon is
+ * received because these are used to find the least recently used
+ * entries. iter_next/iter_prev are updated only when adding new BSSes
+ * and when removing old ones. These should be used when iterating
+ * through the table in a manner that allows beacons to be received
+ * during the iteration. */
+ struct ap_info *next; /* next entry in AP list */
+ struct ap_info *prev; /* previous entry in AP list */
+ struct ap_info *hnext; /* next entry in hash table list */
+ struct ap_info *iter_next; /* next entry in AP iteration list */
+ struct ap_info *iter_prev; /* previous entry in AP iteration list */
+ u8 addr[6];
+ u16 beacon_int;
+ u16 capability;
+ u8 supported_rates[WLAN_SUPP_RATES_MAX];
+ u8 ssid[33];
+ size_t ssid_len;
+ int wpa;
+ int erp; /* ERP Info or -1 if ERP info element not present */
+
+ int channel;
+ int datarate; /* in 100 kbps */
+ int ssi_signal;
+
+ int ht_support;
+
+ unsigned int num_beacons; /* number of beacon frames received */
+ os_time_t last_beacon;
+
+ int already_seen; /* whether API call AP-NEW has already fetched
+ * information about this AP */
+};
+
+struct ieee802_11_elems;
+struct hostapd_frame_info;
+
+struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *sta);
+int ap_ap_for_each(struct hostapd_iface *iface,
+ int (*func)(struct ap_info *s, void *data), void *data);
+void ap_list_process_beacon(struct hostapd_iface *iface,
+ const struct ieee80211_mgmt *mgmt,
+ struct ieee802_11_elems *elems,
+ struct hostapd_frame_info *fi);
+#ifdef NEED_AP_MLME
+int ap_list_init(struct hostapd_iface *iface);
+void ap_list_deinit(struct hostapd_iface *iface);
+#else /* NEED_AP_MLME */
+static inline int ap_list_init(struct hostapd_iface *iface)
+{
+ return 0;
+}
+
+static inline void ap_list_deinit(struct hostapd_iface *iface)
+{
+}
+#endif /* NEED_AP_MLME */
+
+#endif /* AP_LIST_H */
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
new file mode 100644
index 0000000..2b09b11
--- /dev/null
+++ b/src/ap/ap_mlme.c
@@ -0,0 +1,184 @@
+/*
+ * hostapd / IEEE 802.11 MLME
+ * Copyright 2003-2006, Jouni Malinen <j@w1.fi>
+ * Copyright 2003-2004, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "sta_info.h"
+#include "ap_mlme.h"
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+static const char * mlme_auth_alg_str(int alg)
+{
+ switch (alg) {
+ case WLAN_AUTH_OPEN:
+ return "OPEN_SYSTEM";
+ case WLAN_AUTH_SHARED_KEY:
+ return "SHARED_KEY";
+ case WLAN_AUTH_FT:
+ return "FT";
+ }
+
+ return "unknown";
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+/**
+ * mlme_authenticate_indication - Report the establishment of an authentication
+ * relationship with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * authentication relationship with a specific peer MAC entity that
+ * resulted from an authentication procedure that was initiated by
+ * that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ * AuthenticationType = sta->auth_alg (WLAN_AUTH_OPEN / WLAN_AUTH_SHARED_KEY)
+ */
+void mlme_authenticate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-AUTHENTICATE.indication(" MACSTR ", %s)",
+ MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg));
+ if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP))
+ mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_deauthenticate_indication - Report the invalidation of an
+ * authentication relationship with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: Peer STA data
+ * @reason_code: ReasonCode from Deauthentication frame
+ *
+ * MLME calls this function as a result of the invalidation of an
+ * authentication relationship with a specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_deauthenticate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason_code)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)",
+ MAC2STR(sta->addr), reason_code);
+ mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_associate_indication - Report the establishment of an association with
+ * a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * association with a specific peer MAC entity that resulted from an
+ * association procedure that was initiated by that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-ASSOCIATE.indication(" MACSTR ")",
+ MAC2STR(sta->addr));
+ if (sta->auth_alg != WLAN_AUTH_FT)
+ mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_reassociate_indication - Report the establishment of an reassociation
+ * with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * reassociation with a specific peer MAC entity that resulted from a
+ * reassociation procedure that was initiated by that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ *
+ * sta->previous_ap contains the "Current AP" information from ReassocReq.
+ */
+void mlme_reassociate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-REASSOCIATE.indication(" MACSTR ")",
+ MAC2STR(sta->addr));
+ if (sta->auth_alg != WLAN_AUTH_FT)
+ mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_disassociate_indication - Report disassociation with a specific peer
+ * MAC entity
+ * @hapd: BSS data
+ * @sta: Peer STA data
+ * @reason_code: ReasonCode from Disassociation frame
+ *
+ * MLME calls this function as a result of the invalidation of an association
+ * relationship with a specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_disassociate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason_code)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-DISASSOCIATE.indication(" MACSTR ", %d)",
+ MAC2STR(sta->addr), reason_code);
+ mlme_deletekeys_request(hapd, sta);
+}
+
+
+void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-MichaelMICFailure.indication(" MACSTR ")",
+ MAC2STR(addr));
+}
+
+
+void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+ HOSTAPD_LEVEL_DEBUG,
+ "MLME-DELETEKEYS.request(" MACSTR ")",
+ MAC2STR(sta->addr));
+
+ if (sta->wpa_sm)
+ wpa_remove_ptk(sta->wpa_sm);
+}
diff --git a/src/ap/ap_mlme.h b/src/ap/ap_mlme.h
new file mode 100644
index 0000000..c77a939
--- /dev/null
+++ b/src/ap/ap_mlme.h
@@ -0,0 +1,40 @@
+/*
+ * hostapd / IEEE 802.11 MLME
+ * Copyright 2003, Jouni Malinen <j@w1.fi>
+ * Copyright 2003-2004, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef MLME_H
+#define MLME_H
+
+void mlme_authenticate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta);
+
+void mlme_deauthenticate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason_code);
+
+void mlme_associate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta);
+
+void mlme_reassociate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta);
+
+void mlme_disassociate_indication(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason_code);
+
+void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
+ const u8 *addr);
+
+void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta);
+
+#endif /* MLME_H */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
new file mode 100644
index 0000000..7c87fde
--- /dev/null
+++ b/src/ap/authsrv.c
@@ -0,0 +1,217 @@
+/*
+ * Authentication server setup
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/tls.h"
+#include "eap_server/eap.h"
+#include "eap_server/eap_sim_db.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "radius/radius_server.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "authsrv.h"
+
+
+#if defined(EAP_SERVER_SIM) || defined(EAP_SERVER_AKA)
+#define EAP_SIM_DB
+#endif /* EAP_SERVER_SIM || EAP_SERVER_AKA */
+
+
+#ifdef EAP_SIM_DB
+static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx)
+{
+ if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0)
+ return 1;
+ return 0;
+}
+
+
+static void hostapd_sim_db_cb(void *ctx, void *session_ctx)
+{
+ struct hostapd_data *hapd = ctx;
+ if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) {
+#ifdef RADIUS_SERVER
+ radius_server_eap_pending_cb(hapd->radius_srv, session_ctx);
+#endif /* RADIUS_SERVER */
+ }
+}
+#endif /* EAP_SIM_DB */
+
+
+#ifdef RADIUS_SERVER
+
+static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
+ size_t identity_len, int phase2,
+ struct eap_user *user)
+{
+ const struct hostapd_eap_user *eap_user;
+ int i, count;
+
+ eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2);
+ if (eap_user == NULL)
+ return -1;
+
+ if (user == NULL)
+ return 0;
+
+ os_memset(user, 0, sizeof(*user));
+ count = EAP_USER_MAX_METHODS;
+ if (count > EAP_MAX_METHODS)
+ count = EAP_MAX_METHODS;
+ for (i = 0; i < count; i++) {
+ user->methods[i].vendor = eap_user->methods[i].vendor;
+ user->methods[i].method = eap_user->methods[i].method;
+ }
+
+ if (eap_user->password) {
+ user->password = os_malloc(eap_user->password_len);
+ if (user->password == NULL)
+ return -1;
+ os_memcpy(user->password, eap_user->password,
+ eap_user->password_len);
+ user->password_len = eap_user->password_len;
+ user->password_hash = eap_user->password_hash;
+ }
+ user->force_version = eap_user->force_version;
+ user->ttls_auth = eap_user->ttls_auth;
+
+ return 0;
+}
+
+
+static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
+{
+ struct radius_server_conf srv;
+ struct hostapd_bss_config *conf = hapd->conf;
+ os_memset(&srv, 0, sizeof(srv));
+ srv.client_file = conf->radius_server_clients;
+ srv.auth_port = conf->radius_server_auth_port;
+ srv.conf_ctx = conf;
+ srv.eap_sim_db_priv = hapd->eap_sim_db_priv;
+ srv.ssl_ctx = hapd->ssl_ctx;
+ srv.msg_ctx = hapd->msg_ctx;
+ srv.pac_opaque_encr_key = conf->pac_opaque_encr_key;
+ srv.eap_fast_a_id = conf->eap_fast_a_id;
+ srv.eap_fast_a_id_len = conf->eap_fast_a_id_len;
+ srv.eap_fast_a_id_info = conf->eap_fast_a_id_info;
+ srv.eap_fast_prov = conf->eap_fast_prov;
+ srv.pac_key_lifetime = conf->pac_key_lifetime;
+ srv.pac_key_refresh_time = conf->pac_key_refresh_time;
+ srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
+ srv.tnc = conf->tnc;
+ srv.wps = hapd->wps;
+ srv.ipv6 = conf->radius_server_ipv6;
+ srv.get_eap_user = hostapd_radius_get_eap_user;
+ srv.eap_req_id_text = conf->eap_req_id_text;
+ srv.eap_req_id_text_len = conf->eap_req_id_text_len;
+ srv.pwd_group = conf->pwd_group;
+
+ hapd->radius_srv = radius_server_init(&srv);
+ if (hapd->radius_srv == NULL) {
+ wpa_printf(MSG_ERROR, "RADIUS server initialization failed.");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* RADIUS_SERVER */
+
+
+int authsrv_init(struct hostapd_data *hapd)
+{
+#ifdef EAP_TLS_FUNCS
+ if (hapd->conf->eap_server &&
+ (hapd->conf->ca_cert || hapd->conf->server_cert ||
+ hapd->conf->dh_file)) {
+ struct tls_connection_params params;
+
+ hapd->ssl_ctx = tls_init(NULL);
+ if (hapd->ssl_ctx == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize TLS");
+ authsrv_deinit(hapd);
+ return -1;
+ }
+
+ os_memset(&params, 0, sizeof(params));
+ params.ca_cert = hapd->conf->ca_cert;
+ params.client_cert = hapd->conf->server_cert;
+ params.private_key = hapd->conf->private_key;
+ params.private_key_passwd = hapd->conf->private_key_passwd;
+ params.dh_file = hapd->conf->dh_file;
+
+ if (tls_global_set_params(hapd->ssl_ctx, &params)) {
+ wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
+ authsrv_deinit(hapd);
+ return -1;
+ }
+
+ if (tls_global_set_verify(hapd->ssl_ctx,
+ hapd->conf->check_crl)) {
+ wpa_printf(MSG_ERROR, "Failed to enable check_crl");
+ authsrv_deinit(hapd);
+ return -1;
+ }
+ }
+#endif /* EAP_TLS_FUNCS */
+
+#ifdef EAP_SIM_DB
+ if (hapd->conf->eap_sim_db) {
+ hapd->eap_sim_db_priv =
+ eap_sim_db_init(hapd->conf->eap_sim_db,
+ hostapd_sim_db_cb, hapd);
+ if (hapd->eap_sim_db_priv == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM "
+ "database interface");
+ authsrv_deinit(hapd);
+ return -1;
+ }
+ }
+#endif /* EAP_SIM_DB */
+
+#ifdef RADIUS_SERVER
+ if (hapd->conf->radius_server_clients &&
+ hostapd_setup_radius_srv(hapd))
+ return -1;
+#endif /* RADIUS_SERVER */
+
+ return 0;
+}
+
+
+void authsrv_deinit(struct hostapd_data *hapd)
+{
+#ifdef RADIUS_SERVER
+ radius_server_deinit(hapd->radius_srv);
+ hapd->radius_srv = NULL;
+#endif /* RADIUS_SERVER */
+
+#ifdef EAP_TLS_FUNCS
+ if (hapd->ssl_ctx) {
+ tls_deinit(hapd->ssl_ctx);
+ hapd->ssl_ctx = NULL;
+ }
+#endif /* EAP_TLS_FUNCS */
+
+#ifdef EAP_SIM_DB
+ if (hapd->eap_sim_db_priv) {
+ eap_sim_db_deinit(hapd->eap_sim_db_priv);
+ hapd->eap_sim_db_priv = NULL;
+ }
+#endif /* EAP_SIM_DB */
+}
diff --git a/src/ap/authsrv.h b/src/ap/authsrv.h
new file mode 100644
index 0000000..be3051e
--- /dev/null
+++ b/src/ap/authsrv.h
@@ -0,0 +1,21 @@
+/*
+ * Authentication server setup
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef AUTHSRV_H
+#define AUTHSRV_H
+
+int authsrv_init(struct hostapd_data *hapd);
+void authsrv_deinit(struct hostapd_data *hapd);
+
+#endif /* AUTHSRV_H */
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
new file mode 100644
index 0000000..5544925
--- /dev/null
+++ b/src/ap/beacon.c
@@ -0,0 +1,535 @@
+/*
+ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
+ * Copyright (c) 2002-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "drivers/driver.h"
+#include "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"
+
+
+static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
+{
+ u8 erp = 0;
+
+ if (hapd->iface->current_mode == NULL ||
+ hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return 0;
+
+ switch (hapd->iconf->cts_protection_type) {
+ case CTS_PROTECTION_FORCE_ENABLED:
+ erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION;
+ break;
+ case CTS_PROTECTION_FORCE_DISABLED:
+ erp = 0;
+ break;
+ case CTS_PROTECTION_AUTOMATIC:
+ if (hapd->iface->olbc)
+ erp |= ERP_INFO_USE_PROTECTION;
+ /* continue */
+ case CTS_PROTECTION_AUTOMATIC_NO_OLBC:
+ if (hapd->iface->num_sta_non_erp > 0) {
+ erp |= ERP_INFO_NON_ERP_PRESENT |
+ ERP_INFO_USE_PROTECTION;
+ }
+ break;
+ }
+ if (hapd->iface->num_sta_no_short_preamble > 0 ||
+ hapd->iconf->preamble == LONG_PREAMBLE)
+ erp |= ERP_INFO_BARKER_PREAMBLE_MODE;
+
+ return erp;
+}
+
+
+static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid)
+{
+ *eid++ = WLAN_EID_DS_PARAMS;
+ *eid++ = 1;
+ *eid++ = hapd->iconf->channel;
+ return eid;
+}
+
+
+static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid)
+{
+ if (hapd->iface->current_mode == NULL ||
+ hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return eid;
+
+ /* Set NonERP_present and use_protection bits if there
+ * are any associated NonERP stations. */
+ /* TODO: use_protection bit can be set to zero even if
+ * there are NonERP stations present. This optimization
+ * might be useful if NonERP stations are "quiet".
+ * See 802.11g/D6 E-1 for recommended practice.
+ * In addition, Non ERP present might be set, if AP detects Non ERP
+ * operation on other APs. */
+
+ /* Add ERP Information element */
+ *eid++ = WLAN_EID_ERP_INFO;
+ *eid++ = 1;
+ *eid++ = ieee802_11_erp_info(hapd);
+
+ return eid;
+}
+
+
+static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing,
+ struct hostapd_channel_data *start,
+ struct hostapd_channel_data *prev)
+{
+ if (end - pos < 3)
+ return pos;
+
+ /* first channel number */
+ *pos++ = start->chan;
+ /* number of channels */
+ *pos++ = (prev->chan - start->chan) / chan_spacing + 1;
+ /* maximum transmit power level */
+ *pos++ = start->max_tx_power;
+
+ return pos;
+}
+
+
+static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
+ int max_len)
+{
+ u8 *pos = eid;
+ u8 *end = eid + max_len;
+ int i;
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *start, *prev;
+ int chan_spacing = 1;
+
+ if (!hapd->iconf->ieee80211d || max_len < 6 ||
+ hapd->iface->current_mode == NULL)
+ return eid;
+
+ *pos++ = WLAN_EID_COUNTRY;
+ pos++; /* length will be set later */
+ os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
+ pos += 3;
+
+ mode = hapd->iface->current_mode;
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A)
+ chan_spacing = 4;
+
+ start = prev = NULL;
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ if (start && prev &&
+ prev->chan + chan_spacing == chan->chan &&
+ start->max_tx_power == chan->max_tx_power) {
+ prev = chan;
+ continue; /* can use same entry */
+ }
+
+ if (start) {
+ pos = hostapd_eid_country_add(pos, end, chan_spacing,
+ start, prev);
+ start = NULL;
+ }
+
+ /* Start new group */
+ start = prev = chan;
+ }
+
+ if (start) {
+ pos = hostapd_eid_country_add(pos, end, chan_spacing,
+ start, prev);
+ }
+
+ if ((pos - eid) & 1) {
+ if (end - pos < 1)
+ return eid;
+ *pos++ = 0; /* pad for 16-bit alignment */
+ }
+
+ eid[1] = (pos - eid) - 2;
+
+ return pos;
+}
+
+
+static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len,
+ struct sta_info *sta)
+{
+ const u8 *ie;
+ size_t ielen;
+
+ ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
+ if (ie == NULL || ielen > len)
+ return eid;
+
+ os_memcpy(eid, ie, ielen);
+ return eid + ielen;
+}
+
+
+void handle_probe_req(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct ieee80211_mgmt *resp;
+ struct ieee802_11_elems elems;
+ char *ssid;
+ u8 *pos, *epos;
+ const u8 *ie;
+ size_t ssid_len, ie_len;
+ struct sta_info *sta = NULL;
+ size_t buflen;
+ size_t i;
+
+ ie = mgmt->u.probe_req.variable;
+ 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, ie, ie_len) > 0)
+ return;
+
+ if (!hapd->iconf->send_probe_response)
+ return;
+
+ if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ ssid = NULL;
+ ssid_len = 0;
+
+ if ((!elems.ssid || !elems.supp_rates)) {
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
+ "without SSID or supported rates element",
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+#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);
+ }
+#endif /* CONFIG_P2P */
+
+ if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
+ "broadcast SSID ignored", MAC2STR(mgmt->sa));
+ return;
+ }
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+
+#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 */
+
+ if (elems.ssid_len == 0 ||
+ (elems.ssid_len == hapd->conf->ssid.ssid_len &&
+ os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) ==
+ 0)) {
+ ssid = hapd->conf->ssid.ssid;
+ ssid_len = hapd->conf->ssid.ssid_len;
+ if (sta)
+ sta->ssid_probe = &hapd->conf->ssid;
+ }
+
+ if (!ssid) {
+ if (!(mgmt->da[0] & 0x01)) {
+ char ssid_txt[33];
+ ieee802_11_print_ssid(ssid_txt, elems.ssid,
+ elems.ssid_len);
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+ " for foreign SSID '%s' (DA " MACSTR ")",
+ MAC2STR(mgmt->sa), ssid_txt,
+ MAC2STR(mgmt->da));
+ }
+ return;
+ }
+
+ /* TODO: verify that supp_rates contains at least one matching rate
+ * with AP configuration */
+#define MAX_PROBERESP_LEN 768
+ buflen = MAX_PROBERESP_LEN;
+#ifdef CONFIG_WPS
+ if (hapd->wps_probe_resp_ie)
+ buflen += wpabuf_len(hapd->wps_probe_resp_ie);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if (hapd->p2p_probe_resp_ie)
+ buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
+#endif /* CONFIG_P2P */
+ resp = os_zalloc(buflen);
+ if (resp == NULL)
+ return;
+ epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
+
+ resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_PROBE_RESP);
+ os_memcpy(resp->da, mgmt->sa, ETH_ALEN);
+ os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+
+ os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+ resp->u.probe_resp.beacon_int =
+ host_to_le16(hapd->iconf->beacon_int);
+
+ /* hardware or low-level driver will setup seq_ctrl and timestamp */
+ resp->u.probe_resp.capab_info =
+ host_to_le16(hostapd_own_capab_info(hapd, sta, 1));
+
+ pos = resp->u.probe_resp.variable;
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = ssid_len;
+ os_memcpy(pos, ssid, ssid_len);
+ pos += ssid_len;
+
+ /* Supported rates */
+ pos = hostapd_eid_supp_rates(hapd, pos);
+
+ /* DS Params */
+ pos = hostapd_eid_ds_params(hapd, pos);
+
+ pos = hostapd_eid_country(hapd, pos, epos - pos);
+
+ /* ERP Information element */
+ pos = hostapd_eid_erp_info(hapd, pos);
+
+ /* Extended supported rates */
+ pos = hostapd_eid_ext_supp_rates(hapd, pos);
+
+ /* RSN, MDIE, WPA */
+ pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta);
+
+#ifdef CONFIG_IEEE80211N
+ pos = hostapd_eid_ht_capabilities(hapd, pos);
+ pos = hostapd_eid_ht_operation(hapd, pos);
+#endif /* CONFIG_IEEE80211N */
+
+ pos = hostapd_eid_ext_capab(hapd, pos);
+
+ /* 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) && elems.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 */
+
+ if (hostapd_drv_send_mlme(hapd, resp, pos - (u8 *) resp) < 0)
+ perror("handle_probe_req: send");
+
+ os_free(resp);
+
+ wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
+ "SSID", MAC2STR(mgmt->sa),
+ elems.ssid_len == 0 ? "broadcast" : "our");
+}
+
+
+void ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+ struct ieee80211_mgmt *head;
+ u8 *pos, *tail, *tailpos;
+ u16 capab_info;
+ size_t head_len, tail_len;
+
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & (P2P_ENABLED | P2P_GROUP_OWNER)) == P2P_ENABLED)
+ goto no_beacon;
+#endif /* CONFIG_P2P */
+
+#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 */
+ tailpos = tail = os_malloc(tail_len);
+ if (head == NULL || tail == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to set beacon data");
+ os_free(head);
+ os_free(tail);
+ return;
+ }
+
+ head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_BEACON);
+ head->duration = host_to_le16(0);
+ os_memset(head->da, 0xff, ETH_ALEN);
+
+ os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+ head->u.beacon.beacon_int =
+ host_to_le16(hapd->iconf->beacon_int);
+
+ /* hardware or low-level driver will setup seq_ctrl and timestamp */
+ capab_info = hostapd_own_capab_info(hapd, NULL, 0);
+ head->u.beacon.capab_info = host_to_le16(capab_info);
+ pos = &head->u.beacon.variable[0];
+
+ /* SSID */
+ *pos++ = WLAN_EID_SSID;
+ if (hapd->conf->ignore_broadcast_ssid == 2) {
+ /* clear the data, but keep the correct length of the SSID */
+ *pos++ = hapd->conf->ssid.ssid_len;
+ os_memset(pos, 0, hapd->conf->ssid.ssid_len);
+ pos += hapd->conf->ssid.ssid_len;
+ } else if (hapd->conf->ignore_broadcast_ssid) {
+ *pos++ = 0; /* empty SSID */
+ } else {
+ *pos++ = hapd->conf->ssid.ssid_len;
+ os_memcpy(pos, hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len);
+ pos += hapd->conf->ssid.ssid_len;
+ }
+
+ /* Supported rates */
+ pos = hostapd_eid_supp_rates(hapd, pos);
+
+ /* DS Params */
+ pos = hostapd_eid_ds_params(hapd, pos);
+
+ head_len = pos - (u8 *) head;
+
+ tailpos = hostapd_eid_country(hapd, tailpos,
+ tail + BEACON_TAIL_BUF_SIZE - tailpos);
+
+ /* ERP Information element */
+ tailpos = hostapd_eid_erp_info(hapd, tailpos);
+
+ /* Extended supported rates */
+ tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
+
+ /* RSN, MDIE, WPA */
+ tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
+ tailpos, NULL);
+
+#ifdef CONFIG_IEEE80211N
+ tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
+ tailpos = hostapd_eid_ht_operation(hapd, tailpos);
+#endif /* CONFIG_IEEE80211N */
+
+ tailpos = hostapd_eid_ext_capab(hapd, tailpos);
+
+ /* 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 */
+
+ tail_len = tailpos > tail ? tailpos - tail : 0;
+
+ if (hostapd_drv_set_beacon(hapd, (u8 *) head, head_len,
+ tail, tail_len, hapd->conf->dtim_period,
+ hapd->iconf->beacon_int))
+ wpa_printf(MSG_ERROR, "Failed to set beacon head/tail or DTIM "
+ "period");
+
+ os_free(tail);
+ os_free(head);
+
+#ifdef CONFIG_P2P
+no_beacon:
+#endif /* CONFIG_P2P */
+ hostapd_set_bss_params(hapd, !!(ieee802_11_erp_info(hapd) &
+ ERP_INFO_USE_PROTECTION));
+}
+
+
+void ieee802_11_set_beacons(struct hostapd_iface *iface)
+{
+ size_t i;
+ for (i = 0; i < iface->num_bss; i++)
+ ieee802_11_set_beacon(iface->bss[i]);
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
new file mode 100644
index 0000000..c1510e1
--- /dev/null
+++ b/src/ap/beacon.h
@@ -0,0 +1,36 @@
+/*
+ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
+ * Copyright (c) 2002-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef BEACON_H
+#define BEACON_H
+
+struct ieee80211_mgmt;
+
+void handle_probe_req(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+#ifdef NEED_AP_MLME
+void ieee802_11_set_beacon(struct hostapd_data *hapd);
+void ieee802_11_set_beacons(struct hostapd_iface *iface);
+#else /* NEED_AP_MLME */
+static inline void ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+}
+
+static inline void ieee802_11_set_beacons(struct hostapd_iface *iface)
+{
+}
+#endif /* NEED_AP_MLME */
+
+#endif /* BEACON_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
new file mode 100644
index 0000000..d348dc1
--- /dev/null
+++ b/src/ap/ctrl_iface_ap.c
@@ -0,0 +1,108 @@
+/*
+ * Control interface for shared AP commands
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "wps_hostapd.h"
+#include "p2p_hostapd.h"
+#include "ctrl_iface_ap.h"
+
+
+static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ int len, res, ret;
+
+ if (sta == NULL) {
+ ret = os_snprintf(buf, buflen, "FAIL\n");
+ if (ret < 0 || (size_t) ret >= buflen)
+ return 0;
+ return ret;
+ }
+
+ len = 0;
+ ret = os_snprintf(buf + len, buflen - len, MACSTR "\n",
+ MAC2STR(sta->addr));
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
+ res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
+ res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
+ res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
+ buflen - len);
+ if (res >= 0)
+ len += res;
+ res = hostapd_p2p_get_mib_sta(hapd, 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);
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
new file mode 100644
index 0000000..8690bea
--- /dev/null
+++ b/src/ap/ctrl_iface_ap.h
@@ -0,0 +1,25 @@
+/*
+ * Control interface for shared AP commands
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef CTRL_IFACE_AP_H
+#define CTRL_IFACE_AP_H
+
+int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
+ char *buf, size_t buflen);
+int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
+ char *buf, size_t buflen);
+int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+ char *buf, size_t buflen);
+
+#endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
new file mode 100644
index 0000000..02b7ecf
--- /dev/null
+++ b/src/ap/drv_callbacks.c
@@ -0,0 +1,539 @@
+/*
+ * hostapd / Callback functions for driver wrappers
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "radius/radius.h"
+#include "drivers/driver.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/random.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "accounting.h"
+#include "tkip_countermeasures.h"
+#include "iapp.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "wmm.h"
+#include "wps_hostapd.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+
+
+int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *ie, size_t ielen, int reassoc)
+{
+ struct sta_info *sta;
+ int new_assoc, res;
+ struct ieee802_11_elems elems;
+#ifdef CONFIG_P2P
+ const u8 *all_ies = ie;
+ size_t all_ies_len = ielen;
+#endif /* CONFIG_P2P */
+
+ 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(ie, ielen, &elems, 0);
+ if (elems.wps_ie) {
+ ie = elems.wps_ie - 2;
+ ielen = elems.wps_ie_len + 2;
+ wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)AssocReq");
+ } else if (elems.rsn_ie) {
+ ie = elems.rsn_ie - 2;
+ ielen = elems.rsn_ie_len + 2;
+ wpa_printf(MSG_DEBUG, "STA included RSN IE in (Re)AssocReq");
+ } else if (elems.wpa_ie) {
+ ie = elems.wpa_ie - 2;
+ ielen = elems.wpa_ie_len + 2;
+ wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
+ } else {
+ ie = NULL;
+ ielen = 0;
+ wpa_printf(MSG_DEBUG, "STA did not include WPS/RSN/WPA IE in "
+ "(Re)AssocReq");
+ }
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta) {
+ accounting_sta_stop(hapd, sta);
+ } else {
+ sta = ap_sta_add(hapd, addr);
+ if (sta == NULL)
+ return -1;
+ }
+ sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
+
+#ifdef CONFIG_P2P
+ if (elems.p2p) {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = ieee802_11_vendor_ie_concat(all_ies, all_ies_len,
+ P2P_IE_VENDOR_TYPE);
+ }
+#endif /* CONFIG_P2P */
+
+ if (hapd->conf->wpa) {
+ if (ie == NULL || ielen == 0) {
+ if (hapd->conf->wps_state) {
+ wpa_printf(MSG_DEBUG, "STA did not include "
+ "WPA/RSN IE in (Re)Association "
+ "Request - possible WPS use");
+ sta->flags |= WLAN_STA_MAYBE_WPS;
+ goto skip_wpa_check;
+ }
+
+ wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
+ return -1;
+ }
+ if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
+ os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+ sta->flags |= WLAN_STA_WPS;
+ goto skip_wpa_check;
+ }
+
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
+ "machine");
+ return -1;
+ }
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ ie, ielen, NULL, 0);
+ if (res != WPA_IE_OK) {
+ int resp;
+ wpa_printf(MSG_DEBUG, "WPA/RSN information element "
+ "rejected? (res %u)", res);
+ wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
+ if (res == WPA_INVALID_GROUP)
+ resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+ else if (res == WPA_INVALID_PAIRWISE)
+ resp = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
+ else if (res == WPA_INVALID_AKMP)
+ resp = WLAN_REASON_AKMP_NOT_VALID;
+#ifdef CONFIG_IEEE80211W
+ else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
+ resp = WLAN_REASON_INVALID_IE;
+ else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
+ resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+#endif /* CONFIG_IEEE80211W */
+ else
+ resp = WLAN_REASON_INVALID_IE;
+ hostapd_drv_sta_disassoc(hapd, sta->addr, resp);
+ ap_free_sta(hapd, sta);
+ return -1;
+ }
+ } else if (hapd->conf->wps_state) {
+#ifdef CONFIG_WPS_STRICT
+ if (ie) {
+ struct wpabuf *wps;
+ wps = ieee802_11_vendor_ie_concat(ie, ielen,
+ WPS_IE_VENDOR_TYPE);
+ if (wps && wps_validate_assoc_req(wps) < 0) {
+ hostapd_drv_sta_disassoc(
+ hapd, sta->addr,
+ WLAN_REASON_INVALID_IE);
+ ap_free_sta(hapd, sta);
+ wpabuf_free(wps);
+ return -1;
+ }
+ wpabuf_free(wps);
+ }
+#endif /* CONFIG_WPS_STRICT */
+ if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 &&
+ os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+ sta->flags |= WLAN_STA_WPS;
+ } else
+ sta->flags |= WLAN_STA_MAYBE_WPS;
+ }
+skip_wpa_check:
+
+ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+
+ hostapd_new_assoc_sta(hapd, sta, !new_assoc);
+
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+
+#ifdef CONFIG_P2P
+ p2p_group_notif_assoc(hapd->p2p_group, sta->addr,
+ all_ies, all_ies_len);
+#endif /* CONFIG_P2P */
+
+ return 0;
+}
+
+
+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;
+ }
+
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
+ MAC2STR(sta->addr));
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ ap_free_sta(hapd, sta);
+}
+
+
+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);
+}
+
+
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa,
+ const u8 *ie, size_t ie_len)
+{
+ size_t i;
+ int ret = 0;
+
+ 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, ie, ie_len) > 0) {
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+#ifdef HOSTAPD
+
+#ifdef NEED_AP_MLME
+
+static const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
+{
+ u16 fc, type, stype;
+
+ /*
+ * PS-Poll frames are 16 bytes. All other frames are
+ * 24 bytes or longer.
+ */
+ if (len < 16)
+ return NULL;
+
+ fc = le_to_host16(hdr->frame_control);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ switch (type) {
+ case WLAN_FC_TYPE_DATA:
+ if (len < 24)
+ return NULL;
+ switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+ case WLAN_FC_FROMDS | WLAN_FC_TODS:
+ case WLAN_FC_TODS:
+ return hdr->addr1;
+ case WLAN_FC_FROMDS:
+ return hdr->addr2;
+ default:
+ return NULL;
+ }
+ case WLAN_FC_TYPE_CTRL:
+ if (stype != WLAN_FC_STYPE_PSPOLL)
+ return NULL;
+ return hdr->addr1;
+ case WLAN_FC_TYPE_MGMT:
+ return hdr->addr3;
+ default:
+ return NULL;
+ }
+}
+
+
+#define HAPD_BROADCAST ((struct hostapd_data *) -1)
+
+static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+ const u8 *bssid)
+{
+ size_t i;
+
+ if (bssid == NULL)
+ return NULL;
+ if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff &&
+ bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff)
+ return HAPD_BROADCAST;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0)
+ return iface->bss[i];
+ }
+
+ return NULL;
+}
+
+
+static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
+ const u8 *frame, size_t len)
+{
+ const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
+ u16 fc = le_to_host16(hdr->frame_control);
+ hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+ if (hapd == NULL || hapd == HAPD_BROADCAST)
+ return;
+
+ ieee802_11_rx_from_unknown(hapd, hdr->addr2,
+ (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+ (WLAN_FC_TODS | WLAN_FC_FROMDS));
+}
+
+
+static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ const struct ieee80211_hdr *hdr;
+ const u8 *bssid;
+ struct hostapd_frame_info fi;
+
+ hdr = (const struct ieee80211_hdr *) rx_mgmt->frame;
+ bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len);
+ if (bssid == NULL)
+ return;
+
+ hapd = get_hapd_bssid(iface, bssid);
+ if (hapd == NULL) {
+ u16 fc;
+ fc = le_to_host16(hdr->frame_control);
+
+ /*
+ * Drop frames to unknown BSSIDs except for Beacon frames which
+ * could be used to update neighbor information.
+ */
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+ hapd = iface->bss[0];
+ else
+ return;
+ }
+
+ os_memset(&fi, 0, sizeof(fi));
+ fi.datarate = rx_mgmt->datarate;
+ fi.ssi_signal = rx_mgmt->ssi_signal;
+
+ if (hapd == HAPD_BROADCAST) {
+ size_t i;
+ for (i = 0; i < iface->num_bss; i++)
+ ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
+ rx_mgmt->frame_len, &fi);
+ } else
+ ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi);
+
+ random_add_randomness(&fi, sizeof(fi));
+}
+
+
+static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
+ size_t len, u16 stype, int ok)
+{
+ struct ieee80211_hdr *hdr;
+ hdr = (struct ieee80211_hdr *) buf;
+ hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+ if (hapd == NULL || hapd == HAPD_BROADCAST)
+ return;
+ ieee802_11_mgmt_cb(hapd, buf, len, stype, ok);
+}
+
+#endif /* NEED_AP_MLME */
+
+
+static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ if (sta)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR
+ " - adding a new STA", MAC2STR(addr));
+ sta = ap_sta_add(hapd, addr);
+ if (sta) {
+ hostapd_new_assoc_sta(hapd, sta, 0);
+ } else {
+ wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR,
+ MAC2STR(addr));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
+ const u8 *data, size_t data_len)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ size_t j;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ if (ap_get_sta(iface->bss[j], src)) {
+ hapd = iface->bss[j];
+ break;
+ }
+ }
+
+ ieee802_1x_receive(hapd, src, data, data_len);
+}
+
+
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data)
+{
+ struct hostapd_data *hapd = ctx;
+
+ switch (event) {
+ case EVENT_MICHAEL_MIC_FAILURE:
+ michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
+ break;
+ case EVENT_SCAN_RESULTS:
+ if (hapd->iface->scan_cb)
+ hapd->iface->scan_cb(hapd->iface);
+ break;
+#ifdef CONFIG_IEEE80211R
+ case EVENT_FT_RRB_RX:
+ wpa_ft_rrb_rx(hapd->wpa_auth, data->ft_rrb_rx.src,
+ data->ft_rrb_rx.data, data->ft_rrb_rx.data_len);
+ break;
+#endif /* CONFIG_IEEE80211R */
+ case EVENT_WPS_BUTTON_PUSHED:
+ hostapd_wps_button_pushed(hapd, 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_RX_FROM_UNKNOWN:
+ hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.frame,
+ data->rx_from_unknown.len);
+ break;
+ case EVENT_RX_MGMT:
+ hostapd_mgmt_rx(hapd, &data->rx_mgmt);
+ break;
+#endif /* NEED_AP_MLME */
+ case EVENT_RX_PROBE_REQ:
+ 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.ie,
+ data->rx_probe_req.ie_len);
+ break;
+ case EVENT_NEW_STA:
+ hostapd_event_new_sta(hapd, data->new_sta.addr);
+ break;
+ case EVENT_EAPOL_RX:
+ hostapd_event_eapol_rx(hapd, data->eapol_rx.src,
+ data->eapol_rx.data,
+ data->eapol_rx.data_len);
+ break;
+ case EVENT_ASSOC:
+ hostapd_notif_assoc(hapd, data->assoc_info.addr,
+ data->assoc_info.req_ies,
+ data->assoc_info.req_ies_len,
+ 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;
+ default:
+ wpa_printf(MSG_DEBUG, "Unknown event %d", event);
+ break;
+ }
+}
+
+#endif /* HOSTAPD */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
new file mode 100644
index 0000000..4e5eb01
--- /dev/null
+++ b/src/ap/hostapd.c
@@ -0,0 +1,929 @@
+/*
+ * hostapd / Initialization and configuration
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "radius/radius_client.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "authsrv.h"
+#include "sta_info.h"
+#include "accounting.h"
+#include "ap_list.h"
+#include "beacon.h"
+#include "iapp.h"
+#include "ieee802_1x.h"
+#include "ieee802_11_auth.h"
+#include "vlan_init.h"
+#include "wpa_auth.h"
+#include "wps_hostapd.h"
+#include "hw_features.h"
+#include "wpa_auth_glue.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "p2p_hostapd.h"
+
+
+static int hostapd_flush_old_stations(struct hostapd_data *hapd);
+static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
+
+extern int wpa_debug_level;
+
+
+static void hostapd_reload_bss(struct hostapd_data *hapd)
+{
+#ifndef CONFIG_NO_RADIUS
+ radius_client_reconfig(hapd->radius, hapd->conf->radius);
+#endif /* CONFIG_NO_RADIUS */
+
+ 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);
+ else if (hapd->conf->wpa) {
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+ hostapd_reconfig_wpa(hapd);
+ wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len);
+ if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len))
+ wpa_printf(MSG_ERROR, "Failed to configure WPA IE for "
+ "the kernel driver.");
+ } else if (hapd->wpa_auth) {
+ wpa_deinit(hapd->wpa_auth);
+ hapd->wpa_auth = NULL;
+ hostapd_set_privacy(hapd, 0);
+ hostapd_setup_encryption(hapd->conf->iface, hapd);
+ hostapd_set_generic_elem(hapd, (u8 *) "", 0);
+ }
+
+ ieee802_11_set_beacon(hapd);
+ hostapd_update_wps(hapd);
+
+ if (hapd->conf->ssid.ssid_set &&
+ hostapd_set_ssid(hapd, (u8 *) hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len)) {
+ wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
+ /* try to continue */
+ }
+ wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
+}
+
+
+int hostapd_reload_config(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ struct hostapd_config *newconf, *oldconf;
+ size_t j;
+
+ if (iface->config_read_cb == NULL)
+ return -1;
+ newconf = iface->config_read_cb(iface->config_fname);
+ if (newconf == NULL)
+ return -1;
+
+ /*
+ * Deauthenticate all stations since the new configuration may not
+ * allow them to use the BSS anymore.
+ */
+ for (j = 0; j < iface->num_bss; j++) {
+ hostapd_flush_old_stations(iface->bss[j]);
+
+#ifndef CONFIG_NO_RADIUS
+ /* TODO: update dynamic data based on changed configuration
+ * items (e.g., open/close sockets, etc.) */
+ radius_client_flush(iface->bss[j]->radius, 0);
+#endif /* CONFIG_NO_RADIUS */
+ }
+
+ 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++;
+ }
+
+ if (ssid->dyn_vlan_keys) {
+ size_t i;
+ for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) {
+ const char *ifname;
+ struct hostapd_wep_keys *key = ssid->dyn_vlan_keys[i];
+ if (key == NULL)
+ continue;
+ ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan,
+ i);
+ if (ifname == NULL)
+ continue;
+
+ idx = key->idx;
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
+ broadcast_ether_addr, idx, 1,
+ NULL, 0, key->key[idx],
+ key->len[idx])) {
+ wpa_printf(MSG_WARNING, "Could not set "
+ "dynamic VLAN WEP encryption.");
+ errors++;
+ }
+ }
+ }
+
+ return errors;
+}
+
+/**
+ * hostapd_cleanup - Per-BSS cleanup (deinitialization)
+ * @hapd: Pointer to BSS data
+ *
+ * This function is used to free all per-BSS data structures and resources.
+ * This gets called in a loop for each BSS between calls to
+ * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface
+ * is deinitialized. Most of the modules that are initialized in
+ * hostapd_setup_bss() are deinitialized here.
+ */
+static void hostapd_cleanup(struct hostapd_data *hapd)
+{
+ if (hapd->iface->ctrl_iface_deinit)
+ hapd->iface->ctrl_iface_deinit(hapd);
+
+ iapp_deinit(hapd->iapp);
+ hapd->iapp = NULL;
+ accounting_deinit(hapd);
+ hostapd_deinit_wpa(hapd);
+ vlan_deinit(hapd);
+ hostapd_acl_deinit(hapd);
+#ifndef CONFIG_NO_RADIUS
+ radius_client_deinit(hapd->radius);
+ hapd->radius = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+ hostapd_deinit_wps(hapd);
+
+ authsrv_deinit(hapd);
+
+ if (hapd->interface_added &&
+ hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
+ wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
+ hapd->conf->iface);
+ }
+
+ os_free(hapd->probereq_cb);
+ hapd->probereq_cb = NULL;
+
+#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 */
+}
+
+
+/**
+ * hostapd_cleanup_iface_pre - Preliminary per-interface cleanup
+ * @iface: Pointer to interface data
+ *
+ * This function is called before per-BSS data structures are deinitialized
+ * with hostapd_cleanup().
+ */
+static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface)
+{
+}
+
+
+/**
+ * hostapd_cleanup_iface - Complete per-interface cleanup
+ * @iface: Pointer to interface data
+ *
+ * This function is called after per-BSS data structures are deinitialized
+ * with hostapd_cleanup().
+ */
+static void hostapd_cleanup_iface(struct hostapd_iface *iface)
+{
+ hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+ iface->hw_features = NULL;
+ os_free(iface->current_rates);
+ iface->current_rates = NULL;
+ ap_list_deinit(iface);
+ hostapd_config_free(iface->conf);
+ iface->conf = NULL;
+
+ os_free(iface->config_fname);
+ os_free(iface->bss);
+ os_free(iface);
+}
+
+
+static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
+{
+ int i;
+
+ hostapd_broadcast_wep_set(hapd);
+
+ if (hapd->conf->ssid.wep.default_len) {
+ hostapd_set_privacy(hapd, 1);
+ return 0;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (hapd->conf->ssid.wep.key[i] &&
+ 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)
+{
+ int ret = 0;
+ u8 addr[ETH_ALEN];
+
+ if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "Flushing old station entries");
+ if (hostapd_flush(hapd)) {
+ wpa_printf(MSG_WARNING, "Could not connect to kernel driver.");
+ ret = -1;
+ }
+ wpa_printf(MSG_DEBUG, "Deauthenticate all stations");
+ os_memset(addr, 0xff, ETH_ALEN);
+ hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+ 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 res;
+ int auto_addr = 0;
+
+ if (hostapd_drv_none(hapd))
+ return 0;
+
+ /* Generate BSSID mask that is large enough to cover the BSSIDs. */
+
+ /* Determine the bits necessary to cover the number of BSSIDs. */
+ for (i--; i; i >>= 1)
+ bits++;
+
+ /* Determine the bits necessary to any configured BSSIDs,
+ if they are higher than the number of BSSIDs. */
+ for (j = 0; j < iface->conf->num_bss; j++) {
+ if (hostapd_mac_comp_empty(iface->conf->bss[j].bssid) == 0) {
+ if (j)
+ auto_addr++;
+ continue;
+ }
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ mask[i] |=
+ iface->conf->bss[j].bssid[i] ^
+ hapd->own_addr[i];
+ }
+ }
+
+ if (!auto_addr)
+ goto skip_mask_ext;
+
+ for (i = 0; i < ETH_ALEN && mask[i] == 0; i++)
+ ;
+ j = 0;
+ if (i < ETH_ALEN) {
+ j = (5 - i) * 8;
+
+ while (mask[i] != 0) {
+ mask[i] >>= 1;
+ j++;
+ }
+ }
+
+ if (bits < j)
+ bits = j;
+
+ if (bits > 40) {
+ wpa_printf(MSG_ERROR, "Too many bits in the BSSID mask (%u)",
+ bits);
+ return -1;
+ }
+
+ os_memset(mask, 0xff, ETH_ALEN);
+ j = bits / 8;
+ for (i = 5; i > 5 - j; i--)
+ mask[i] = 0;
+ j = bits % 8;
+ while (j--)
+ mask[i] <<= 1;
+
+skip_mask_ext:
+ wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
+ (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits);
+
+ res = hostapd_valid_bss_mask(hapd, hapd->own_addr, mask);
+ if (res == 0)
+ return 0;
+
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "Driver did not accept BSSID mask "
+ MACSTR " for start address " MACSTR ".",
+ MAC2STR(mask), MAC2STR(hapd->own_addr));
+ return -1;
+ }
+
+ if (!auto_addr)
+ return 0;
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ if ((hapd->own_addr[i] & mask[i]) != hapd->own_addr[i]) {
+ wpa_printf(MSG_ERROR, "Invalid BSSID mask " MACSTR
+ " for start address " MACSTR ".",
+ MAC2STR(mask), MAC2STR(hapd->own_addr));
+ wpa_printf(MSG_ERROR, "Start address must be the "
+ "first address in the block (i.e., addr "
+ "AND mask == addr).");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int mac_in_conf(struct hostapd_config *conf, const void *a)
+{
+ size_t i;
+
+ for (i = 0; i < conf->num_bss; i++) {
+ if (hostapd_mac_comp(conf->bss[i].bssid, a) == 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+
+
+/**
+ * hostapd_setup_bss - Per-BSS setup (initialization)
+ * @hapd: Pointer to BSS data
+ * @first: Whether this BSS is the first BSS of an interface
+ *
+ * This function is used to initialize all per-BSS data structures and
+ * resources. This gets called in a loop for each BSS when an interface is
+ * initialized. Most of the modules that are initialized here will be
+ * deinitialized in hostapd_cleanup().
+ */
+static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
+{
+ struct hostapd_bss_config *conf = hapd->conf;
+ u8 ssid[HOSTAPD_MAX_SSID_LEN + 1];
+ int ssid_len, set_ssid;
+ char force_ifname[IFNAMSIZ];
+ u8 if_addr[ETH_ALEN];
+
+ if (!first) {
+ if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) {
+ /* Allocate the next available BSSID. */
+ do {
+ inc_byte_array(hapd->own_addr, ETH_ALEN);
+ } while (mac_in_conf(hapd->iconf, hapd->own_addr));
+ } else {
+ /* Allocate the configured BSSID. */
+ os_memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN);
+
+ if (hostapd_mac_comp(hapd->own_addr,
+ hapd->iface->bss[0]->own_addr) ==
+ 0) {
+ wpa_printf(MSG_ERROR, "BSS '%s' may not have "
+ "BSSID set to the MAC address of "
+ "the radio", hapd->conf->iface);
+ return -1;
+ }
+ }
+
+ hapd->interface_added = 1;
+ if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
+ hapd->conf->iface, hapd->own_addr, hapd,
+ &hapd->drv_priv, force_ifname, if_addr,
+ hapd->conf->bridge[0] ? hapd->conf->bridge :
+ NULL)) {
+ 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);
+ hostapd_set_privacy(hapd, 0);
+
+ hostapd_broadcast_wep_clear(hapd);
+ if (hostapd_setup_encryption(hapd->conf->iface, hapd))
+ return -1;
+
+ /*
+ * Fetch the SSID from the system and use it or,
+ * if one was specified in the config file, verify they
+ * match.
+ */
+ ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid));
+ if (ssid_len < 0) {
+ wpa_printf(MSG_ERROR, "Could not read SSID from system");
+ return -1;
+ }
+ if (conf->ssid.ssid_set) {
+ /*
+ * If SSID is specified in the config file and it differs
+ * from what is being used then force installation of the
+ * new SSID.
+ */
+ set_ssid = (conf->ssid.ssid_len != (size_t) ssid_len ||
+ os_memcmp(conf->ssid.ssid, ssid, ssid_len) != 0);
+ } else {
+ /*
+ * No SSID in the config file; just use the one we got
+ * from the system.
+ */
+ set_ssid = 0;
+ conf->ssid.ssid_len = ssid_len;
+ os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len);
+ conf->ssid.ssid[conf->ssid.ssid_len] = '\0';
+ }
+
+ if (!hostapd_drv_none(hapd)) {
+ wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR
+ " and ssid '%s'",
+ hapd->conf->iface, MAC2STR(hapd->own_addr),
+ hapd->conf->ssid.ssid);
+ }
+
+ if (hostapd_setup_wpa_psk(conf)) {
+ wpa_printf(MSG_ERROR, "WPA-PSK setup failed.");
+ return -1;
+ }
+
+ /* Set SSID for the kernel driver (to be used in beacon and probe
+ * response frames) */
+ if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid.ssid,
+ conf->ssid.ssid_len)) {
+ wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
+ return -1;
+ }
+
+ if (wpa_debug_level == MSG_MSGDUMP)
+ conf->radius->msg_dumps = 1;
+#ifndef CONFIG_NO_RADIUS
+ hapd->radius = radius_client_init(hapd, conf->radius);
+ if (hapd->radius == NULL) {
+ wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
+ return -1;
+ }
+#endif /* CONFIG_NO_RADIUS */
+
+ if (hostapd_acl_init(hapd)) {
+ wpa_printf(MSG_ERROR, "ACL initialization failed.");
+ return -1;
+ }
+ if (hostapd_init_wps(hapd, conf))
+ return -1;
+
+ if (authsrv_init(hapd) < 0)
+ return -1;
+
+ if (ieee802_1x_init(hapd)) {
+ wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed.");
+ return -1;
+ }
+
+ if (hapd->conf->wpa && hostapd_setup_wpa(hapd))
+ return -1;
+
+ if (accounting_init(hapd)) {
+ wpa_printf(MSG_ERROR, "Accounting initialization failed.");
+ return -1;
+ }
+
+ if (hapd->conf->ieee802_11f &&
+ (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) {
+ wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization "
+ "failed.");
+ return -1;
+ }
+
+ if (hapd->iface->ctrl_iface_init &&
+ hapd->iface->ctrl_iface_init(hapd)) {
+ wpa_printf(MSG_ERROR, "Failed to setup control interface");
+ return -1;
+ }
+
+ if (!hostapd_drv_none(hapd) && vlan_init(hapd)) {
+ wpa_printf(MSG_ERROR, "VLAN initialization failed.");
+ return -1;
+ }
+
+ ieee802_11_set_beacon(hapd);
+
+ 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 setup_interface(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ size_t i;
+ char country[4];
+
+ /*
+ * Make sure that all BSSes get configured with a pointer to the same
+ * driver interface.
+ */
+ for (i = 1; i < iface->num_bss; i++) {
+ iface->bss[i]->driver = hapd->driver;
+ iface->bss[i]->drv_priv = hapd->drv_priv;
+ }
+
+ if (hostapd_validate_bssid_configuration(iface))
+ return -1;
+
+ if (hapd->iconf->country[0] && hapd->iconf->country[1]) {
+ os_memcpy(country, hapd->iconf->country, 3);
+ country[3] = '\0';
+ if (hostapd_set_country(hapd, country) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to set country code");
+ return -1;
+ }
+ }
+
+ if (hostapd_get_hw_features(iface)) {
+ /* Not all drivers support this yet, so continue without hw
+ * feature data. */
+ } else {
+ int ret = hostapd_select_hw_mode(iface);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "Could not select hw_mode and "
+ "channel. (%d)", ret);
+ return -1;
+ }
+ ret = hostapd_check_ht_capab(iface);
+ if (ret < 0)
+ return -1;
+ if (ret == 1) {
+ wpa_printf(MSG_DEBUG, "Interface initialization will "
+ "be completed in a callback");
+ return 0;
+ }
+ }
+ return hostapd_setup_interface_complete(iface, 0);
+}
+
+
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ size_t j;
+ u8 *prev_addr;
+
+ if (err) {
+ wpa_printf(MSG_ERROR, "Interface initialization failed");
+ eloop_terminate();
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Completing interface initialization");
+ if (hapd->iconf->channel) {
+ iface->freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel);
+ wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d "
+ "Frequency: %d MHz",
+ hostapd_hw_mode_txt(hapd->iconf->hw_mode),
+ hapd->iconf->channel, iface->freq);
+
+ if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+ hapd->iconf->channel,
+ hapd->iconf->ieee80211n,
+ hapd->iconf->secondary_channel)) {
+ wpa_printf(MSG_ERROR, "Could not set channel for "
+ "kernel driver");
+ return -1;
+ }
+ }
+
+ if (iface->current_mode) {
+ if (hostapd_prepare_rates(hapd, 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;
+ }
+
+ hostapd_tx_queue_params(iface);
+
+ ap_list_init(iface);
+
+ if (hostapd_driver_commit(hapd) < 0) {
+ wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
+ "configuration", __func__);
+ return -1;
+ }
+
+ 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);
+
+ return 0;
+}
+
+
+/**
+ * hostapd_setup_interface - Setup of an interface
+ * @iface: Pointer to interface data.
+ * Returns: 0 on success, -1 on failure
+ *
+ * Initializes the driver interface, validates the configuration,
+ * and sets driver parameters based on the configuration.
+ * Flushes old stations, sets the channel, encryption,
+ * beacons, and WDS links based on the configuration.
+ */
+int hostapd_setup_interface(struct hostapd_iface *iface)
+{
+ int ret;
+
+ ret = setup_interface(iface);
+ if (ret) {
+ wpa_printf(MSG_ERROR, "%s: Unable to setup interface.",
+ iface->bss[0]->conf->iface);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * hostapd_alloc_bss_data - Allocate and initialize per-BSS data
+ * @hapd_iface: Pointer to interface data
+ * @conf: Pointer to per-interface configuration
+ * @bss: Pointer to per-BSS configuration for this BSS
+ * Returns: Pointer to allocated BSS data
+ *
+ * This function is used to allocate per-BSS data structure. This data will be
+ * freed after hostapd_cleanup() is called for it during interface
+ * deinitialization.
+ */
+struct hostapd_data *
+hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ struct hostapd_config *conf,
+ struct hostapd_bss_config *bss)
+{
+ struct hostapd_data *hapd;
+
+ hapd = os_zalloc(sizeof(*hapd));
+ if (hapd == NULL)
+ return NULL;
+
+ hapd->new_assoc_sta_cb = hostapd_new_assoc_sta;
+ hapd->iconf = conf;
+ hapd->conf = bss;
+ hapd->iface = hapd_iface;
+ hapd->driver = hapd->iconf->driver;
+
+ return hapd;
+}
+
+
+void hostapd_interface_deinit(struct hostapd_iface *iface)
+{
+ size_t j;
+
+ if (iface == NULL)
+ return;
+
+ hostapd_cleanup_iface_pre(iface);
+ for (j = 0; j < iface->num_bss; j++) {
+ struct hostapd_data *hapd = iface->bss[j];
+ hostapd_free_stas(hapd);
+ hostapd_flush_old_stations(hapd);
+ hostapd_cleanup(hapd);
+ }
+}
+
+
+void hostapd_interface_free(struct hostapd_iface *iface)
+{
+ size_t j;
+ for (j = 0; j < iface->num_bss; j++)
+ os_free(iface->bss[j]);
+ hostapd_cleanup_iface(iface);
+}
+
+
+/**
+ * hostapd_new_assoc_sta - Notify that a new station associated with the AP
+ * @hapd: Pointer to BSS data
+ * @sta: Pointer to the associated STA data
+ * @reassoc: 1 to indicate this was a re-association; 0 = first association
+ *
+ * This function will be called whenever a station associates with the AP. It
+ * can be called from ieee802_11.c for drivers that export MLME to hostapd and
+ * from drv_callbacks.c based on driver events for drivers that take care of
+ * management frames (IEEE 802.11 authentication and association) internally.
+ */
+void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ int reassoc)
+{
+ if (hapd->tkip_countermeasures) {
+ 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)
+ accounting_sta_start(hapd, sta);
+
+ /* Start IEEE 802.1X authentication process for new stations */
+ ieee802_1x_new_station(hapd, sta);
+ if (reassoc) {
+ if (sta->auth_alg != WLAN_AUTH_FT &&
+ !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
+ wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
+ } else
+ wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
+}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
new file mode 100644
index 0000000..d4501a1
--- /dev/null
+++ b/src/ap/hostapd.h
@@ -0,0 +1,262 @@
+/*
+ * hostapd / Initialization and configuration
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef HOSTAPD_H
+#define HOSTAPD_H
+
+#include "common/defs.h"
+
+struct wpa_driver_ops;
+struct wpa_ctrl_dst;
+struct radius_server_data;
+struct upnp_wps_device_sm;
+struct hapd_interfaces;
+struct hostapd_data;
+struct sta_info;
+struct hostap_sta_driver_data;
+struct ieee80211_ht_capabilities;
+struct full_dynamic_vlan;
+enum wps_event;
+union wps_event_data;
+
+struct hostapd_probereq_cb {
+ int (*cb)(void *ctx, const u8 *sa, const u8 *ie, size_t ie_len);
+ void *ctx;
+};
+
+#define HOSTAPD_RATE_BASIC 0x00000001
+
+struct hostapd_rate_data {
+ int rate; /* rate in 100 kbps */
+ int flags; /* HOSTAPD_RATE_ flags */
+};
+
+struct hostapd_frame_info {
+ u32 channel;
+ u32 datarate;
+ u32 ssi_signal;
+};
+
+
+/**
+ * struct hostapd_data - hostapd per-BSS data structure
+ */
+struct hostapd_data {
+ struct hostapd_iface *iface;
+ struct hostapd_config *iconf;
+ struct hostapd_bss_config *conf;
+ int interface_added; /* virtual interface added for this BSS */
+
+ u8 own_addr[ETH_ALEN];
+
+ int num_sta; /* number of entries in sta_list */
+ struct sta_info *sta_list; /* STA info list head */
+#define STA_HASH_SIZE 256
+#define STA_HASH(sta) (sta[5])
+ struct sta_info *sta_hash[STA_HASH_SIZE];
+
+ /*
+ * Bitfield for indicating which AIDs are allocated. Only AID values
+ * 1-2007 are used and as such, the bit at index 0 corresponds to AID
+ * 1.
+ */
+#define AID_WORDS ((2008 + 31) / 32)
+ u32 sta_aid[AID_WORDS];
+
+ const struct wpa_driver_ops *driver;
+ void *drv_priv;
+
+ void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
+ struct sta_info *sta, int reassoc);
+
+ void *msg_ctx; /* ctx for wpa_msg() calls */
+
+ struct radius_client_data *radius;
+ u32 acct_session_id_hi, acct_session_id_lo;
+
+ struct iapp_data *iapp;
+
+ struct hostapd_cached_radius_acl *acl_cache;
+ struct hostapd_acl_query_data *acl_queries;
+
+ struct wpa_authenticator *wpa_auth;
+ struct eapol_authenticator *eapol_auth;
+
+ struct rsn_preauth_interface *preauth_iface;
+ time_t michael_mic_failure;
+ int michael_mic_failures;
+ int tkip_countermeasures;
+
+ int ctrl_sock;
+ struct wpa_ctrl_dst *ctrl_dst;
+
+ void *ssl_ctx;
+ void *eap_sim_db_priv;
+ struct radius_server_data *radius_srv;
+
+ int parameter_set_count;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ struct full_dynamic_vlan *full_dynamic_vlan;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+ struct l2_packet_data *l2;
+ struct wps_context *wps;
+
+ struct wpabuf *wps_beacon_ie;
+ struct wpabuf *wps_probe_resp_ie;
+#ifdef CONFIG_WPS
+ unsigned int ap_pin_failures;
+ struct upnp_wps_device_sm *wps_upnp;
+ unsigned int ap_pin_lockout_time;
+#endif /* CONFIG_WPS */
+
+ struct hostapd_probereq_cb *probereq_cb;
+ size_t num_probereq_cb;
+
+ void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
+ int freq);
+ void *public_action_cb_ctx;
+
+ 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);
+ void *sta_authorized_cb_ctx;
+
+ void (*setup_complete_cb)(void *ctx);
+ void *setup_complete_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 */
+};
+
+
+/**
+ * struct hostapd_iface - hostapd per-interface data structure
+ */
+struct hostapd_iface {
+ struct hapd_interfaces *interfaces;
+ void *owner;
+ int (*reload_config)(struct hostapd_iface *iface);
+ struct hostapd_config * (*config_read_cb)(const char *config_fname);
+ char *config_fname;
+ struct hostapd_config *conf;
+
+ size_t num_bss;
+ struct hostapd_data **bss;
+
+ int num_ap; /* number of entries in ap_list */
+ struct ap_info *ap_list; /* AP info list head */
+ struct ap_info *ap_hash[STA_HASH_SIZE];
+ struct ap_info *ap_iter_list;
+
+ unsigned int drv_flags;
+ struct hostapd_hw_modes *hw_features;
+ int num_hw_features;
+ struct hostapd_hw_modes *current_mode;
+ /* Rates that are currently used (i.e., filtered copy of
+ * current_mode->channels */
+ int num_rates;
+ struct hostapd_rate_data *current_rates;
+ int freq;
+
+ u16 hw_flags;
+
+ /* Number of associated Non-ERP stations (i.e., stations using 802.11b
+ * in 802.11g BSS) */
+ int num_sta_non_erp;
+
+ /* Number of associated stations that do not support Short Slot Time */
+ int num_sta_no_short_slot_time;
+
+ /* Number of associated stations that do not support Short Preamble */
+ int num_sta_no_short_preamble;
+
+ int olbc; /* Overlapping Legacy BSS Condition */
+
+ /* Number of HT associated stations that do not support greenfield */
+ int num_sta_ht_no_gf;
+
+ /* Number of associated non-HT stations */
+ int num_sta_no_ht;
+
+ /* Number of HT associated stations 20 MHz */
+ int num_sta_ht_20mhz;
+
+ /* Overlapping BSS information */
+ int olbc_ht;
+
+ u16 ht_op_mode;
+ void (*scan_cb)(struct hostapd_iface *iface);
+
+ int (*ctrl_iface_init)(struct hostapd_data *hapd);
+ void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
+
+ int (*for_each_interface)(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
+};
+
+/* hostapd.c */
+int hostapd_reload_config(struct hostapd_iface *iface);
+struct hostapd_data *
+hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+ struct hostapd_config *conf,
+ struct hostapd_bss_config *bss);
+int hostapd_setup_interface(struct hostapd_iface *iface);
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
+void hostapd_interface_deinit(struct hostapd_iface *iface);
+void hostapd_interface_free(struct hostapd_iface *iface);
+void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ int reassoc);
+
+/* utils.c */
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+ int (*cb)(void *ctx, const u8 *sa,
+ const u8 *ie, size_t ie_len),
+ void *ctx);
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
+
+/* drv_callbacks.c (TODO: move to somewhere else?) */
+int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *ie, size_t ielen, 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);
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa,
+ const u8 *ie, size_t ie_len);
+
+#endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
new file mode 100644
index 0000000..f3fffd7
--- /dev/null
+++ b/src/ap/hw_features.c
@@ -0,0 +1,751 @@
+/*
+ * hostapd / Hardware feature query and different modes
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "hw_features.h"
+
+
+void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+ size_t num_hw_features)
+{
+ size_t i;
+
+ if (hw_features == NULL)
+ return;
+
+ for (i = 0; i < num_hw_features; i++) {
+ os_free(hw_features[i].channels);
+ os_free(hw_features[i].rates);
+ }
+
+ os_free(hw_features);
+}
+
+
+int hostapd_get_hw_features(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ int ret = 0, i, j;
+ u16 num_modes, flags;
+ struct hostapd_hw_modes *modes;
+
+ if (hostapd_drv_none(hapd))
+ return -1;
+ modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags);
+ if (modes == NULL) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Fetching hardware channel/rate support not "
+ "supported.");
+ return -1;
+ }
+
+ iface->hw_flags = flags;
+
+ hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+ iface->hw_features = modes;
+ iface->num_hw_features = num_modes;
+
+ for (i = 0; i < num_modes; i++) {
+ struct hostapd_hw_modes *feature = &modes[i];
+ /* set flag for channels we can use in current regulatory
+ * domain */
+ for (j = 0; j < feature->num_channels; j++) {
+ /*
+ * Disable all channels that are marked not to allow
+ * IBSS operation or active scanning. In addition,
+ * disable all channels that require radar detection,
+ * since that (in addition to full DFS) is not yet
+ * supported.
+ */
+ if (feature->channels[j].flag &
+ (HOSTAPD_CHAN_NO_IBSS |
+ HOSTAPD_CHAN_PASSIVE_SCAN |
+ HOSTAPD_CHAN_RADAR))
+ feature->channels[j].flag |=
+ HOSTAPD_CHAN_DISABLED;
+ if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
+ "chan=%d freq=%d MHz max_tx_power=%d dBm",
+ feature->mode,
+ feature->channels[j].chan,
+ feature->channels[j].freq,
+ feature->channels[j].max_tx_power);
+ }
+ }
+
+ return ret;
+}
+
+
+int hostapd_prepare_rates(struct hostapd_data *hapd,
+ struct hostapd_hw_modes *mode)
+{
+ int i, num_basic_rates = 0;
+ int basic_rates_a[] = { 60, 120, 240, -1 };
+ int basic_rates_b[] = { 10, 20, -1 };
+ int basic_rates_g[] = { 10, 20, 55, 110, -1 };
+ int *basic_rates;
+
+ if (hapd->iconf->basic_rates)
+ basic_rates = hapd->iconf->basic_rates;
+ else switch (mode->mode) {
+ case HOSTAPD_MODE_IEEE80211A:
+ basic_rates = basic_rates_a;
+ break;
+ case HOSTAPD_MODE_IEEE80211B:
+ basic_rates = basic_rates_b;
+ break;
+ case HOSTAPD_MODE_IEEE80211G:
+ basic_rates = basic_rates_g;
+ break;
+ default:
+ return -1;
+ }
+
+ if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates,
+ basic_rates, mode->mode)) {
+ wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel "
+ "module");
+ }
+
+ os_free(hapd->iface->current_rates);
+ hapd->iface->num_rates = 0;
+
+ hapd->iface->current_rates =
+ os_zalloc(mode->num_rates * sizeof(struct hostapd_rate_data));
+ if (!hapd->iface->current_rates) {
+ wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
+ "table.");
+ return -1;
+ }
+
+ for (i = 0; i < mode->num_rates; i++) {
+ struct hostapd_rate_data *rate;
+
+ if (hapd->iconf->supported_rates &&
+ !hostapd_rate_found(hapd->iconf->supported_rates,
+ mode->rates[i]))
+ continue;
+
+ rate = &hapd->iface->current_rates[hapd->iface->num_rates];
+ rate->rate = mode->rates[i];
+ if (hostapd_rate_found(basic_rates, rate->rate)) {
+ rate->flags |= HOSTAPD_RATE_BASIC;
+ num_basic_rates++;
+ }
+ wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
+ hapd->iface->num_rates, rate->rate, rate->flags);
+ hapd->iface->num_rates++;
+ }
+
+ if ((hapd->iface->num_rates == 0 || num_basic_rates == 0) &&
+ (!hapd->iconf->ieee80211n || !hapd->iconf->require_ht)) {
+ wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
+ "rate sets (%d,%d).",
+ hapd->iface->num_rates, num_basic_rates);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211N
+static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
+{
+ int sec_chan, ok, j, first;
+ int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+ 184, 192 };
+ size_t k;
+
+ if (!iface->conf->secondary_channel)
+ return 1; /* HT40 not used */
+
+ sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4;
+ wpa_printf(MSG_DEBUG, "HT40: control channel: %d "
+ "secondary channel: %d",
+ iface->conf->channel, sec_chan);
+
+ /* Verify that HT40 secondary channel is an allowed 20 MHz
+ * channel */
+ ok = 0;
+ for (j = 0; j < iface->current_mode->num_channels; j++) {
+ struct hostapd_channel_data *chan =
+ &iface->current_mode->channels[j];
+ if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+ chan->chan == sec_chan) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok) {
+ wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
+ sec_chan);
+ return 0;
+ }
+
+ /*
+ * Verify that HT40 primary,secondary channel pair is allowed per
+ * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
+ * 2.4 GHz rules allow all cases where the secondary channel fits into
+ * the list of allowed channels (already checked above).
+ */
+ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 1;
+
+ if (iface->conf->secondary_channel > 0)
+ first = iface->conf->channel;
+ else
+ first = sec_chan;
+
+ ok = 0;
+ for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) {
+ if (first == allowed[k]) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok) {
+ wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
+ iface->conf->channel,
+ iface->conf->secondary_channel);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
+{
+ if (iface->conf->secondary_channel > 0) {
+ iface->conf->channel += 4;
+ iface->conf->secondary_channel = -1;
+ } else {
+ iface->conf->channel -= 4;
+ iface->conf->secondary_channel = 1;
+ }
+}
+
+
+static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
+ int *pri_chan, int *sec_chan)
+{
+ struct ieee80211_ht_operation *oper;
+ struct ieee802_11_elems elems;
+
+ *pri_chan = *sec_chan = 0;
+
+ ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+ if (elems.ht_operation &&
+ elems.ht_operation_len >= sizeof(*oper)) {
+ oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+ *pri_chan = oper->control_chan;
+ if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
+ 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 wpa_scan_results_free(struct wpa_scan_results *res)
+{
+ size_t i;
+
+ if (res == NULL)
+ return;
+
+ for (i = 0; i < res->num; i++)
+ os_free(res->res[i]);
+ os_free(res->res);
+ os_free(res);
+}
+
+
+static void ieee80211n_check_scan(struct hostapd_iface *iface)
+{
+ struct wpa_scan_results *scan_res;
+ int oper40;
+ int res;
+
+ /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
+ * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */
+
+ iface->scan_cb = NULL;
+
+ scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
+ if (scan_res == NULL) {
+ hostapd_setup_interface_complete(iface, 1);
+ return;
+ }
+
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
+ oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
+ else
+ oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
+ wpa_scan_results_free(scan_res);
+
+ if (!oper40) {
+ wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
+ "channel pri=%d sec=%d based on overlapping BSSes",
+ iface->conf->channel,
+ iface->conf->channel +
+ iface->conf->secondary_channel * 4);
+ iface->conf->secondary_channel = 0;
+ iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+ }
+
+ res = ieee80211n_allowed_ht40_channel_pair(iface);
+ hostapd_setup_interface_complete(iface, !res);
+}
+
+
+static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
+{
+ struct wpa_driver_scan_params params;
+
+ if (!iface->conf->secondary_channel)
+ return 0; /* HT40 not used */
+
+ wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
+ "40 MHz channel");
+ os_memset(&params, 0, sizeof(params));
+ /* TODO: scan only the needed frequency */
+ if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to request a scan of "
+ "neighboring BSSes");
+ return -1;
+ }
+
+ iface->scan_cb = ieee80211n_check_scan;
+ return 1;
+}
+
+
+static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
+{
+ u16 hw = iface->current_mode->ht_capab;
+ u16 conf = iface->conf->ht_capab;
+
+ if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
+ !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [LDPC]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+ !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [HT40*]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) &&
+ (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [SMPS-*]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
+ !(hw & HT_CAP_INFO_GREEN_FIELD)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [GF]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
+ !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [SHORT-GI-20]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
+ !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [SHORT-GI-40]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [TX-STBC]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
+ (hw & HT_CAP_INFO_RX_STBC_MASK)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [RX-STBC*]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_DELAYED_BA) &&
+ !(hw & HT_CAP_INFO_DELAYED_BA)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [DELAYED-BA]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
+ !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [MAX-AMSDU-7935]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
+ !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [DSSS_CCK-40]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [PSMP]");
+ return 0;
+ }
+
+ if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
+ !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured "
+ "HT capability [LSIG-TXOP-PROT]");
+ return 0;
+ }
+
+ return 1;
+}
+
+#endif /* CONFIG_IEEE80211N */
+
+
+int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211N
+ int ret;
+ if (!iface->conf->ieee80211n)
+ return 0;
+ if (!ieee80211n_supported_ht_capab(iface))
+ return -1;
+ ret = ieee80211n_check_40mhz(iface);
+ if (ret)
+ return ret;
+ if (!ieee80211n_allowed_ht40_channel_pair(iface))
+ return -1;
+#endif /* CONFIG_IEEE80211N */
+
+ return 0;
+}
+
+
+/**
+ * hostapd_select_hw_mode - Select the hardware mode
+ * @iface: Pointer to interface data.
+ * Returns: 0 on success, -1 on failure
+ *
+ * Sets up the hardware mode, channel, rates, and passive scanning
+ * based on the configuration.
+ */
+int hostapd_select_hw_mode(struct hostapd_iface *iface)
+{
+ int i, j, ok;
+
+ if (iface->num_hw_features < 1)
+ return -1;
+
+ iface->current_mode = NULL;
+ for (i = 0; i < iface->num_hw_features; i++) {
+ struct hostapd_hw_modes *mode = &iface->hw_features[i];
+ if (mode->mode == iface->conf->hw_mode) {
+ iface->current_mode = mode;
+ break;
+ }
+ }
+
+ if (iface->current_mode == NULL) {
+ wpa_printf(MSG_ERROR, "Hardware does not support configured "
+ "mode");
+ hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Hardware does not support configured mode "
+ "(%d)", (int) iface->conf->hw_mode);
+ return -1;
+ }
+
+ ok = 0;
+ for (j = 0; j < iface->current_mode->num_channels; j++) {
+ struct hostapd_channel_data *chan =
+ &iface->current_mode->channels[j];
+ if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+ (chan->chan == iface->conf->channel)) {
+ ok = 1;
+ break;
+ }
+ }
+ if (ok && iface->conf->secondary_channel) {
+ int sec_ok = 0;
+ int sec_chan = iface->conf->channel +
+ iface->conf->secondary_channel * 4;
+ 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)) {
+ sec_ok = 1;
+ break;
+ }
+ }
+ if (!sec_ok) {
+ hostapd_logger(iface->bss[0], NULL,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Configured HT40 secondary channel "
+ "(%d) not found from the channel list "
+ "of current mode (%d) %s",
+ sec_chan, iface->current_mode->mode,
+ hostapd_hw_mode_txt(
+ iface->current_mode->mode));
+ ok = 0;
+ }
+ }
+ if (iface->conf->channel == 0) {
+ /* TODO: could request a scan of neighboring BSSes and select
+ * the channel automatically */
+ wpa_printf(MSG_ERROR, "Channel not configured "
+ "(hw_mode/channel in hostapd.conf)");
+ return -1;
+ }
+ if (ok == 0 && iface->conf->channel != 0) {
+ hostapd_logger(iface->bss[0], NULL,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Configured channel (%d) not found from the "
+ "channel list of current mode (%d) %s",
+ iface->conf->channel,
+ iface->current_mode->mode,
+ hostapd_hw_mode_txt(iface->current_mode->mode));
+ iface->current_mode = NULL;
+ }
+
+ if (iface->current_mode == NULL) {
+ hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Hardware does not support configured channel");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+const char * hostapd_hw_mode_txt(int mode)
+{
+ switch (mode) {
+ case HOSTAPD_MODE_IEEE80211A:
+ return "IEEE 802.11a";
+ case HOSTAPD_MODE_IEEE80211B:
+ return "IEEE 802.11b";
+ case HOSTAPD_MODE_IEEE80211G:
+ return "IEEE 802.11g";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
+{
+ int i;
+
+ if (!hapd->iface->current_mode)
+ return 0;
+
+ for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
+ struct hostapd_channel_data *ch =
+ &hapd->iface->current_mode->channels[i];
+ if (ch->chan == chan)
+ return ch->freq;
+ }
+
+ return 0;
+}
+
+
+int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
+{
+ int i;
+
+ if (!hapd->iface->current_mode)
+ return 0;
+
+ for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
+ struct hostapd_channel_data *ch =
+ &hapd->iface->current_mode->channels[i];
+ if (ch->freq == freq)
+ return ch->chan;
+ }
+
+ return 0;
+}
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
new file mode 100644
index 0000000..88c2322
--- /dev/null
+++ b/src/ap/hw_features.h
@@ -0,0 +1,70 @@
+/*
+ * hostapd / Hardware feature query and different modes
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef HW_FEATURES_H
+#define HW_FEATURES_H
+
+#ifdef NEED_AP_MLME
+void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+ size_t num_hw_features);
+int hostapd_get_hw_features(struct hostapd_iface *iface);
+int hostapd_select_hw_mode(struct hostapd_iface *iface);
+const char * hostapd_hw_mode_txt(int mode);
+int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
+int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
+int hostapd_check_ht_capab(struct hostapd_iface *iface);
+int hostapd_prepare_rates(struct hostapd_data *hapd,
+ 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 -1;
+}
+
+static inline const char * hostapd_hw_mode_txt(int mode)
+{
+ return NULL;
+}
+
+static inline int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
+{
+ return -1;
+}
+
+static inline int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+ return 0;
+}
+
+static inline int hostapd_prepare_rates(struct hostapd_data *hapd,
+ struct hostapd_hw_modes *mode)
+{
+ return 0;
+}
+
+#endif /* NEED_AP_MLME */
+
+#endif /* HW_FEATURES_H */
diff --git a/src/ap/iapp.c b/src/ap/iapp.c
new file mode 100644
index 0000000..115d91e
--- /dev/null
+++ b/src/ap/iapp.c
@@ -0,0 +1,535 @@
+/*
+ * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * Note: IEEE 802.11F-2003 was a experimental use specification. It has expired
+ * and IEEE has withdrawn it. In other words, it is likely better to look at
+ * using some other mechanism for AP-to-AP communication than extending the
+ * implementation here.
+ */
+
+/* TODO:
+ * Level 1: no administrative or security support
+ * (e.g., static BSSID to IP address mapping in each AP)
+ * Level 2: support for dynamic mapping of BSSID to IP address
+ * Level 3: support for encryption and authentication of IAPP messages
+ * - add support for MOVE-notify and MOVE-response (this requires support for
+ * finding out IP address for previous AP using RADIUS)
+ * - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during
+ * reassociation to another AP
+ * - implement counters etc. for IAPP MIB
+ * - verify endianness of fields in IAPP messages; are they big-endian as
+ * used here?
+ * - RADIUS connection for AP registration and BSSID to IP address mapping
+ * - TCP connection for IAPP MOVE, CACHE
+ * - broadcast ESP for IAPP ADD-notify
+ * - ESP for IAPP MOVE messages
+ * - security block sending/processing
+ * - IEEE 802.11 context transfer
+ */
+
+#include "utils/includes.h"
+#include <net/if.h>
+#include <sys/ioctl.h>
+#ifdef USE_KERNEL_HEADERS
+#include <linux/if_packet.h>
+#else /* USE_KERNEL_HEADERS */
+#include <netpacket/packet.h>
+#endif /* USE_KERNEL_HEADERS */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "iapp.h"
+
+
+#define IAPP_MULTICAST "224.0.1.178"
+#define IAPP_UDP_PORT 3517
+#define IAPP_TCP_PORT 3517
+
+struct iapp_hdr {
+ u8 version;
+ u8 command;
+ be16 identifier;
+ be16 length;
+ /* followed by length-6 octets of data */
+} __attribute__ ((packed));
+
+#define IAPP_VERSION 0
+
+enum IAPP_COMMAND {
+ IAPP_CMD_ADD_notify = 0,
+ IAPP_CMD_MOVE_notify = 1,
+ IAPP_CMD_MOVE_response = 2,
+ IAPP_CMD_Send_Security_Block = 3,
+ IAPP_CMD_ACK_Security_Block = 4,
+ IAPP_CMD_CACHE_notify = 5,
+ IAPP_CMD_CACHE_response = 6,
+};
+
+
+/* ADD-notify - multicast UDP on the local LAN */
+struct iapp_add_notify {
+ u8 addr_len; /* ETH_ALEN */
+ u8 reserved;
+ u8 mac_addr[ETH_ALEN];
+ be16 seq_num;
+} __attribute__ ((packed));
+
+
+/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
+struct iapp_layer2_update {
+ u8 da[ETH_ALEN]; /* broadcast */
+ u8 sa[ETH_ALEN]; /* STA addr */
+ be16 len; /* 6 */
+ u8 dsap; /* null DSAP address */
+ u8 ssap; /* null SSAP address, CR=Response */
+ u8 control;
+ u8 xid_info[3];
+} __attribute__ ((packed));
+
+
+/* MOVE-notify - unicast TCP */
+struct iapp_move_notify {
+ u8 addr_len; /* ETH_ALEN */
+ u8 reserved;
+ u8 mac_addr[ETH_ALEN];
+ u16 seq_num;
+ u16 ctx_block_len;
+ /* followed by ctx_block_len bytes */
+} __attribute__ ((packed));
+
+
+/* MOVE-response - unicast TCP */
+struct iapp_move_response {
+ u8 addr_len; /* ETH_ALEN */
+ u8 status;
+ u8 mac_addr[ETH_ALEN];
+ u16 seq_num;
+ u16 ctx_block_len;
+ /* followed by ctx_block_len bytes */
+} __attribute__ ((packed));
+
+enum {
+ IAPP_MOVE_SUCCESSFUL = 0,
+ IAPP_MOVE_DENIED = 1,
+ IAPP_MOVE_STALE_MOVE = 2,
+};
+
+
+/* CACHE-notify */
+struct iapp_cache_notify {
+ u8 addr_len; /* ETH_ALEN */
+ u8 reserved;
+ u8 mac_addr[ETH_ALEN];
+ u16 seq_num;
+ u8 current_ap[ETH_ALEN];
+ u16 ctx_block_len;
+ /* ctx_block_len bytes of context block followed by 16-bit context
+ * timeout */
+} __attribute__ ((packed));
+
+
+/* CACHE-response - unicast TCP */
+struct iapp_cache_response {
+ u8 addr_len; /* ETH_ALEN */
+ u8 status;
+ u8 mac_addr[ETH_ALEN];
+ u16 seq_num;
+} __attribute__ ((packed));
+
+enum {
+ IAPP_CACHE_SUCCESSFUL = 0,
+ IAPP_CACHE_STALE_CACHE = 1,
+};
+
+
+/* Send-Security-Block - unicast TCP */
+struct iapp_send_security_block {
+ u8 iv[8];
+ u16 sec_block_len;
+ /* followed by sec_block_len bytes of security block */
+} __attribute__ ((packed));
+
+
+/* ACK-Security-Block - unicast TCP */
+struct iapp_ack_security_block {
+ u8 iv[8];
+ u8 new_ap_ack_authenticator[48];
+} __attribute__ ((packed));
+
+
+struct iapp_data {
+ struct hostapd_data *hapd;
+ u16 identifier; /* next IAPP identifier */
+ struct in_addr own, multicast;
+ int udp_sock;
+ int packet_sock;
+};
+
+
+static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num)
+{
+ char buf[128];
+ struct iapp_hdr *hdr;
+ struct iapp_add_notify *add;
+ struct sockaddr_in addr;
+
+ /* Send IAPP ADD-notify to remove possible association from other APs
+ */
+
+ hdr = (struct iapp_hdr *) buf;
+ hdr->version = IAPP_VERSION;
+ hdr->command = IAPP_CMD_ADD_notify;
+ hdr->identifier = host_to_be16(iapp->identifier++);
+ hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add));
+
+ add = (struct iapp_add_notify *) (hdr + 1);
+ add->addr_len = ETH_ALEN;
+ add->reserved = 0;
+ os_memcpy(add->mac_addr, mac_addr, ETH_ALEN);
+
+ add->seq_num = host_to_be16(seq_num);
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = iapp->multicast.s_addr;
+ addr.sin_port = htons(IAPP_UDP_PORT);
+ if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
+ (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ perror("sendto[IAPP-ADD]");
+}
+
+
+static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr)
+{
+ struct iapp_layer2_update msg;
+
+ /* Send Level 2 Update Frame to update forwarding tables in layer 2
+ * bridge devices */
+
+ /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
+ * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
+
+ os_memset(msg.da, 0xff, ETH_ALEN);
+ os_memcpy(msg.sa, addr, ETH_ALEN);
+ msg.len = host_to_be16(6);
+ msg.dsap = 0; /* NULL DSAP address */
+ msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */
+ msg.control = 0xaf; /* XID response lsb.1111F101.
+ * F=0 (no poll command; unsolicited frame) */
+ msg.xid_info[0] = 0x81; /* XID format identifier */
+ msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
+ msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW)
+ * FIX: what is correct RW with 802.11? */
+
+ if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
+ perror("send[L2 Update]");
+}
+
+
+/**
+ * iapp_new_station - IAPP processing for a new STA
+ * @iapp: IAPP data
+ * @sta: The associated station
+ */
+void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
+{
+ struct ieee80211_mgmt *assoc;
+ u16 seq;
+
+ if (iapp == NULL)
+ return;
+
+ assoc = sta->last_assoc_req;
+ seq = assoc ? WLAN_GET_SEQ_SEQ(le_to_host16(assoc->seq_ctrl)) : 0;
+
+ /* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */
+ hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP,
+ HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq);
+ iapp_send_layer2_update(iapp, sta->addr);
+ iapp_send_add(iapp, sta->addr, seq);
+
+ if (assoc && WLAN_FC_GET_STYPE(le_to_host16(assoc->frame_control)) ==
+ WLAN_FC_STYPE_REASSOC_REQ) {
+ /* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP,
+ * Context Block, Timeout)
+ */
+ /* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to
+ * IP address */
+ }
+}
+
+
+static void iapp_process_add_notify(struct iapp_data *iapp,
+ struct sockaddr_in *from,
+ struct iapp_hdr *hdr, int len)
+{
+ struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1);
+ struct sta_info *sta;
+
+ if (len != sizeof(*add)) {
+ printf("Invalid IAPP-ADD packet length %d (expected %lu)\n",
+ len, (unsigned long) sizeof(*add));
+ return;
+ }
+
+ sta = ap_get_sta(iapp->hapd, add->mac_addr);
+
+ /* IAPP-ADD.indication(MAC Address, Sequence Number) */
+ hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
+ HOSTAPD_LEVEL_INFO,
+ "Received IAPP ADD-notify (seq# %d) from %s:%d%s",
+ be_to_host16(add->seq_num),
+ inet_ntoa(from->sin_addr), ntohs(from->sin_port),
+ sta ? "" : " (STA not found)");
+
+ if (!sta)
+ return;
+
+ /* TODO: could use seq_num to try to determine whether last association
+ * to this AP is newer than the one advertised in IAPP-ADD. Although,
+ * this is not really a reliable verification. */
+
+ hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
+ HOSTAPD_LEVEL_DEBUG,
+ "Removing STA due to IAPP ADD-notify");
+ ap_sta_disconnect(iapp->hapd, sta, NULL, 0);
+}
+
+
+/**
+ * iapp_receive_udp - Process IAPP UDP frames
+ * @sock: File descriptor for the socket
+ * @eloop_ctx: IAPP data (struct iapp_data *)
+ * @sock_ctx: Not used
+ */
+static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct iapp_data *iapp = eloop_ctx;
+ int len, hlen;
+ unsigned char buf[128];
+ struct sockaddr_in from;
+ socklen_t fromlen;
+ struct iapp_hdr *hdr;
+
+ /* Handle incoming IAPP frames (over UDP/IP) */
+
+ fromlen = sizeof(from);
+ len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (len < 0) {
+ perror("recvfrom");
+ return;
+ }
+
+ if (from.sin_addr.s_addr == iapp->own.s_addr)
+ return; /* ignore own IAPP messages */
+
+ hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
+ HOSTAPD_LEVEL_DEBUG,
+ "Received %d byte IAPP frame from %s%s\n",
+ len, inet_ntoa(from.sin_addr),
+ len < (int) sizeof(*hdr) ? " (too short)" : "");
+
+ if (len < (int) sizeof(*hdr))
+ return;
+
+ hdr = (struct iapp_hdr *) buf;
+ hlen = be_to_host16(hdr->length);
+ hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
+ HOSTAPD_LEVEL_DEBUG,
+ "RX: version=%d command=%d id=%d len=%d\n",
+ hdr->version, hdr->command,
+ be_to_host16(hdr->identifier), hlen);
+ if (hdr->version != IAPP_VERSION) {
+ printf("Dropping IAPP frame with unknown version %d\n",
+ hdr->version);
+ return;
+ }
+ if (hlen > len) {
+ printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len);
+ return;
+ }
+ if (hlen < len) {
+ printf("Ignoring %d extra bytes from IAPP frame\n",
+ len - hlen);
+ len = hlen;
+ }
+
+ switch (hdr->command) {
+ case IAPP_CMD_ADD_notify:
+ iapp_process_add_notify(iapp, &from, hdr, hlen - sizeof(*hdr));
+ break;
+ case IAPP_CMD_MOVE_notify:
+ /* TODO: MOVE is using TCP; so move this to TCP handler once it
+ * is implemented.. */
+ /* IAPP-MOVE.indication(MAC Address, New BSSID,
+ * Sequence Number, AP Address, Context Block) */
+ /* TODO: process */
+ break;
+ default:
+ printf("Unknown IAPP command %d\n", hdr->command);
+ break;
+ }
+}
+
+
+struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
+{
+ struct ifreq ifr;
+ struct sockaddr_ll addr;
+ int ifindex;
+ struct sockaddr_in *paddr, uaddr;
+ struct iapp_data *iapp;
+ struct ip_mreqn mreq;
+
+ iapp = os_zalloc(sizeof(*iapp));
+ if (iapp == NULL)
+ return NULL;
+ iapp->hapd = hapd;
+ iapp->udp_sock = iapp->packet_sock = -1;
+
+ /* TODO:
+ * open socket for sending and receiving IAPP frames over TCP
+ */
+
+ iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (iapp->udp_sock < 0) {
+ perror("socket[PF_INET,SOCK_DGRAM]");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
+ perror("ioctl(SIOCGIFINDEX)");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+ ifindex = ifr.ifr_ifindex;
+
+ if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) {
+ perror("ioctl(SIOCGIFADDR)");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+ paddr = (struct sockaddr_in *) &ifr.ifr_addr;
+ if (paddr->sin_family != AF_INET) {
+ printf("Invalid address family %i (SIOCGIFADDR)\n",
+ paddr->sin_family);
+ iapp_deinit(iapp);
+ return NULL;
+ }
+ iapp->own.s_addr = paddr->sin_addr.s_addr;
+
+ if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) {
+ perror("ioctl(SIOCGIFBRDADDR)");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+ paddr = (struct sockaddr_in *) &ifr.ifr_addr;
+ if (paddr->sin_family != AF_INET) {
+ printf("Invalid address family %i (SIOCGIFBRDADDR)\n",
+ paddr->sin_family);
+ iapp_deinit(iapp);
+ return NULL;
+ }
+ inet_aton(IAPP_MULTICAST, &iapp->multicast);
+
+ os_memset(&uaddr, 0, sizeof(uaddr));
+ uaddr.sin_family = AF_INET;
+ uaddr.sin_port = htons(IAPP_UDP_PORT);
+ if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
+ sizeof(uaddr)) < 0) {
+ perror("bind[UDP]");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+
+ os_memset(&mreq, 0, sizeof(mreq));
+ mreq.imr_multiaddr = iapp->multicast;
+ mreq.imr_address.s_addr = INADDR_ANY;
+ mreq.imr_ifindex = 0;
+ if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq)) < 0) {
+ perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+
+ iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (iapp->packet_sock < 0) {
+ perror("socket[PF_PACKET,SOCK_RAW]");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sll_family = AF_PACKET;
+ addr.sll_ifindex = ifindex;
+ if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
+ sizeof(addr)) < 0) {
+ perror("bind[PACKET]");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+
+ if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp,
+ iapp, NULL)) {
+ printf("Could not register read socket for IAPP.\n");
+ iapp_deinit(iapp);
+ return NULL;
+ }
+
+ printf("IEEE 802.11F (IAPP) using interface %s\n", iface);
+
+ /* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive
+ * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually
+ * be openned only after receiving Initiate-Accept. If Initiate-Reject
+ * is received, IAPP is not started. */
+
+ return iapp;
+}
+
+
+void iapp_deinit(struct iapp_data *iapp)
+{
+ struct ip_mreqn mreq;
+
+ if (iapp == NULL)
+ return;
+
+ if (iapp->udp_sock >= 0) {
+ os_memset(&mreq, 0, sizeof(mreq));
+ mreq.imr_multiaddr = iapp->multicast;
+ mreq.imr_address.s_addr = INADDR_ANY;
+ mreq.imr_ifindex = 0;
+ if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]");
+ }
+
+ eloop_unregister_read_sock(iapp->udp_sock);
+ close(iapp->udp_sock);
+ }
+ if (iapp->packet_sock >= 0) {
+ eloop_unregister_read_sock(iapp->packet_sock);
+ close(iapp->packet_sock);
+ }
+ os_free(iapp);
+}
diff --git a/src/ap/iapp.h b/src/ap/iapp.h
new file mode 100644
index 0000000..5fc01cb
--- /dev/null
+++ b/src/ap/iapp.h
@@ -0,0 +1,45 @@
+/*
+ * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef IAPP_H
+#define IAPP_H
+
+struct iapp_data;
+
+#ifdef CONFIG_IAPP
+
+void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta);
+struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface);
+void iapp_deinit(struct iapp_data *iapp);
+
+#else /* CONFIG_IAPP */
+
+static inline void iapp_new_station(struct iapp_data *iapp,
+ struct sta_info *sta)
+{
+}
+
+static inline struct iapp_data * iapp_init(struct hostapd_data *hapd,
+ const char *iface)
+{
+ return NULL;
+}
+
+static inline void iapp_deinit(struct iapp_data *iapp)
+{
+}
+
+#endif /* CONFIG_IAPP */
+
+#endif /* IAPP_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
new file mode 100644
index 0000000..4d8dd25
--- /dev/null
+++ b/src/ap/ieee802_11.c
@@ -0,0 +1,1882 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "drivers/driver.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "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 "ieee802_11.h"
+
+
+u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ int i, num, count;
+
+ if (hapd->iface->current_rates == NULL)
+ return eid;
+
+ *pos++ = WLAN_EID_SUPP_RATES;
+ num = hapd->iface->num_rates;
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+ num++;
+ if (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 &&
+ hapd->iface->num_rates < 8)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_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 (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 &&
+ hapd->iface->num_rates >= 8)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_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;
+}
+
+
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+
+ if ((hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)) ==
+ 0)
+ return eid;
+
+ *pos++ = WLAN_EID_EXT_CAPAB;
+ *pos++ = 5;
+ *pos++ = 0x00;
+ *pos++ = 0x00;
+ *pos++ = 0x00;
+ *pos++ = 0x00;
+ *pos = 0x00;
+ if (hapd->conf->tdls & TDLS_PROHIBIT)
+ *pos |= 0x40; /* Bit 38 - TDLS Prohibited */
+ if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH)
+ *pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */
+ pos++;
+
+ return pos;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+static u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *eid)
+{
+ u8 *pos = eid;
+ u32 timeout, tu;
+ struct os_time now, passed;
+
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
+ os_get_time(&now);
+ os_time_sub(&now, &sta->sa_query_start, &passed);
+ tu = (passed.sec * 1000000 + passed.usec) / 1024;
+ if (hapd->conf->assoc_sa_query_max_timeout > tu)
+ timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
+ else
+ timeout = 0;
+ if (timeout < hapd->conf->assoc_sa_query_max_timeout)
+ timeout++; /* add some extra time for local timers */
+ WPA_PUT_LE32(pos, timeout);
+ pos += 4;
+
+ return pos;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len)
+{
+ int i;
+ if (len > HOSTAPD_MAX_SSID_LEN)
+ len = HOSTAPD_MAX_SSID_LEN;
+ for (i = 0; i < len; i++) {
+ if (ssid[i] >= 32 && ssid[i] < 127)
+ buf[i] = ssid[i];
+ else
+ buf[i] = '.';
+ }
+ buf[len] = '\0';
+}
+
+
+static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 auth_transaction, const u8 *challenge,
+ int iswep)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "authentication (shared key, transaction %d)",
+ auth_transaction);
+
+ if (auth_transaction == 1) {
+ if (!sta->challenge) {
+ /* Generate a pseudo-random challenge */
+ u8 key[8];
+ 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)
+ perror("send_auth_reply: send");
+
+ os_free(buf);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
+ u16 auth_transaction, u16 status,
+ const u8 *ies, size_t ies_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction,
+ status, ies, ies_len);
+
+ if (status != WLAN_STATUS_SUCCESS)
+ return;
+
+ sta = ap_get_sta(hapd, dst);
+ if (sta == NULL)
+ return;
+
+ hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+ sta->flags |= WLAN_STA_AUTH;
+ mlme_authenticate_indication(hapd, sta);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+static void handle_auth(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ u16 auth_alg, auth_transaction, status_code;
+ u16 resp = WLAN_STATUS_SUCCESS;
+ struct sta_info *sta = NULL;
+ int res;
+ u16 fc;
+ const u8 *challenge = NULL;
+ u32 session_timeout, acct_interim_interval;
+ int vlan_id = 0;
+ u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+ size_t resp_ies_len = 0;
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+ printf("handle_auth - too short payload (len=%lu)\n",
+ (unsigned long) len);
+ return;
+ }
+
+ auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+ auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+ status_code = le_to_host16(mgmt->u.auth.status_code);
+ fc = le_to_host16(mgmt->frame_control);
+
+ if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
+ 2 + WLAN_AUTH_CHALLENGE_LEN &&
+ mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE &&
+ mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN)
+ challenge = &mgmt->u.auth.variable[2];
+
+ wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
+ "auth_transaction=%d status_code=%d wep=%d%s",
+ MAC2STR(mgmt->sa), auth_alg, auth_transaction,
+ status_code, !!(fc & WLAN_FC_ISWEP),
+ challenge ? " challenge" : "");
+
+ if (hapd->tkip_countermeasures) {
+ resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+ goto fail;
+ }
+
+ if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
+ auth_alg == WLAN_AUTH_OPEN) ||
+#ifdef CONFIG_IEEE80211R
+ (hapd->conf->wpa &&
+ (hapd->conf->wpa_key_mgmt &
+ (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) &&
+ auth_alg == WLAN_AUTH_FT) ||
+#endif /* CONFIG_IEEE80211R */
+ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
+ auth_alg == WLAN_AUTH_SHARED_KEY))) {
+ printf("Unsupported authentication algorithm (%d)\n",
+ auth_alg);
+ resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ goto fail;
+ }
+
+ if (!(auth_transaction == 1 ||
+ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
+ printf("Unknown authentication transaction number (%d)\n",
+ auth_transaction);
+ resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ goto fail;
+ }
+
+ if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+ printf("Station " MACSTR " not allowed to authenticate.\n",
+ MAC2STR(mgmt->sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
+ &session_timeout,
+ &acct_interim_interval, &vlan_id);
+ if (res == HOSTAPD_ACL_REJECT) {
+ printf("Station " MACSTR " not allowed to authenticate.\n",
+ MAC2STR(mgmt->sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ if (res == HOSTAPD_ACL_PENDING) {
+ wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
+ " waiting for an external authentication",
+ MAC2STR(mgmt->sa));
+ /* Authentication code will re-send the authentication frame
+ * after it has received (and cached) information from the
+ * external source. */
+ return;
+ }
+
+ sta = ap_sta_add(hapd, mgmt->sa);
+ if (!sta) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (vlan_id > 0) {
+ if (hostapd_get_vlan_id_ifname(hapd->conf->vlan,
+ vlan_id) == NULL) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO, "Invalid VLAN ID "
+ "%d received from RADIUS server",
+ vlan_id);
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ sta->vlan_id = vlan_id;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+ }
+
+ sta->flags &= ~WLAN_STA_PREAUTH;
+ ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+
+ if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
+ sta->acct_interim_interval = acct_interim_interval;
+ if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+ ap_sta_session_timeout(hapd, sta, session_timeout);
+ else
+ ap_sta_no_session_timeout(hapd, sta);
+
+ switch (auth_alg) {
+ case WLAN_AUTH_OPEN:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "authentication OK (open system)");
+#ifdef IEEE80211_REQUIRE_AUTH_ACK
+ /* Station will be marked authenticated if it ACKs the
+ * authentication reply. */
+#else
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sta->auth_alg = WLAN_AUTH_OPEN;
+ mlme_authenticate_indication(hapd, sta);
+#endif
+ break;
+ case WLAN_AUTH_SHARED_KEY:
+ resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
+ fc & WLAN_FC_ISWEP);
+ sta->auth_alg = WLAN_AUTH_SHARED_KEY;
+ mlme_authenticate_indication(hapd, sta);
+ if (sta->challenge && auth_transaction == 1) {
+ resp_ies[0] = WLAN_EID_CHALLENGE;
+ resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN;
+ os_memcpy(resp_ies + 2, sta->challenge,
+ WLAN_AUTH_CHALLENGE_LEN);
+ resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN;
+ }
+ break;
+#ifdef CONFIG_IEEE80211R
+ case WLAN_AUTH_FT:
+ sta->auth_alg = WLAN_AUTH_FT;
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
+ "state machine");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid,
+ auth_transaction, mgmt->u.auth.variable,
+ len - IEEE80211_HDRLEN -
+ sizeof(mgmt->u.auth),
+ handle_auth_ft_finish, hapd);
+ /* handle_auth_ft_finish() callback will complete auth. */
+ return;
+#endif /* CONFIG_IEEE80211R */
+ }
+
+ fail:
+ send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
+ auth_transaction + 1, resp, resp_ies, resp_ies_len);
+}
+
+
+static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int i, j = 32, aid;
+
+ /* get a unique AID */
+ if (sta->aid > 0) {
+ wpa_printf(MSG_DEBUG, " old AID %d", sta->aid);
+ return 0;
+ }
+
+ for (i = 0; i < AID_WORDS; i++) {
+ if (hapd->sta_aid[i] == (u32) -1)
+ continue;
+ for (j = 0; j < 32; j++) {
+ if (!(hapd->sta_aid[i] & BIT(j)))
+ break;
+ }
+ if (j < 32)
+ break;
+ }
+ if (j == 32)
+ return -1;
+ aid = i * 32 + j + 1;
+ if (aid > 2007)
+ return -1;
+
+ sta->aid = aid;
+ hapd->sta_aid[i] |= BIT(j);
+ wpa_printf(MSG_DEBUG, " new AID %d", sta->aid);
+ return 0;
+}
+
+
+static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ssid_ie, size_t ssid_ie_len)
+{
+ if (ssid_ie == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
+ os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
+ char ssid_txt[33];
+ ieee802_11_print_ssid(ssid_txt, ssid_ie, ssid_ie_len);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station tried to associate with unknown SSID "
+ "'%s'", ssid_txt);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *wmm_ie, size_t wmm_ie_len)
+{
+ sta->flags &= ~WLAN_STA_WMM;
+ if (wmm_ie && hapd->conf->wmm_enabled) {
+ if (hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len))
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "invalid WMM element in association "
+ "request");
+ else
+ sta->flags |= WLAN_STA_WMM;
+ }
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
+ struct ieee802_11_elems *elems)
+{
+ if (!elems->supp_rates) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "No supported rates element in AssocReq");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (elems->supp_rates_len > sizeof(sta->supported_rates)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Invalid supported rates element length %d",
+ elems->supp_rates_len);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ os_memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
+ os_memcpy(sta->supported_rates, elems->supp_rates,
+ elems->supp_rates_len);
+ sta->supported_rates_len = elems->supp_rates_len;
+
+ if (elems->ext_supp_rates) {
+ if (elems->supp_rates_len + elems->ext_supp_rates_len >
+ sizeof(sta->supported_rates)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Invalid supported rates element length"
+ " %d+%d", elems->supp_rates_len,
+ elems->ext_supp_rates_len);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ os_memcpy(sta->supported_rates + elems->supp_rates_len,
+ elems->ext_supp_rates, elems->ext_supp_rates_len);
+ sta->supported_rates_len += elems->ext_supp_rates_len;
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ies, size_t ies_len, int reassoc)
+{
+ struct ieee802_11_elems elems;
+ u16 resp;
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "Station sent an invalid "
+ "association request");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ resp = copy_supp_rates(hapd, sta, &elems);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+#ifdef CONFIG_IEEE80211N
+ resp = copy_sta_ht_capab(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 */
+
+ if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
+ wpa_ie = elems.rsn_ie;
+ wpa_ie_len = elems.rsn_ie_len;
+ } else if ((hapd->conf->wpa & WPA_PROTO_WPA) &&
+ elems.wpa_ie) {
+ wpa_ie = elems.wpa_ie;
+ wpa_ie_len = elems.wpa_ie_len;
+ } else {
+ wpa_ie = NULL;
+ wpa_ie_len = 0;
+ }
+
+#ifdef CONFIG_WPS
+ sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
+ if (hapd->conf->wps_state && elems.wps_ie) {
+ wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
+ "Request - assume WPS is used");
+ sta->flags |= WLAN_STA_WPS;
+ wpabuf_free(sta->wps_ie);
+ sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+ WPS_IE_VENDOR_TYPE);
+ wpa_ie = NULL;
+ wpa_ie_len = 0;
+ 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);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to initialize WPA "
+ "state machine");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ wpa_ie, wpa_ie_len,
+ elems.mdie, elems.mdie_len);
+ if (res == WPA_INVALID_GROUP)
+ resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+ else if (res == WPA_INVALID_PAIRWISE)
+ resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ else if (res == WPA_INVALID_AKMP)
+ resp = WLAN_STATUS_AKMP_NOT_VALID;
+ else if (res == WPA_ALLOC_FAIL)
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+#ifdef CONFIG_IEEE80211W
+ else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
+ resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+ else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
+ resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+#endif /* CONFIG_IEEE80211W */
+ else if (res == WPA_INVALID_MDIE)
+ resp = WLAN_STATUS_INVALID_MDIE;
+ else if (res != WPA_IE_OK)
+ resp = WLAN_STATUS_INVALID_IE;
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+#ifdef CONFIG_IEEE80211W
+ if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+ sta->sa_query_count > 0)
+ ap_check_sa_query_timeout(hapd, sta);
+ if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+ (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
+ /*
+ * STA has already been associated with MFP and SA
+ * Query timeout has not been reached. Reject the
+ * association attempt temporarily and start SA Query,
+ * if one is not pending.
+ */
+
+ if (sta->sa_query_count == 0)
+ ap_sta_start_sa_query(hapd, sta);
+
+ return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+ }
+
+ if (wpa_auth_uses_mfp(sta->wpa_sm))
+ sta->flags |= WLAN_STA_MFP;
+ else
+ sta->flags &= ~WLAN_STA_MFP;
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211R
+ if (sta->auth_alg == WLAN_AUTH_FT) {
+ if (!reassoc) {
+ wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
+ "to use association (not "
+ "re-association) with FT auth_alg",
+ MAC2STR(sta->addr));
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
+ ies_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211N
+ if ((sta->flags & WLAN_STA_HT) &&
+ wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station tried to use TKIP with HT "
+ "association");
+ return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+ }
+#endif /* CONFIG_IEEE80211N */
+ } else
+ wpa_auth_sta_no_wpa(sta->wpa_sm);
+
+#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);
+
+ } else {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = NULL;
+ }
+
+ p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
+#endif /* CONFIG_P2P */
+
+ 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)
+ 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 */
+
+ p = hostapd_eid_ext_capab(hapd, p);
+
+ if (sta->flags & WLAN_STA_WMM)
+ p = hostapd_eid_wmm(hapd, p);
+
+#ifdef CONFIG_WPS
+ if (sta->flags & WLAN_STA_WPS) {
+ struct wpabuf *wps = wps_build_assoc_resp_ie();
+ if (wps) {
+ os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
+ p += wpabuf_len(wps);
+ wpabuf_free(wps);
+ }
+ }
+#endif /* CONFIG_WPS */
+
+#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)
+ wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
+ strerror(errno));
+}
+
+
+static void handle_assoc(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int reassoc)
+{
+ u16 capab_info, listen_interval;
+ u16 resp = WLAN_STATUS_SUCCESS;
+ const u8 *pos;
+ int left, i;
+ struct sta_info *sta;
+
+ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
+ sizeof(mgmt->u.assoc_req))) {
+ printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)"
+ "\n", reassoc, (unsigned long) len);
+ return;
+ }
+
+ if (reassoc) {
+ capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
+ listen_interval = le_to_host16(
+ mgmt->u.reassoc_req.listen_interval);
+ wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR
+ " capab_info=0x%02x listen_interval=%d current_ap="
+ MACSTR,
+ MAC2STR(mgmt->sa), capab_info, listen_interval,
+ MAC2STR(mgmt->u.reassoc_req.current_ap));
+ left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
+ pos = mgmt->u.reassoc_req.variable;
+ } else {
+ capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
+ listen_interval = le_to_host16(
+ mgmt->u.assoc_req.listen_interval);
+ wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR
+ " capab_info=0x%02x listen_interval=%d",
+ MAC2STR(mgmt->sa), capab_info, listen_interval);
+ left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
+ pos = mgmt->u.assoc_req.variable;
+ }
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+#ifdef CONFIG_IEEE80211R
+ if (sta && sta->auth_alg == WLAN_AUTH_FT &&
+ (sta->flags & WLAN_STA_AUTH) == 0) {
+ wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
+ "prior to authentication since it is using "
+ "over-the-DS FT", MAC2STR(mgmt->sa));
+ } else
+#endif /* CONFIG_IEEE80211R */
+ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "Station tried to "
+ "associate before authentication "
+ "(aid=%d flags=0x%x)",
+ sta ? sta->aid : -1,
+ sta ? sta->flags : 0);
+ send_deauth(hapd, mgmt->sa,
+ WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
+ return;
+ }
+
+ if (hapd->tkip_countermeasures) {
+ resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+ goto fail;
+ }
+
+ if (listen_interval > hapd->conf->max_listen_interval) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Too large Listen Interval (%d)",
+ listen_interval);
+ resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE;
+ goto fail;
+ }
+
+ /* followed by SSID and Supported rates; and HT capabilities if 802.11n
+ * is used */
+ resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto fail;
+
+ if (hostapd_get_aid(hapd, sta) < 0) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "No room for more AIDs");
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
+
+ sta->capability = capab_info;
+ sta->listen_interval = listen_interval;
+
+ if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ sta->flags |= WLAN_STA_NONERP;
+ for (i = 0; i < sta->supported_rates_len; i++) {
+ if ((sta->supported_rates[i] & 0x7f) > 22) {
+ sta->flags &= ~WLAN_STA_NONERP;
+ break;
+ }
+ }
+ if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) {
+ sta->nonerp_set = 1;
+ hapd->iface->num_sta_non_erp++;
+ if (hapd->iface->num_sta_non_erp == 1)
+ ieee802_11_set_beacons(hapd->iface);
+ }
+
+ if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) &&
+ !sta->no_short_slot_time_set) {
+ sta->no_short_slot_time_set = 1;
+ hapd->iface->num_sta_no_short_slot_time++;
+ if (hapd->iface->current_mode->mode ==
+ HOSTAPD_MODE_IEEE80211G &&
+ hapd->iface->num_sta_no_short_slot_time == 1)
+ ieee802_11_set_beacons(hapd->iface);
+ }
+
+ if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ sta->flags |= WLAN_STA_SHORT_PREAMBLE;
+ else
+ sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
+
+ if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
+ !sta->no_short_preamble_set) {
+ sta->no_short_preamble_set = 1;
+ hapd->iface->num_sta_no_short_preamble++;
+ if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ && hapd->iface->num_sta_no_short_preamble == 1)
+ ieee802_11_set_beacons(hapd->iface);
+ }
+
+#ifdef CONFIG_IEEE80211N
+ update_ht_state(hapd, sta);
+#endif /* CONFIG_IEEE80211N */
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "association OK (aid %d)", sta->aid);
+ /* Station will be marked associated, after it acknowledges AssocResp
+ */
+ sta->flags |= WLAN_STA_ASSOC_REQ_OK;
+
+#ifdef CONFIG_IEEE80211W
+ if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) {
+ wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out "
+ "SA Query procedure", reassoc ? "re" : "");
+ /* TODO: Send a protected Disassociate frame to the STA using
+ * the old key and Reason Code "Previous Authentication no
+ * longer valid". Make sure this is only sent protected since
+ * unprotected frame would be received by the STA that is now
+ * trying to associate.
+ */
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ if (reassoc) {
+ os_memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap,
+ ETH_ALEN);
+ }
+
+ if (sta->last_assoc_req)
+ os_free(sta->last_assoc_req);
+ sta->last_assoc_req = os_malloc(len);
+ if (sta->last_assoc_req)
+ os_memcpy(sta->last_assoc_req, mgmt, len);
+
+ /* Make sure that the previously registered inactivity timer will not
+ * remove the STA immediately. */
+ sta->timeout_next = STA_NULLFUNC;
+
+ fail:
+ send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
+}
+
+
+static void handle_disassoc(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct sta_info *sta;
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
+ printf("handle_disassoc - too short payload (len=%lu)\n",
+ (unsigned long) len);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d",
+ MAC2STR(mgmt->sa),
+ le_to_host16(mgmt->u.disassoc.reason_code));
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (sta == NULL) {
+ printf("Station " MACSTR " trying to disassociate, but it "
+ "is not associated.\n", MAC2STR(mgmt->sa));
+ return;
+ }
+
+ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
+ MAC2STR(sta->addr));
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "disassociated");
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ /* Stop Accounting and IEEE 802.1X sessions, but leave the STA
+ * authenticated. */
+ accounting_sta_stop(hapd, sta);
+ ieee802_1x_free_station(sta);
+ 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_DEBUG, "handle_deauth - too short payload "
+ "(len=%lu)", (unsigned long) len);
+ return;
+ }
+
+ wpa_msg(hapd, 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_DEBUG, "Station " MACSTR " trying to "
+ "deauthenticate, but it is not authenticated",
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_ASSOC_REQ_OK);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
+ MAC2STR(sta->addr));
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "deauthenticated");
+ mlme_deauthenticate_indication(
+ hapd, sta, le_to_host16(mgmt->u.deauth.reason_code));
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ ap_free_sta(hapd, sta);
+}
+
+
+static void handle_beacon(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ struct hostapd_frame_info *fi)
+{
+ struct ieee802_11_elems elems;
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
+ printf("handle_beacon - too short payload (len=%lu)\n",
+ (unsigned long) len);
+ return;
+ }
+
+ (void) ieee802_11_parse_elems(mgmt->u.beacon.variable,
+ len - (IEEE80211_HDRLEN +
+ sizeof(mgmt->u.beacon)), &elems,
+ 0);
+
+ ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
+}
+
+
+#ifdef CONFIG_IEEE80211W
+
+/* MLME-SAQuery.request */
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *trans_id)
+{
+ struct ieee80211_mgmt mgmt;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
+ MACSTR, MAC2STR(addr));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ os_memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt.da, addr, ETH_ALEN);
+ os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+ mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
+ mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+ os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+ if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt) < 0)
+ perror("ieee802_11_send_sa_query_req: send");
+}
+
+
+static void hostapd_sa_query_request(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt)
+{
+ struct sta_info *sta;
+ struct ieee80211_mgmt resp;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
+ MACSTR, MAC2STR(mgmt->sa));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ mgmt->u.action.u.sa_query_resp.trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
+ "from unassociated STA " MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
+ MACSTR, MAC2STR(mgmt->sa));
+
+ os_memset(&resp, 0, sizeof(resp));
+ resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(resp.da, mgmt->sa, ETH_ALEN);
+ os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
+ resp.u.action.category = WLAN_ACTION_SA_QUERY;
+ resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+ os_memcpy(resp.u.action.u.sa_query_req.trans_id,
+ mgmt->u.action.u.sa_query_req.trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+ if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp) < 0)
+ perror("hostapd_sa_query_request: send");
+}
+
+
+static void hostapd_sa_query_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct sta_info *sta;
+ const u8 *end;
+ int i;
+
+ end = mgmt->u.action.u.sa_query_resp.trans_id +
+ WLAN_SA_QUERY_TR_ID_LEN;
+ if (((u8 *) mgmt) + len < end) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action "
+ "frame (len=%lu)", (unsigned long) len);
+ return;
+ }
+
+ if (mgmt->u.action.u.sa_query_resp.action == WLAN_SA_QUERY_REQUEST) {
+ hostapd_sa_query_request(hapd, mgmt);
+ return;
+ }
+
+ if (mgmt->u.action.u.sa_query_resp.action != WLAN_SA_QUERY_RESPONSE) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
+ "Action %d", mgmt->u.action.u.sa_query_resp.action);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
+ MACSTR, MAC2STR(mgmt->sa));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ mgmt->u.action.u.sa_query_resp.trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+
+ /* MLME-SAQuery.confirm */
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (sta == NULL || sta->sa_query_trans_id == NULL) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
+ "pending SA Query request found");
+ return;
+ }
+
+ for (i = 0; i < sta->sa_query_count; i++) {
+ if (os_memcmp(sta->sa_query_trans_id +
+ i * WLAN_SA_QUERY_TR_ID_LEN,
+ mgmt->u.action.u.sa_query_resp.trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN) == 0)
+ break;
+ }
+
+ if (i >= sta->sa_query_count) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
+ "transaction identifier found");
+ return;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Reply to pending SA Query received");
+ ap_sta_stop_sa_query(hapd, sta);
+}
+
+
+static int robust_action_frame(u8 category)
+{
+ return category != WLAN_ACTION_PUBLIC &&
+ category != WLAN_ACTION_HT;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+static void handle_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct sta_info *sta;
+
+ if (len < IEEE80211_HDRLEN + 1) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "handle_action - too short payload (len=%lu)",
+ (unsigned long) len);
+ return;
+ }
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+#ifdef CONFIG_IEEE80211W
+ if (sta && (sta->flags & WLAN_STA_MFP) &&
+ !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) &&
+ robust_action_frame(mgmt->u.action.category))) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Dropped unprotected Robust Action frame from "
+ "an MFP STA");
+ return;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ switch (mgmt->u.action.category) {
+#ifdef CONFIG_IEEE80211R
+ case WLAN_ACTION_FT:
+ {
+ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored FT Action "
+ "frame from unassociated STA " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
+ len - IEEE80211_HDRLEN))
+ break;
+
+ return;
+ }
+#endif /* CONFIG_IEEE80211R */
+ case WLAN_ACTION_WMM:
+ hostapd_wmm_action(hapd, mgmt, len);
+ return;
+#ifdef CONFIG_IEEE80211W
+ case WLAN_ACTION_SA_QUERY:
+ hostapd_sa_query_action(hapd, mgmt, len);
+ return;
+#endif /* CONFIG_IEEE80211W */
+ case WLAN_ACTION_PUBLIC:
+ if (hapd->public_action_cb) {
+ hapd->public_action_cb(hapd->public_action_cb_ctx,
+ (u8 *) mgmt, len,
+ hapd->iface->freq);
+ return;
+ }
+ break;
+ 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;
+
+ hostapd_drv_send_mlme(hapd, resp, len);
+ os_free(resp);
+ }
+}
+
+
+/**
+ * ieee802_11_mgmt - process incoming IEEE 802.11 management frames
+ * @hapd: hostapd BSS data structure (the BSS to which the management frame was
+ * sent to)
+ * @buf: management frame data (starting from IEEE 802.11 header)
+ * @len: length of frame data in octets
+ * @fi: meta data about received frame (signal level, etc.)
+ *
+ * Process all incoming IEEE 802.11 management frames. This will be called for
+ * each frame received from the kernel driver through wlan#ap interface. In
+ * addition, it can be called to re-inserted pending frames (e.g., when using
+ * external RADIUS server as an MAC ACL).
+ */
+void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ struct hostapd_frame_info *fi)
+{
+ struct ieee80211_mgmt *mgmt;
+ int broadcast;
+ u16 fc, stype;
+
+ if (len < 24)
+ return;
+
+ mgmt = (struct ieee80211_mgmt *) buf;
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ if (stype == WLAN_FC_STYPE_BEACON) {
+ handle_beacon(hapd, mgmt, len, fi);
+ return;
+ }
+
+ broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff &&
+ mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff &&
+ mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff;
+
+ if (!broadcast &&
+ os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
+ printf("MGMT: BSSID=" MACSTR " not our address\n",
+ MAC2STR(mgmt->bssid));
+ return;
+ }
+
+
+ if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+ handle_probe_req(hapd, mgmt, len);
+ return;
+ }
+
+ if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "MGMT: DA=" MACSTR " not our address",
+ MAC2STR(mgmt->da));
+ return;
+ }
+
+ switch (stype) {
+ case WLAN_FC_STYPE_AUTH:
+ wpa_printf(MSG_DEBUG, "mgmt::auth");
+ handle_auth(hapd, mgmt, len);
+ break;
+ case WLAN_FC_STYPE_ASSOC_REQ:
+ wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
+ handle_assoc(hapd, mgmt, len, 0);
+ break;
+ case WLAN_FC_STYPE_REASSOC_REQ:
+ wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
+ handle_assoc(hapd, mgmt, len, 1);
+ break;
+ case WLAN_FC_STYPE_DISASSOC:
+ wpa_printf(MSG_DEBUG, "mgmt::disassoc");
+ handle_disassoc(hapd, mgmt, len);
+ break;
+ case WLAN_FC_STYPE_DEAUTH:
+ wpa_msg(hapd, MSG_DEBUG, "mgmt::deauth");
+ handle_deauth(hapd, mgmt, len);
+ break;
+ case WLAN_FC_STYPE_ACTION:
+ wpa_printf(MSG_DEBUG, "mgmt::action");
+ handle_action(hapd, mgmt, len);
+ break;
+ default:
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "unknown mgmt frame subtype %d", stype);
+ break;
+ }
+}
+
+
+static void handle_auth_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ u16 auth_alg, auth_transaction, status_code;
+ struct sta_info *sta;
+
+ if (!ok) {
+ hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_NOTICE,
+ "did not acknowledge authentication response");
+ return;
+ }
+
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+ printf("handle_auth_cb - too short payload (len=%lu)\n",
+ (unsigned long) len);
+ return;
+ }
+
+ auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+ auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+ status_code = le_to_host16(mgmt->u.auth.status_code);
+
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ printf("handle_auth_cb: STA " MACSTR " not found\n",
+ MAC2STR(mgmt->da));
+ return;
+ }
+
+ if (status_code == WLAN_STATUS_SUCCESS &&
+ ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
+ (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "authenticated");
+ sta->flags |= WLAN_STA_AUTH;
+ }
+}
+
+
+static void handle_assoc_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int reassoc, int ok)
+{
+ u16 status;
+ struct sta_info *sta;
+ int new_assoc = 1;
+ struct ieee80211_ht_capabilities ht_cap;
+
+ if (!ok) {
+ hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "did not acknowledge association response");
+ return;
+ }
+
+ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
+ sizeof(mgmt->u.assoc_resp))) {
+ printf("handle_assoc_cb(reassoc=%d) - too short payload "
+ "(len=%lu)\n", reassoc, (unsigned long) len);
+ return;
+ }
+
+ if (reassoc)
+ status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+ else
+ status = le_to_host16(mgmt->u.assoc_resp.status_code);
+
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ printf("handle_assoc_cb: STA " MACSTR " not found\n",
+ MAC2STR(mgmt->da));
+ return;
+ }
+
+ if (status != WLAN_STATUS_SUCCESS)
+ goto fail;
+
+ /* Stop previous accounting session, if one is started, and allocate
+ * new session id for the new session. */
+ accounting_sta_stop(hapd, sta);
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "associated (aid %d)",
+ sta->aid);
+
+ if (sta->flags & WLAN_STA_ASSOC)
+ new_assoc = 0;
+ sta->flags |= WLAN_STA_ASSOC;
+ if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+ sta->auth_alg == WLAN_AUTH_FT) {
+ /*
+ * Open, static WEP, or FT protocol; no separate authorization
+ * step.
+ */
+ ap_sta_set_authorized(hapd, sta, 1);
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+ }
+
+ if (reassoc)
+ mlme_reassociate_indication(hapd, sta);
+ else
+ mlme_associate_indication(hapd, sta);
+
+#ifdef CONFIG_IEEE80211W
+ sta->sa_query_timed_out = 0;
+#endif /* CONFIG_IEEE80211W */
+
+ /*
+ * Remove the STA entry in order to make sure the STA PS state gets
+ * cleared and configuration gets updated in case of reassociation back
+ * to the same AP.
+ */
+ 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 */
+
+ 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)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_NOTICE,
+ "Could not add STA to kernel driver");
+ }
+
+ if (sta->flags & WLAN_STA_WDS)
+ hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
+
+ 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;
+ }
+}
+
+
+/**
+ * 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:
+ /* ignore */
+ break;
+ case WLAN_FC_STYPE_ACTION:
+ wpa_printf(MSG_DEBUG, "mgmt::action cb");
+ break;
+ default:
+ printf("unknown mgmt cb frame subtype %d\n", stype);
+ break;
+ }
+}
+
+
+int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+ /* TODO */
+ return 0;
+}
+
+
+int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ /* TODO */
+ return 0;
+}
+
+
+void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *buf, size_t len, int ack)
+{
+ struct sta_info *sta;
+ struct hostapd_iface *iface = hapd->iface;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL && iface->num_bss > 1) {
+ size_t j;
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ break;
+ }
+ }
+ if (sta == NULL)
+ return;
+ if (sta->flags & WLAN_STA_PENDING_POLL) {
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
+ "activity poll", MAC2STR(sta->addr),
+ ack ? "ACKed" : "did not ACK");
+ if (ack)
+ sta->flags &= ~WLAN_STA_PENDING_POLL;
+ }
+
+ ieee802_1x_tx_status(hapd, sta, buf, len, ack);
+}
+
+
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ int wds)
+{
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, src);
+ if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+ if (wds && !(sta->flags & WLAN_STA_WDS)) {
+ wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
+ "STA " MACSTR " (aid %u)",
+ MAC2STR(sta->addr), sta->aid);
+ sta->flags |= WLAN_STA_WDS;
+ hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
+ }
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
+ MACSTR, MAC2STR(src));
+ if (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/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
new file mode 100644
index 0000000..157198c
--- /dev/null
+++ b/src/ap/ieee802_11.h
@@ -0,0 +1,68 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef IEEE802_11_H
+#define IEEE802_11_H
+
+struct hostapd_iface;
+struct hostapd_data;
+struct sta_info;
+struct hostapd_frame_info;
+struct ieee80211_ht_capabilities;
+
+void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ struct hostapd_frame_info *fi);
+void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ u16 stype, int ok);
+void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len);
+#ifdef NEED_AP_MLME
+int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen);
+#else /* NEED_AP_MLME */
+static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf,
+ size_t buflen)
+{
+ return 0;
+}
+
+static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ return 0;
+}
+#endif /* NEED_AP_MLME */
+u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
+ int probe);
+u8 * hostapd_eid_ext_capab(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);
+int hostapd_ht_operation_update(struct hostapd_iface *iface);
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *trans_id);
+void hostapd_get_ht_capab(struct hostapd_data *hapd,
+ struct ieee80211_ht_capabilities *ht_cap,
+ struct ieee80211_ht_capabilities *neg_ht_cap);
+u16 copy_sta_ht_capab(struct 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);
+void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *buf, size_t len, int ack);
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ int wds);
+
+#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
new file mode 100644
index 0000000..b933263
--- /dev/null
+++ b/src/ap/ieee802_11_auth.c
@@ -0,0 +1,524 @@
+/*
+ * hostapd / IEEE 802.11 authentication (ACL)
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * Access control list for IEEE 802.11 authentication can uses statically
+ * configured ACL from configuration files or an external RADIUS server.
+ * Results from external RADIUS queries are cached to allow faster
+ * authentication frame processing.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "ieee802_11.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_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(struct hostapd_cached_radius_acl *acl_cache)
+{
+ struct hostapd_cached_radius_acl *prev;
+
+ while (acl_cache) {
+ prev = acl_cache;
+ acl_cache = acl_cache->next;
+ os_free(prev);
+ }
+}
+
+
+static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
+ u32 *session_timeout,
+ u32 *acct_interim_interval, int *vlan_id)
+{
+ struct hostapd_cached_radius_acl *entry;
+ struct os_time now;
+
+ os_get_time(&now);
+ entry = hapd->acl_cache;
+
+ while (entry) {
+ if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
+ 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;
+ return entry->accepted;
+ }
+
+ entry = entry->next;
+ }
+
+ return -1;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
+{
+ if (query == NULL)
+ return;
+ os_free(query->auth_msg);
+ os_free(query);
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
+ struct hostapd_acl_query_data *query)
+{
+ struct radius_msg *msg;
+ char buf[128];
+
+ query->radius_id = radius_client_get_id(hapd->radius);
+ msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
+ if (msg == NULL)
+ return -1;
+
+ radius_msg_make_authenticator(msg, addr, ETH_ALEN);
+
+ os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
+ os_strlen(buf))) {
+ wpa_printf(MSG_DEBUG, "Could not add User-Name");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr_user_password(
+ msg, (u8 *) buf, os_strlen(buf),
+ hapd->conf->radius->auth_server->shared_secret,
+ hapd->conf->radius->auth_server->shared_secret_len)) {
+ wpa_printf(MSG_DEBUG, "Could not add User-Password");
+ goto fail;
+ }
+
+ if (hapd->conf->own_ip_addr.af == AF_INET &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
+ wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address");
+ goto fail;
+ }
+
+#ifdef CONFIG_IPV6
+ if (hapd->conf->own_ip_addr.af == AF_INET6 &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
+ wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address");
+ goto fail;
+ }
+#endif /* CONFIG_IPV6 */
+
+ if (hapd->conf->nas_identifier &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+ (u8 *) hapd->conf->nas_identifier,
+ os_strlen(hapd->conf->nas_identifier))) {
+ wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier");
+ goto fail;
+ }
+
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+ MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id");
+ goto fail;
+ }
+
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(addr));
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
+ RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+ wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type");
+ goto fail;
+ }
+
+ os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
+ goto fail;
+ }
+
+ radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr);
+ return 0;
+
+ fail:
+ radius_msg_free(msg);
+ return -1;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+/**
+ * hostapd_allowed_address - Check whether a specified STA can be authenticated
+ * @hapd: hostapd BSS data
+ * @addr: MAC address of the STA
+ * @msg: Authentication message
+ * @len: Length of msg in octets
+ * @session_timeout: Buffer for returning session timeout (from RADIUS)
+ * @acct_interim_interval: Buffer for returning account interval (from RADIUS)
+ * @vlan_id: Buffer for returning VLAN ID
+ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ */
+int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *msg, size_t len, u32 *session_timeout,
+ u32 *acct_interim_interval, int *vlan_id)
+{
+ if (session_timeout)
+ *session_timeout = 0;
+ if (acct_interim_interval)
+ *acct_interim_interval = 0;
+ if (vlan_id)
+ *vlan_id = 0;
+
+ if (hostapd_maclist_found(hapd->conf->accept_mac,
+ hapd->conf->num_accept_mac, addr, vlan_id))
+ return HOSTAPD_ACL_ACCEPT;
+
+ if (hostapd_maclist_found(hapd->conf->deny_mac,
+ hapd->conf->num_deny_mac, addr, vlan_id))
+ return HOSTAPD_ACL_REJECT;
+
+ if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
+ return HOSTAPD_ACL_ACCEPT;
+ if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
+ return HOSTAPD_ACL_REJECT;
+
+ if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
+#ifdef CONFIG_NO_RADIUS
+ return HOSTAPD_ACL_REJECT;
+#else /* CONFIG_NO_RADIUS */
+ struct hostapd_acl_query_data *query;
+
+ /* Check whether ACL cache has an entry for this station */
+ int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
+ acct_interim_interval,
+ vlan_id);
+ if (res == HOSTAPD_ACL_ACCEPT ||
+ res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+ return res;
+ if (res == HOSTAPD_ACL_REJECT)
+ return HOSTAPD_ACL_REJECT;
+
+ query = hapd->acl_queries;
+ while (query) {
+ if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
+ /* pending query in RADIUS retransmit queue;
+ * do not generate a new one */
+ return HOSTAPD_ACL_PENDING;
+ }
+ query = query->next;
+ }
+
+ if (!hapd->conf->radius->auth_server)
+ return HOSTAPD_ACL_REJECT;
+
+ /* No entry in the cache - query external RADIUS server */
+ query = os_zalloc(sizeof(*query));
+ if (query == NULL) {
+ wpa_printf(MSG_ERROR, "malloc for query data failed");
+ return HOSTAPD_ACL_REJECT;
+ }
+ time(&query->timestamp);
+ os_memcpy(query->addr, addr, ETH_ALEN);
+ if (hostapd_radius_acl_query(hapd, addr, query)) {
+ wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
+ "for ACL query.");
+ hostapd_acl_query_free(query);
+ return HOSTAPD_ACL_REJECT;
+ }
+
+ query->auth_msg = os_malloc(len);
+ if (query->auth_msg == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to allocate memory for "
+ "auth frame.");
+ hostapd_acl_query_free(query);
+ return HOSTAPD_ACL_REJECT;
+ }
+ os_memcpy(query->auth_msg, msg, len);
+ query->auth_msg_len = len;
+ query->next = hapd->acl_queries;
+ hapd->acl_queries = query;
+
+ /* Queued data will be processed in hostapd_acl_recv_radius()
+ * when RADIUS server replies to the sent Access-Request. */
+ return HOSTAPD_ACL_PENDING;
+#endif /* CONFIG_NO_RADIUS */
+ }
+
+ return HOSTAPD_ACL_REJECT;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_acl_expire_cache(struct hostapd_data *hapd, 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;
+ os_free(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);
+}
+
+
+/**
+ * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
+ * was processed here) or RADIUS_RX_UNKNOWN if not.
+ */
+static RadiusRxResult
+hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
+ const u8 *shared_secret, size_t shared_secret_len,
+ void *data)
+{
+ struct hostapd_data *hapd = data;
+ struct hostapd_acl_query_data *query, *prev;
+ struct hostapd_cached_radius_acl *cache;
+ struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+ query = hapd->acl_queries;
+ prev = NULL;
+ while (query) {
+ if (query->radius_id == hdr->identifier)
+ break;
+ prev = query;
+ query = query->next;
+ }
+ if (query == NULL)
+ return RADIUS_RX_UNKNOWN;
+
+ wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
+ "message (id=%d)", query->radius_id);
+
+ if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
+ wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
+ "correct authenticator - dropped\n");
+ return RADIUS_RX_INVALID_AUTHENTICATOR;
+ }
+
+ if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+ hdr->code != RADIUS_CODE_ACCESS_REJECT) {
+ wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
+ "query", hdr->code);
+ return RADIUS_RX_UNKNOWN;
+ }
+
+ /* Insert Accept/Reject info into ACL cache */
+ cache = os_zalloc(sizeof(*cache));
+ if (cache == NULL) {
+ wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
+ goto done;
+ }
+ time(&cache->timestamp);
+ os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
+ if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
+ if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
+ &cache->session_timeout) == 0)
+ cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
+ else
+ cache->accepted = HOSTAPD_ACL_ACCEPT;
+
+ if (radius_msg_get_attr_int32(
+ msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
+ &cache->acct_interim_interval) == 0 &&
+ cache->acct_interim_interval < 60) {
+ wpa_printf(MSG_DEBUG, "Ignored too small "
+ "Acct-Interim-Interval %d for STA " MACSTR,
+ cache->acct_interim_interval,
+ MAC2STR(query->addr));
+ cache->acct_interim_interval = 0;
+ }
+
+ cache->vlan_id = radius_msg_get_vlanid(msg);
+ } else
+ cache->accepted = HOSTAPD_ACL_REJECT;
+ cache->next = hapd->acl_cache;
+ hapd->acl_cache = cache;
+
+#ifdef CONFIG_DRIVER_RADIUS_ACL
+ 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);
+ }
+}
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
new file mode 100644
index 0000000..b2971e5
--- /dev/null
+++ b/src/ap/ieee802_11_auth.h
@@ -0,0 +1,31 @@
+/*
+ * hostapd / IEEE 802.11 authentication (ACL)
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef IEEE802_11_AUTH_H
+#define IEEE802_11_AUTH_H
+
+enum {
+ HOSTAPD_ACL_REJECT = 0,
+ HOSTAPD_ACL_ACCEPT = 1,
+ HOSTAPD_ACL_PENDING = 2,
+ HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
+};
+
+int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *msg, size_t len, u32 *session_timeout,
+ u32 *acct_interim_interval, int *vlan_id);
+int hostapd_acl_init(struct hostapd_data *hapd);
+void hostapd_acl_deinit(struct hostapd_data *hapd);
+
+#endif /* IEEE802_11_AUTH_H */
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
new file mode 100644
index 0000000..3dce5cb
--- /dev/null
+++ b/src/ap/ieee802_11_ht.c
@@ -0,0 +1,267 @@
+/*
+ * hostapd / IEEE 802.11n HT
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2008, Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+
+
+u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_ht_capabilities *cap;
+ u8 *pos = eid;
+
+ if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
+ 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);
+
+ 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->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
+ && iface->num_sta_ht_20mhz)
+ new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED;
+ else if (iface->olbc_ht)
+ new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS;
+ else
+ new_op_mode = OP_MODE_PURE;
+
+ cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+ if (cur_op_mode != new_op_mode) {
+ iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+ iface->ht_op_mode |= new_op_mode;
+ op_mode_changes++;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d",
+ __func__, iface->ht_op_mode, op_mode_changes);
+
+ return op_mode_changes;
+}
+
+
+u16 copy_sta_ht_capab(struct 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);
+ cap &= hapd->iconf->ht_capab;
+ cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_DISABLED);
+
+ /*
+ * STBC needs to be handled specially
+ * if we don't support RX STBC, mask out TX STBC in the STA's HT caps
+ * if we don't support TX STBC, mask out RX STBC in the STA's HT caps
+ */
+ if (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK))
+ cap &= ~HT_CAP_INFO_TX_STBC;
+ if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC))
+ cap &= ~HT_CAP_INFO_RX_STBC_MASK;
+
+ neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
new file mode 100644
index 0000000..ac0c127
--- /dev/null
+++ b/src/ap/ieee802_1x.c
@@ -0,0 +1,2085 @@
+/*
+ * hostapd / IEEE 802.1X-2004 Authenticator
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/md5.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "eap_server/eap.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "hostapd.h"
+#include "accounting.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+#include "pmksa_cache_auth.h"
+#include "ap_config.h"
+#include "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, 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) {
+ if (!ap_sta_is_authorized(sta))
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+ 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 {
+ if (ap_sta_is_authorized(sta) && (sta->flags & WLAN_STA_ASSOC))
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ AP_STA_DISCONNECTED MACSTR,
+ MAC2STR(sta->addr));
+ ap_sta_set_authorized(hapd, sta, 0);
+ res = hostapd_set_authorized(hapd, sta, 0);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "unauthorizing port");
+ }
+
+ if (res && errno != ENOENT) {
+ printf("Could not set station " MACSTR " flags for kernel "
+ "driver (errno=%d).\n", MAC2STR(sta->addr), errno);
+ }
+
+ if (authorized)
+ accounting_sta_start(hapd, sta);
+}
+
+
+static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ int idx, int broadcast,
+ u8 *key_data, size_t key_len)
+{
+ u8 *buf, *ekey;
+ struct ieee802_1x_hdr *hdr;
+ struct ieee802_1x_eapol_key *key;
+ size_t len, ekey_len;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm == NULL)
+ return;
+
+ len = sizeof(*key) + key_len;
+ buf = os_zalloc(sizeof(*hdr) + len);
+ if (buf == NULL)
+ return;
+
+ hdr = (struct ieee802_1x_hdr *) buf;
+ key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+ key->type = EAPOL_KEY_TYPE_RC4;
+ key->key_length = htons(key_len);
+ wpa_get_ntp_timestamp(key->replay_counter);
+
+ if (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);
+}
+
+
+#ifndef CONFIG_NO_VLAN
+static struct hostapd_wep_keys *
+ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname)
+{
+ struct hostapd_wep_keys *key;
+
+ key = os_zalloc(sizeof(*key));
+ if (key == NULL)
+ return NULL;
+
+ key->default_len = hapd->conf->default_wep_key_len;
+
+ if (key->idx >= hapd->conf->broadcast_key_idx_max ||
+ key->idx < hapd->conf->broadcast_key_idx_min)
+ key->idx = hapd->conf->broadcast_key_idx_min;
+ else
+ key->idx++;
+
+ if (!key->key[key->idx])
+ key->key[key->idx] = os_malloc(key->default_len);
+ if (key->key[key->idx] == NULL ||
+ random_get_bytes(key->key[key->idx], key->default_len)) {
+ printf("Could not generate random WEP key (dynamic VLAN).\n");
+ os_free(key->key[key->idx]);
+ key->key[key->idx] = NULL;
+ os_free(key);
+ return NULL;
+ }
+ key->len[key->idx] = key->default_len;
+
+ wpa_printf(MSG_DEBUG, "%s: Default WEP idx %d for dynamic VLAN\n",
+ ifname, key->idx);
+ wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)",
+ key->key[key->idx], key->len[key->idx]);
+
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
+ broadcast_ether_addr, key->idx, 1,
+ NULL, 0, key->key[key->idx],
+ key->len[key->idx]))
+ printf("Could not set dynamic VLAN WEP encryption key.\n");
+
+ hostapd_set_drv_ieee8021x(hapd, ifname, 1);
+
+ return key;
+}
+
+
+static struct hostapd_wep_keys *
+ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid,
+ size_t vlan_id)
+{
+ const char *ifname;
+
+ if (vlan_id == 0)
+ return &ssid->wep;
+
+ if (vlan_id <= ssid->max_dyn_vlan_keys && ssid->dyn_vlan_keys &&
+ ssid->dyn_vlan_keys[vlan_id])
+ return ssid->dyn_vlan_keys[vlan_id];
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Creating new group "
+ "state machine for VLAN ID %lu",
+ (unsigned long) vlan_id);
+
+ ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
+ if (ifname == NULL) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Unknown VLAN ID %lu - "
+ "cannot create group key state machine",
+ (unsigned long) vlan_id);
+ return NULL;
+ }
+
+ if (ssid->dyn_vlan_keys == NULL) {
+ int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]);
+ ssid->dyn_vlan_keys = os_zalloc(size);
+ if (ssid->dyn_vlan_keys == NULL)
+ return NULL;
+ ssid->max_dyn_vlan_keys = vlan_id;
+ }
+
+ if (ssid->max_dyn_vlan_keys < vlan_id) {
+ struct hostapd_wep_keys **na;
+ int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]);
+ na = os_realloc(ssid->dyn_vlan_keys, size);
+ if (na == NULL)
+ return NULL;
+ ssid->dyn_vlan_keys = na;
+ os_memset(&ssid->dyn_vlan_keys[ssid->max_dyn_vlan_keys + 1], 0,
+ (vlan_id - ssid->max_dyn_vlan_keys) *
+ sizeof(ssid->dyn_vlan_keys[0]));
+ ssid->max_dyn_vlan_keys = vlan_id;
+ }
+
+ ssid->dyn_vlan_keys[vlan_id] = ieee802_1x_group_alloc(hapd, ifname);
+
+ return ssid->dyn_vlan_keys[vlan_id];
+}
+#endif /* CONFIG_NO_VLAN */
+
+
+void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct eapol_authenticator *eapol = hapd->eapol_auth;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+#ifndef CONFIG_NO_VLAN
+ struct hostapd_wep_keys *key = NULL;
+ int vlan_id;
+#endif /* CONFIG_NO_VLAN */
+
+ if (sm == NULL || !sm->eap_if->eapKeyData)
+ return;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR,
+ MAC2STR(sta->addr));
+
+#ifndef CONFIG_NO_VLAN
+ vlan_id = sta->vlan_id;
+ if (vlan_id < 0 || vlan_id > MAX_VLAN_ID)
+ vlan_id = 0;
+
+ if (vlan_id) {
+ key = ieee802_1x_get_group(hapd, sta->ssid, vlan_id);
+ if (key && key->key[key->idx])
+ ieee802_1x_tx_key_one(hapd, sta, key->idx, 1,
+ key->key[key->idx],
+ key->len[key->idx]);
+ } else
+#endif /* CONFIG_NO_VLAN */
+ if (eapol->default_wep_key) {
+ ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1,
+ eapol->default_wep_key,
+ hapd->conf->default_wep_key_len);
+ }
+
+ if (hapd->conf->individual_wep_key_len > 0) {
+ u8 *ikey;
+ ikey = os_malloc(hapd->conf->individual_wep_key_len);
+ if (ikey == NULL ||
+ 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_IEEE80211A:
+ return "802.11a";
+ case HOSTAPD_MODE_IEEE80211G:
+ return "802.11g";
+ case HOSTAPD_MODE_IEEE80211B:
+ default:
+ return "802.11b";
+ }
+}
+
+
+int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int i;
+ u8 rate = 0;
+
+ for (i = 0; i < sta->supported_rates_len; i++)
+ if ((sta->supported_rates[i] & 0x7f) > rate)
+ rate = sta->supported_rates[i] & 0x7f;
+
+ return rate;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
+ struct eapol_state_machine *sm,
+ const u8 *eap, size_t len)
+{
+ const u8 *identity;
+ size_t identity_len;
+
+ if (len <= sizeof(struct eap_hdr) ||
+ eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY)
+ return;
+
+ identity = eap_get_identity(sm->eap, &identity_len);
+ if (identity == NULL)
+ return;
+
+ /* Save station identity for future RADIUS packets */
+ os_free(sm->identity);
+ sm->identity = os_malloc(identity_len + 1);
+ if (sm->identity == NULL) {
+ sm->identity_len = 0;
+ return;
+ }
+
+ os_memcpy(sm->identity, identity, identity_len);
+ sm->identity_len = identity_len;
+ sm->identity[identity_len] = '\0';
+ hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity);
+ sm->dot1xAuthEapolRespIdFramesRx++;
+}
+
+
+static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *eap, size_t len)
+{
+ struct radius_msg *msg;
+ char buf[128];
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm == NULL)
+ return;
+
+ ieee802_1x_learn_identity(hapd, sm, eap, len);
+
+ wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS "
+ "packet");
+
+ sm->radius_identifier = radius_client_get_id(hapd->radius);
+ msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
+ sm->radius_identifier);
+ if (msg == NULL) {
+ printf("Could not create net RADIUS packet\n");
+ return;
+ }
+
+ radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
+
+ if (sm->identity &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
+ sm->identity, sm->identity_len)) {
+ printf("Could not add User-Name\n");
+ goto fail;
+ }
+
+ if (hapd->conf->own_ip_addr.af == AF_INET &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
+ printf("Could not add NAS-IP-Address\n");
+ goto fail;
+ }
+
+#ifdef CONFIG_IPV6
+ if (hapd->conf->own_ip_addr.af == AF_INET6 &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
+ printf("Could not add NAS-IPv6-Address\n");
+ goto fail;
+ }
+#endif /* CONFIG_IPV6 */
+
+ if (hapd->conf->nas_identifier &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+ (u8 *) hapd->conf->nas_identifier,
+ os_strlen(hapd->conf->nas_identifier))) {
+ printf("Could not add NAS-Identifier\n");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+ printf("Could not add NAS-Port\n");
+ goto fail;
+ }
+
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+ MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
+ buf[sizeof(buf) - 1] = '\0';
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ printf("Could not add Called-Station-Id\n");
+ goto fail;
+ }
+
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(sta->addr));
+ buf[sizeof(buf) - 1] = '\0';
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ printf("Could not add Calling-Station-Id\n");
+ goto fail;
+ }
+
+ /* TODO: should probably check MTU from driver config; 2304 is max for
+ * IEEE 802.11, but use 1400 to avoid problems with too large packets
+ */
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
+ printf("Could not add Framed-MTU\n");
+ goto fail;
+ }
+
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
+ RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+ printf("Could not add NAS-Port-Type\n");
+ goto fail;
+ }
+
+ if (sta->flags & WLAN_STA_PREAUTH) {
+ os_strlcpy(buf, "IEEE 802.11i Pre-Authentication",
+ sizeof(buf));
+ } else {
+ os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
+ radius_sta_rate(hapd, sta) / 2,
+ (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
+ radius_mode_txt(hapd));
+ buf[sizeof(buf) - 1] = '\0';
+ }
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+ (u8 *) buf, os_strlen(buf))) {
+ printf("Could not add Connect-Info\n");
+ goto fail;
+ }
+
+ if (eap && !radius_msg_add_eap(msg, eap, len)) {
+ printf("Could not add EAP-Message\n");
+ goto fail;
+ }
+
+ /* State attribute must be copied if and only if this packet is
+ * Access-Request reply to the previous Access-Challenge */
+ if (sm->last_recv_radius &&
+ radius_msg_get_hdr(sm->last_recv_radius)->code ==
+ RADIUS_CODE_ACCESS_CHALLENGE) {
+ int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
+ RADIUS_ATTR_STATE);
+ if (res < 0) {
+ printf("Could not copy State attribute from previous "
+ "Access-Challenge\n");
+ goto fail;
+ }
+ if (res > 0) {
+ wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute");
+ }
+ }
+
+ 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) {
+ printf("handle_eap_response: too short response data\n");
+ return;
+ }
+
+ sm->eap_type_supp = type = data[0];
+
+ hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
+ "id=%d len=%d) from STA: EAP Response-%s (%d)",
+ eap->code, eap->identifier, be_to_host16(eap->length),
+ eap_server_get_name(0, type), type);
+
+ sm->dot1xAuthEapolRespFramesRx++;
+
+ wpabuf_free(sm->eap_if->eapRespData);
+ sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
+ sm->eapolEap = TRUE;
+}
+
+
+/* Process incoming EAP packet from Supplicant */
+static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *buf, size_t len)
+{
+ struct eap_hdr *eap;
+ u16 eap_len;
+
+ if (len < sizeof(*eap)) {
+ printf(" too short EAP packet\n");
+ return;
+ }
+
+ eap = (struct eap_hdr *) buf;
+
+ eap_len = be_to_host16(eap->length);
+ wpa_printf(MSG_DEBUG, "EAP: code=%d identifier=%d length=%d",
+ eap->code, eap->identifier, eap_len);
+ if (eap_len < sizeof(*eap)) {
+ wpa_printf(MSG_DEBUG, " Invalid EAP length");
+ return;
+ } else if (eap_len > len) {
+ wpa_printf(MSG_DEBUG, " Too short frame to contain this EAP "
+ "packet");
+ return;
+ } else if (eap_len < len) {
+ wpa_printf(MSG_DEBUG, " Ignoring %lu extra bytes after EAP "
+ "packet", (unsigned long) len - eap_len);
+ }
+
+ switch (eap->code) {
+ case EAP_CODE_REQUEST:
+ wpa_printf(MSG_DEBUG, " (request)");
+ return;
+ case EAP_CODE_RESPONSE:
+ wpa_printf(MSG_DEBUG, " (response)");
+ handle_eap_response(hapd, sta, eap, eap_len);
+ break;
+ case EAP_CODE_SUCCESS:
+ wpa_printf(MSG_DEBUG, " (success)");
+ return;
+ case EAP_CODE_FAILURE:
+ wpa_printf(MSG_DEBUG, " (failure)");
+ return;
+ default:
+ wpa_printf(MSG_DEBUG, " (unknown code)");
+ return;
+ }
+}
+
+
+static struct eapol_state_machine *
+ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int flags = 0;
+ if (sta->flags & WLAN_STA_PREAUTH)
+ flags |= EAPOL_SM_PREAUTH;
+ if (sta->wpa_sm) {
+ flags |= EAPOL_SM_USES_WPA;
+ if (wpa_auth_sta_get_pmksa(sta->wpa_sm))
+ flags |= EAPOL_SM_FROM_PMKSA_CACHE;
+ }
+ return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
+ sta->wps_ie, sta->p2p_ie, sta);
+}
+
+
+/**
+ * ieee802_1x_receive - Process the EAPOL frames from the Supplicant
+ * @hapd: hostapd BSS data
+ * @sa: Source address (sender of the EAPOL frame)
+ * @buf: EAPOL frame
+ * @len: Length of buf in octets
+ *
+ * This function is called for each incoming EAPOL frame from the interface
+ */
+void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
+ size_t len)
+{
+ struct sta_info *sta;
+ struct ieee802_1x_hdr *hdr;
+ struct ieee802_1x_eapol_key *key;
+ u16 datalen;
+ struct rsn_pmksa_cache_entry *pmksa;
+ 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))) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not "
+ "associated/Pre-authenticating STA");
+ return;
+ }
+
+ if (len < sizeof(*hdr)) {
+ printf(" too short IEEE 802.1X packet\n");
+ return;
+ }
+
+ hdr = (struct ieee802_1x_hdr *) buf;
+ datalen = be_to_host16(hdr->length);
+ wpa_printf(MSG_DEBUG, " IEEE 802.1X: version=%d type=%d length=%d",
+ hdr->version, hdr->type, datalen);
+
+ if (len - sizeof(*hdr) < datalen) {
+ printf(" frame too short for this IEEE 802.1X packet\n");
+ if (sta->eapol_sm)
+ sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
+ return;
+ }
+ if (len - sizeof(*hdr) > datalen) {
+ wpa_printf(MSG_DEBUG, " ignoring %lu extra octets after "
+ "IEEE 802.1X packet",
+ (unsigned long) len - sizeof(*hdr) - datalen);
+ }
+
+ if (sta->eapol_sm) {
+ sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version;
+ sta->eapol_sm->dot1xAuthEapolFramesRx++;
+ }
+
+ key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+ if (datalen >= sizeof(struct ieee802_1x_eapol_key) &&
+ hdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
+ (key->type == EAPOL_KEY_TYPE_WPA ||
+ key->type == EAPOL_KEY_TYPE_RSN)) {
+ wpa_receive(hapd->wpa_auth, sta->wpa_sm, (u8 *) hdr,
+ sizeof(*hdr) + datalen);
+ return;
+ }
+
+ if (!hapd->conf->ieee802_1x &&
+ !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
+ wpa_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 &&
+ ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
+ WLAN_STA_MAYBE_WPS)) {
+ /*
+ * Delay EAPOL frame transmission until a possible WPS
+ * STA initiates the handshake with EAPOL-Start.
+ */
+ sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+ }
+#endif /* CONFIG_WPS */
+
+ sta->eapol_sm->eap_if->portEnabled = TRUE;
+ }
+
+ /* since we support version 1, we can ignore version field and proceed
+ * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */
+ /* TODO: actually, we are not version 1 anymore.. However, Version 2
+ * does not change frame contents, so should be ok to process frames
+ * more or less identically. Some changes might be needed for
+ * verification of fields. */
+
+ switch (hdr->type) {
+ case IEEE802_1X_TYPE_EAP_PACKET:
+ handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen);
+ break;
+
+ case IEEE802_1X_TYPE_EAPOL_START:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start "
+ "from STA");
+ sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
+ pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+ if (pmksa) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG, "cached PMKSA "
+ "available - ignore it since "
+ "STA sent EAPOL-Start");
+ wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa);
+ }
+ sta->eapol_sm->eapolStart = TRUE;
+ sta->eapol_sm->dot1xAuthEapolStartFramesRx++;
+ 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");
+ 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");
+ return;
+ }
+
+ if (sta->eapol_sm == NULL) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "start authentication");
+ sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
+ if (sta->eapol_sm == NULL) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_INFO,
+ "failed to allocate state machine");
+ return;
+ }
+ reassoc = 0;
+ }
+
+#ifdef CONFIG_WPS
+ sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
+ if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) {
+ /*
+ * Delay EAPOL frame transmission until a possible WPS
+ * initiates the handshake with EAPOL-Start.
+ */
+ sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+ }
+#endif /* CONFIG_WPS */
+
+ sta->eapol_sm->eap_if->portEnabled = TRUE;
+
+#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;
+ 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;
+ if (sta->eapol_sm->eap)
+ eap_sm_notify_cached(sta->eapol_sm->eap);
+ old_vlanid = sta->vlan_id;
+ pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm);
+ if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+ sta->vlan_id = 0;
+ ap_sta_bind_vlan(hapd, sta, old_vlanid);
+ } else {
+ if (reassoc) {
+ /*
+ * Force EAPOL state machines to start
+ * re-authentication without having to wait for the
+ * Supplicant to send EAPOL-Start.
+ */
+ sta->eapol_sm->reAuthenticate = TRUE;
+ }
+ eapol_auth_step(sta->eapol_sm);
+ }
+}
+
+
+void ieee802_1x_free_station(struct sta_info *sta)
+{
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm == NULL)
+ return;
+
+ sta->eapol_sm = NULL;
+
+#ifndef CONFIG_NO_RADIUS
+ radius_msg_free(sm->last_recv_radius);
+ radius_free_class(&sm->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+
+ os_free(sm->identity);
+ eapol_auth_free(sm);
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ u8 *eap;
+ size_t len;
+ struct eap_hdr *hdr;
+ int eap_type = -1;
+ char buf[64];
+ struct radius_msg *msg;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm == NULL || sm->last_recv_radius == NULL) {
+ if (sm)
+ sm->eap_if->aaaEapNoReq = TRUE;
+ return;
+ }
+
+ msg = sm->last_recv_radius;
+
+ eap = radius_msg_get_eap(msg, &len);
+ if (eap == NULL) {
+ /* RFC 3579, Chap. 2.6.3:
+ * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
+ * attribute */
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING, "could not extract "
+ "EAP-Message from RADIUS message");
+ sm->eap_if->aaaEapNoReq = TRUE;
+ return;
+ }
+
+ if (len < sizeof(*hdr)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING, "too short EAP packet "
+ "received from authentication server");
+ os_free(eap);
+ sm->eap_if->aaaEapNoReq = TRUE;
+ return;
+ }
+
+ if (len > sizeof(*hdr))
+ eap_type = eap[sizeof(*hdr)];
+
+ hdr = (struct eap_hdr *) eap;
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (eap_type >= 0)
+ sm->eap_type_authsrv = eap_type;
+ os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
+ eap_type >= 0 ? eap_server_get_name(0, eap_type) :
+ "??",
+ eap_type);
+ break;
+ case EAP_CODE_RESPONSE:
+ os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
+ eap_type >= 0 ? eap_server_get_name(0, eap_type) :
+ "??",
+ eap_type);
+ break;
+ case EAP_CODE_SUCCESS:
+ os_strlcpy(buf, "EAP Success", sizeof(buf));
+ break;
+ case EAP_CODE_FAILURE:
+ os_strlcpy(buf, "EAP Failure", sizeof(buf));
+ break;
+ default:
+ os_strlcpy(buf, "unknown EAP code", sizeof(buf));
+ break;
+ }
+ buf[sizeof(buf) - 1] = '\0';
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d "
+ "id=%d len=%d) from RADIUS server: %s",
+ hdr->code, hdr->identifier, be_to_host16(hdr->length),
+ buf);
+ sm->eap_if->aaaEapReq = TRUE;
+
+ wpabuf_free(sm->eap_if->aaaEapReqData);
+ sm->eap_if->aaaEapReqData = wpabuf_alloc_ext_data(eap, len);
+}
+
+
+static void ieee802_1x_get_keys(struct hostapd_data *hapd,
+ struct sta_info *sta, struct radius_msg *msg,
+ struct radius_msg *req,
+ const u8 *shared_secret,
+ size_t shared_secret_len)
+{
+ struct radius_ms_mppe_keys *keys;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ if (sm == NULL)
+ return;
+
+ keys = radius_msg_get_ms_keys(msg, req, shared_secret,
+ shared_secret_len);
+
+ if (keys && keys->send && keys->recv) {
+ size_t len = keys->send_len + keys->recv_len;
+ wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key",
+ keys->send, keys->send_len);
+ wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key",
+ keys->recv, keys->recv_len);
+
+ os_free(sm->eap_if->aaaEapKeyData);
+ sm->eap_if->aaaEapKeyData = os_malloc(len);
+ if (sm->eap_if->aaaEapKeyData) {
+ os_memcpy(sm->eap_if->aaaEapKeyData, keys->recv,
+ keys->recv_len);
+ os_memcpy(sm->eap_if->aaaEapKeyData + keys->recv_len,
+ keys->send, keys->send_len);
+ sm->eap_if->aaaEapKeyDataLen = len;
+ sm->eap_if->aaaEapKeyAvailable = TRUE;
+ }
+ }
+
+ if (keys) {
+ os_free(keys->send);
+ os_free(keys->recv);
+ os_free(keys);
+ }
+}
+
+
+static void ieee802_1x_store_radius_class(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ u8 *class;
+ size_t class_len;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ int count, i;
+ struct radius_attr_data *nclass;
+ size_t nclass_count;
+
+ if (!hapd->conf->radius->acct_server || hapd->radius == NULL ||
+ sm == NULL)
+ return;
+
+ radius_free_class(&sm->radius_class);
+ count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1);
+ if (count <= 0)
+ return;
+
+ nclass = os_zalloc(count * sizeof(struct radius_attr_data));
+ if (nclass == NULL)
+ return;
+
+ nclass_count = 0;
+
+ class = NULL;
+ for (i = 0; i < count; i++) {
+ do {
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS,
+ &class, &class_len,
+ class) < 0) {
+ i = count;
+ break;
+ }
+ } while (class_len < 1);
+
+ nclass[nclass_count].data = os_malloc(class_len);
+ if (nclass[nclass_count].data == NULL)
+ break;
+
+ os_memcpy(nclass[nclass_count].data, class, class_len);
+ nclass[nclass_count].len = class_len;
+ nclass_count++;
+ }
+
+ sm->radius_class.attr = nclass;
+ sm->radius_class.count = nclass_count;
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Stored %lu RADIUS Class "
+ "attributes for " MACSTR,
+ (unsigned long) sm->radius_class.count,
+ MAC2STR(sta->addr));
+}
+
+
+/* Update sta->identity based on User-Name attribute in Access-Accept */
+static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ u8 *buf, *identity;
+ size_t len;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm == NULL)
+ return;
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len,
+ NULL) < 0)
+ return;
+
+ identity = os_malloc(len + 1);
+ if (identity == NULL)
+ return;
+
+ os_memcpy(identity, buf, len);
+ identity[len] = '\0';
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with "
+ "User-Name from Access-Accept '%s'",
+ sm->identity ? (char *) sm->identity : "N/A",
+ (char *) identity);
+
+ os_free(sm->identity);
+ sm->identity = identity;
+ sm->identity_len = len;
+}
+
+
+struct sta_id_search {
+ u8 identifier;
+ struct eapol_state_machine *sm;
+};
+
+
+static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ void *ctx)
+{
+ struct sta_id_search *id_search = ctx;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm && sm->radius_identifier >= 0 &&
+ sm->radius_identifier == id_search->identifier) {
+ id_search->sm = sm;
+ return 1;
+ }
+ return 0;
+}
+
+
+static struct eapol_state_machine *
+ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier)
+{
+ struct sta_id_search id_search;
+ id_search.identifier = identifier;
+ id_search.sm = NULL;
+ ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search);
+ return id_search.sm;
+}
+
+
+/**
+ * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: Processing status
+ */
+static RadiusRxResult
+ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
+ const u8 *shared_secret, size_t shared_secret_len,
+ void *data)
+{
+ struct hostapd_data *hapd = data;
+ struct sta_info *sta;
+ u32 session_timeout = 0, termination_action, acct_interim_interval;
+ int session_timeout_set, old_vlanid = 0;
+ struct eapol_state_machine *sm;
+ int override_eapReq = 0;
+ struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+ sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier);
+ if (sm == NULL) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Could not find matching "
+ "station for this RADIUS message");
+ return RADIUS_RX_UNKNOWN;
+ }
+ sta = sm->sta;
+
+ /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
+ * present when packet contains an EAP-Message attribute */
+ if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
+ radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
+ 0) < 0 &&
+ radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "Allowing RADIUS Access-Reject without "
+ "Message-Authenticator since it does not include "
+ "EAP-Message");
+ } else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
+ req, 1)) {
+ printf("Incoming RADIUS packet did not have correct "
+ "Message-Authenticator - dropped\n");
+ return RADIUS_RX_INVALID_AUTHENTICATOR;
+ }
+
+ if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+ hdr->code != RADIUS_CODE_ACCESS_REJECT &&
+ hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
+ printf("Unknown RADIUS message code\n");
+ return RADIUS_RX_UNKNOWN;
+ }
+
+ sm->radius_identifier = -1;
+ wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR,
+ MAC2STR(sta->addr));
+
+ radius_msg_free(sm->last_recv_radius);
+ sm->last_recv_radius = msg;
+
+ session_timeout_set =
+ !radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
+ &session_timeout);
+ if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION,
+ &termination_action))
+ termination_action = RADIUS_TERMINATION_ACTION_DEFAULT;
+
+ if (hapd->conf->acct_interim_interval == 0 &&
+ hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
+ radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
+ &acct_interim_interval) == 0) {
+ if (acct_interim_interval < 60) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_INFO,
+ "ignored too small "
+ "Acct-Interim-Interval %d",
+ acct_interim_interval);
+ } else
+ sta->acct_interim_interval = acct_interim_interval;
+ }
+
+
+ switch (hdr->code) {
+ case RADIUS_CODE_ACCESS_ACCEPT:
+ if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+ sta->vlan_id = 0;
+#ifndef CONFIG_NO_VLAN
+ else {
+ old_vlanid = sta->vlan_id;
+ sta->vlan_id = radius_msg_get_vlanid(msg);
+ }
+ if (sta->vlan_id > 0 &&
+ hostapd_get_vlan_id_ifname(hapd->conf->vlan,
+ sta->vlan_id)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "VLAN ID %d", sta->vlan_id);
+ } else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) {
+ sta->eapol_sm->authFail = TRUE;
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_INFO, "authentication "
+ "server did not include required VLAN "
+ "ID in Access-Accept");
+ break;
+ }
+#endif /* CONFIG_NO_VLAN */
+
+ if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0)
+ break;
+
+ /* RFC 3580, Ch. 3.17 */
+ if (session_timeout_set && termination_action ==
+ RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
+ sm->reAuthPeriod = session_timeout;
+ } else if (session_timeout_set)
+ ap_sta_session_timeout(hapd, sta, session_timeout);
+
+ sm->eap_if->aaaSuccess = TRUE;
+ override_eapReq = 1;
+ ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret,
+ shared_secret_len);
+ ieee802_1x_store_radius_class(hapd, sta, msg);
+ ieee802_1x_update_sta_identity(hapd, sta, msg);
+ if (sm->eap_if->eapKeyAvailable &&
+ wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
+ session_timeout_set ?
+ (int) session_timeout : -1, sm) == 0) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "Added PMKSA cache entry");
+ }
+ break;
+ case RADIUS_CODE_ACCESS_REJECT:
+ sm->eap_if->aaaFail = TRUE;
+ override_eapReq = 1;
+ break;
+ case RADIUS_CODE_ACCESS_CHALLENGE:
+ sm->eap_if->aaaEapReq = TRUE;
+ if (session_timeout_set) {
+ /* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */
+ sm->eap_if->aaaMethodTimeout = session_timeout;
+ hostapd_logger(hapd, sm->addr,
+ HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "using EAP timeout of %d seconds (from "
+ "RADIUS)",
+ sm->eap_if->aaaMethodTimeout);
+ } else {
+ /*
+ * Use dynamic retransmission behavior per EAP
+ * specification.
+ */
+ sm->eap_if->aaaMethodTimeout = 0;
+ }
+ break;
+ }
+
+ ieee802_1x_decapsulate_radius(hapd, sta);
+ if (override_eapReq)
+ sm->eap_if->aaaEapReq = FALSE;
+
+ eapol_auth_step(sm);
+
+ return RADIUS_RX_QUEUED;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ if (sm == NULL)
+ return;
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "aborting authentication");
+
+#ifndef CONFIG_NO_RADIUS
+ radius_msg_free(sm->last_recv_radius);
+ sm->last_recv_radius = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+ if (sm->eap_if->eapTimeout) {
+ /*
+ * Disconnect the STA since it did not reply to the last EAP
+ * request and we cannot continue EAP processing (EAP-Failure
+ * could only be sent if the EAP peer actually replied).
+ */
+ sm->eap_if->portEnabled = FALSE;
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+}
+
+
+static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd)
+{
+ struct eapol_authenticator *eapol = hapd->eapol_auth;
+
+ if (hapd->conf->default_wep_key_len < 1)
+ return 0;
+
+ os_free(eapol->default_wep_key);
+ eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len);
+ if (eapol->default_wep_key == NULL ||
+ random_get_bytes(eapol->default_wep_key,
+ hapd->conf->default_wep_key_len)) {
+ printf("Could not generate random WEP key.\n");
+ os_free(eapol->default_wep_key);
+ eapol->default_wep_key = NULL;
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "IEEE 802.1X: New default WEP key",
+ eapol->default_wep_key,
+ hapd->conf->default_wep_key_len);
+
+ return 0;
+}
+
+
+static int ieee802_1x_sta_key_available(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx)
+{
+ if (sta->eapol_sm) {
+ sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+ eapol_auth_step(sta->eapol_sm);
+ }
+ return 0;
+}
+
+
+static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct eapol_authenticator *eapol = hapd->eapol_auth;
+
+ if (eapol->default_wep_key_idx >= 3)
+ eapol->default_wep_key_idx =
+ hapd->conf->individual_wep_key_len > 0 ? 1 : 0;
+ else
+ eapol->default_wep_key_idx++;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d",
+ eapol->default_wep_key_idx);
+
+ if (ieee802_1x_rekey_broadcast(hapd)) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_WARNING, "failed to generate a "
+ "new broadcast key");
+ os_free(eapol->default_wep_key);
+ eapol->default_wep_key = NULL;
+ return;
+ }
+
+ /* TODO: Could setup key for RX here, but change default TX keyid only
+ * after new broadcast key has been sent to all stations. */
+ if (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, count;
+
+ eap_user = hostapd_get_eap_user(hapd->conf, identity,
+ identity_len, phase2);
+ if (eap_user == NULL)
+ return -1;
+
+ os_memset(user, 0, sizeof(*user));
+ user->phase2 = phase2;
+ count = EAP_USER_MAX_METHODS;
+ if (count > EAP_MAX_METHODS)
+ count = EAP_MAX_METHODS;
+ for (i = 0; i < count; i++) {
+ user->methods[i].vendor = eap_user->methods[i].vendor;
+ user->methods[i].method = eap_user->methods[i].method;
+ }
+
+ if (eap_user->password) {
+ user->password = os_malloc(eap_user->password_len);
+ if (user->password == NULL)
+ return -1;
+ os_memcpy(user->password, eap_user->password,
+ eap_user->password_len);
+ user->password_len = eap_user->password_len;
+ }
+ user->force_version = eap_user->force_version;
+ user->ttls_auth = eap_user->ttls_auth;
+
+ return 0;
+}
+
+
+static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL || sta->eapol_sm == NULL)
+ return 0;
+ return 1;
+}
+
+
+static void ieee802_1x_logger(void *ctx, const u8 *addr,
+ eapol_logger_level level, const char *txt)
+{
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+ struct hostapd_data *hapd = ctx;
+ int hlevel;
+
+ switch (level) {
+ case EAPOL_LOGGER_WARNING:
+ hlevel = HOSTAPD_LEVEL_WARNING;
+ break;
+ case EAPOL_LOGGER_INFO:
+ hlevel = HOSTAPD_LEVEL_INFO;
+ break;
+ case EAPOL_LOGGER_DEBUG:
+ default:
+ hlevel = HOSTAPD_LEVEL_DEBUG;
+ break;
+ }
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s",
+ txt);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+}
+
+
+static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx,
+ int authorized)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = sta_ctx;
+ ieee802_1x_set_sta_authorized(hapd, sta, authorized);
+}
+
+
+static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = sta_ctx;
+ ieee802_1x_abort_auth(hapd, sta);
+}
+
+
+static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = sta_ctx;
+ ieee802_1x_tx_key(hapd, sta);
+}
+
+
+static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
+ enum eapol_event type)
+{
+ /* struct hostapd_data *hapd = ctx; */
+ struct sta_info *sta = sta_ctx;
+ switch (type) {
+ case EAPOL_AUTH_SM_CHANGE:
+ wpa_auth_sm_notify(sta->wpa_sm);
+ break;
+ case EAPOL_AUTH_REAUTHENTICATE:
+ wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
+ break;
+ }
+}
+
+
+int ieee802_1x_init(struct hostapd_data *hapd)
+{
+ int i;
+ struct eapol_auth_config conf;
+ struct eapol_auth_cb cb;
+
+ os_memset(&conf, 0, sizeof(conf));
+ conf.ctx = hapd;
+ conf.eap_reauth_period = hapd->conf->eap_reauth_period;
+ conf.wpa = hapd->conf->wpa;
+ conf.individual_wep_key_len = hapd->conf->individual_wep_key_len;
+ conf.eap_server = hapd->conf->eap_server;
+ conf.ssl_ctx = hapd->ssl_ctx;
+ conf.msg_ctx = hapd->msg_ctx;
+ conf.eap_sim_db_priv = hapd->eap_sim_db_priv;
+ conf.eap_req_id_text = hapd->conf->eap_req_id_text;
+ conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
+ conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
+ conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
+ conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
+ conf.eap_fast_a_id_info = hapd->conf->eap_fast_a_id_info;
+ conf.eap_fast_prov = hapd->conf->eap_fast_prov;
+ conf.pac_key_lifetime = hapd->conf->pac_key_lifetime;
+ conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
+ conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
+ conf.tnc = hapd->conf->tnc;
+ conf.wps = hapd->wps;
+ conf.fragment_size = hapd->conf->fragment_size;
+ conf.pwd_group = hapd->conf->pwd_group;
+
+ 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;
+ struct ieee802_1x_hdr *xhdr;
+ struct ieee802_1x_eapol_key *key;
+ u8 *pos;
+ const unsigned char rfc1042_hdr[ETH_ALEN] =
+ { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+ if (sta == NULL)
+ return -1;
+ if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2 + sizeof(*xhdr))
+ return 0;
+
+ hdr = (struct ieee80211_hdr *) buf;
+ pos = (u8 *) (hdr + 1);
+ if (os_memcmp(pos, rfc1042_hdr, sizeof(rfc1042_hdr)) != 0)
+ return 0;
+ pos += sizeof(rfc1042_hdr);
+ if (WPA_GET_BE16(pos) != ETH_P_PAE)
+ return 0;
+ pos += 2;
+
+ xhdr = (struct ieee802_1x_hdr *) pos;
+ pos += sizeof(*xhdr);
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " TX status - version=%d "
+ "type=%d length=%d - ack=%d",
+ MAC2STR(sta->addr), xhdr->version, xhdr->type,
+ be_to_host16(xhdr->length), ack);
+
+ if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
+ pos + sizeof(struct wpa_eapol_key) <= buf + len) {
+ const struct wpa_eapol_key *wpa;
+ wpa = (const struct wpa_eapol_key *) pos;
+ if (wpa->type == EAPOL_KEY_TYPE_RSN ||
+ wpa->type == EAPOL_KEY_TYPE_WPA)
+ wpa_auth_eapol_key_tx_status(hapd->wpa_auth,
+ sta->wpa_sm, ack);
+ }
+
+ /* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant
+ * or Authenticator state machines, but EAPOL-Key packets are not
+ * retransmitted in case of failure. Try to re-sent failed EAPOL-Key
+ * packets couple of times because otherwise STA keys become
+ * unsynchronized with AP. */
+ if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && !ack &&
+ pos + sizeof(*key) <= buf + len) {
+ key = (struct ieee802_1x_eapol_key *) pos;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key "
+ "frame (%scast index=%d)",
+ key->key_index & BIT(7) ? "uni" : "broad",
+ key->key_index & ~BIT(7));
+ /* TODO: re-send EAPOL-Key couple of times (with short delay
+ * between them?). If all attempt fail, report error and
+ * deauthenticate STA so that it will get new keys when
+ * authenticating again (e.g., after returning in range).
+ * Separate limit/transmit state needed both for unicast and
+ * broadcast keys(?) */
+ }
+ /* TODO: could move unicast key configuration from ieee802_1x_tx_key()
+ * to here and change the key only if the EAPOL-Key packet was Acked.
+ */
+
+ return 1;
+}
+
+
+u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len)
+{
+ if (sm == NULL || sm->identity == NULL)
+ return NULL;
+
+ *len = sm->identity_len;
+ return sm->identity;
+}
+
+
+u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
+ int idx)
+{
+ if (sm == NULL || sm->radius_class.attr == NULL ||
+ idx >= (int) sm->radius_class.count)
+ return NULL;
+
+ *len = sm->radius_class.attr[idx].len;
+ return sm->radius_class.attr[idx].data;
+}
+
+
+const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
+{
+ if (sm == NULL)
+ return NULL;
+
+ *len = sm->eap_if->eapKeyDataLen;
+ return sm->eap_if->eapKeyData;
+}
+
+
+void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
+ int enabled)
+{
+ if (sm == NULL)
+ return;
+ sm->eap_if->portEnabled = enabled ? TRUE : FALSE;
+ eapol_auth_step(sm);
+}
+
+
+void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
+ int valid)
+{
+ if (sm == NULL)
+ return;
+ sm->portValid = valid ? TRUE : FALSE;
+ eapol_auth_step(sm);
+}
+
+
+void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth)
+{
+ if (sm == NULL)
+ return;
+ if (pre_auth)
+ sm->flags |= EAPOL_SM_PREAUTH;
+ else
+ sm->flags &= ~EAPOL_SM_PREAUTH;
+}
+
+
+static const char * bool_txt(Boolean bool)
+{
+ return bool ? "TRUE" : "FALSE";
+}
+
+
+int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+ /* TODO */
+ return 0;
+}
+
+
+int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ int len = 0, ret;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm == NULL)
+ return 0;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "dot1xPaePortNumber=%d\n"
+ "dot1xPaePortProtocolVersion=%d\n"
+ "dot1xPaePortCapabilities=1\n"
+ "dot1xPaePortInitialize=%d\n"
+ "dot1xPaePortReauthenticate=FALSE\n",
+ sta->aid,
+ EAPOL_VERSION,
+ sm->initialize);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ /* dot1xAuthConfigTable */
+ ret = os_snprintf(buf + len, buflen - len,
+ "dot1xAuthPaeState=%d\n"
+ "dot1xAuthBackendAuthState=%d\n"
+ "dot1xAuthAdminControlledDirections=%d\n"
+ "dot1xAuthOperControlledDirections=%d\n"
+ "dot1xAuthAuthControlledPortStatus=%d\n"
+ "dot1xAuthAuthControlledPortControl=%d\n"
+ "dot1xAuthQuietPeriod=%u\n"
+ "dot1xAuthServerTimeout=%u\n"
+ "dot1xAuthReAuthPeriod=%u\n"
+ "dot1xAuthReAuthEnabled=%s\n"
+ "dot1xAuthKeyTxEnabled=%s\n",
+ sm->auth_pae_state + 1,
+ sm->be_auth_state + 1,
+ sm->adminControlledDirections,
+ sm->operControlledDirections,
+ sm->authPortStatus,
+ sm->portControl,
+ sm->quietPeriod,
+ sm->serverTimeout,
+ sm->reAuthPeriod,
+ bool_txt(sm->reAuthEnabled),
+ bool_txt(sm->keyTxEnabled));
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ /* dot1xAuthStatsTable */
+ ret = os_snprintf(buf + len, buflen - len,
+ "dot1xAuthEapolFramesRx=%u\n"
+ "dot1xAuthEapolFramesTx=%u\n"
+ "dot1xAuthEapolStartFramesRx=%u\n"
+ "dot1xAuthEapolLogoffFramesRx=%u\n"
+ "dot1xAuthEapolRespIdFramesRx=%u\n"
+ "dot1xAuthEapolRespFramesRx=%u\n"
+ "dot1xAuthEapolReqIdFramesTx=%u\n"
+ "dot1xAuthEapolReqFramesTx=%u\n"
+ "dot1xAuthInvalidEapolFramesRx=%u\n"
+ "dot1xAuthEapLengthErrorFramesRx=%u\n"
+ "dot1xAuthLastEapolFrameVersion=%u\n"
+ "dot1xAuthLastEapolFrameSource=" MACSTR "\n",
+ sm->dot1xAuthEapolFramesRx,
+ sm->dot1xAuthEapolFramesTx,
+ sm->dot1xAuthEapolStartFramesRx,
+ sm->dot1xAuthEapolLogoffFramesRx,
+ sm->dot1xAuthEapolRespIdFramesRx,
+ sm->dot1xAuthEapolRespFramesRx,
+ sm->dot1xAuthEapolReqIdFramesTx,
+ sm->dot1xAuthEapolReqFramesTx,
+ sm->dot1xAuthInvalidEapolFramesRx,
+ sm->dot1xAuthEapLengthErrorFramesRx,
+ sm->dot1xAuthLastEapolFrameVersion,
+ MAC2STR(sm->addr));
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ /* dot1xAuthDiagTable */
+ ret = os_snprintf(buf + len, buflen - len,
+ "dot1xAuthEntersConnecting=%u\n"
+ "dot1xAuthEapLogoffsWhileConnecting=%u\n"
+ "dot1xAuthEntersAuthenticating=%u\n"
+ "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n"
+ "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n"
+ "dot1xAuthAuthFailWhileAuthenticating=%u\n"
+ "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n"
+ "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n"
+ "dot1xAuthAuthReauthsWhileAuthenticated=%u\n"
+ "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n"
+ "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n"
+ "dot1xAuthBackendResponses=%u\n"
+ "dot1xAuthBackendAccessChallenges=%u\n"
+ "dot1xAuthBackendOtherRequestsToSupplicant=%u\n"
+ "dot1xAuthBackendAuthSuccesses=%u\n"
+ "dot1xAuthBackendAuthFails=%u\n",
+ sm->authEntersConnecting,
+ sm->authEapLogoffsWhileConnecting,
+ sm->authEntersAuthenticating,
+ sm->authAuthSuccessesWhileAuthenticating,
+ sm->authAuthTimeoutsWhileAuthenticating,
+ sm->authAuthFailWhileAuthenticating,
+ sm->authAuthEapStartsWhileAuthenticating,
+ sm->authAuthEapLogoffWhileAuthenticating,
+ sm->authAuthReauthsWhileAuthenticated,
+ sm->authAuthEapStartsWhileAuthenticated,
+ sm->authAuthEapLogoffWhileAuthenticated,
+ sm->backendResponses,
+ sm->backendAccessChallenges,
+ sm->backendOtherRequestsToSupplicant,
+ sm->backendAuthSuccesses,
+ sm->backendAuthFails);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ /* dot1xAuthSessionStatsTable */
+ ret = os_snprintf(buf + len, buflen - len,
+ /* TODO: dot1xAuthSessionOctetsRx */
+ /* TODO: dot1xAuthSessionOctetsTx */
+ /* TODO: dot1xAuthSessionFramesRx */
+ /* TODO: dot1xAuthSessionFramesTx */
+ "dot1xAuthSessionId=%08X-%08X\n"
+ "dot1xAuthSessionAuthenticMethod=%d\n"
+ "dot1xAuthSessionTime=%u\n"
+ "dot1xAuthSessionTerminateCause=999\n"
+ "dot1xAuthSessionUserName=%s\n",
+ sta->acct_session_id_hi, sta->acct_session_id_lo,
+ (wpa_key_mgmt_wpa_ieee8021x(
+ wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
+ 1 : 2,
+ (unsigned int) (time(NULL) -
+ sta->acct_session_start),
+ sm->identity);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ return len;
+}
+
+
+static void ieee802_1x_finished(struct hostapd_data *hapd,
+ struct sta_info *sta, int success)
+{
+ const u8 *key;
+ size_t len;
+ /* TODO: get PMKLifetime from WPA parameters */
+ static const int dot11RSNAConfigPMKLifetime = 43200;
+
+ key = ieee802_1x_get_key(sta->eapol_sm, &len);
+ if (success && key && len >= PMK_LEN &&
+ wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime,
+ sta->eapol_sm) == 0) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "Added PMKSA cache entry (IEEE 802.1X)");
+ }
+
+#ifdef CONFIG_WPS
+ if (!success && (sta->flags & WLAN_STA_WPS)) {
+ /*
+ * Many devices require deauthentication after WPS provisioning
+ * and some may not be be able to do that themselves, so
+ * disconnect the client here.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Force disconnection after "
+ "EAP-Failure");
+ /* Add a small sleep to increase likelihood of previously
+ * requested EAP-Failure TX getting out before this should the
+ * driver reorder operations.
+ */
+ os_sleep(0, 10000);
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+#endif /* CONFIG_WPS */
+}
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
new file mode 100644
index 0000000..1a4d2eb
--- /dev/null
+++ b/src/ap/ieee802_1x.h
@@ -0,0 +1,89 @@
+/*
+ * hostapd / IEEE 802.1X-2004 Authenticator
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef IEEE802_1X_H
+#define IEEE802_1X_H
+
+struct hostapd_data;
+struct sta_info;
+struct eapol_state_machine;
+struct hostapd_config;
+struct hostapd_bss_config;
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* RFC 3580, 4. RC4 EAPOL-Key Frame */
+
+struct ieee802_1x_eapol_key {
+ u8 type;
+ u16 key_length;
+ u8 replay_counter[8]; /* does not repeat within the life of the keying
+ * material used to encrypt the Key field;
+ * 64-bit NTP timestamp MAY be used here */
+ u8 key_iv[16]; /* cryptographically random number */
+ u8 key_index; /* key flag in the most significant bit:
+ * 0 = broadcast (default key),
+ * 1 = unicast (key mapping key); key index is in the
+ * 7 least significant bits */
+ u8 key_signature[16]; /* HMAC-MD5 message integrity check computed with
+ * MS-MPPE-Send-Key as the key */
+
+ /* followed by key: if packet body length = 44 + key length, then the
+ * key field (of key_length bytes) contains the key in encrypted form;
+ * if packet body length = 44, key field is absent and key_length
+ * represents the number of least significant octets from
+ * MS-MPPE-Send-Key attribute to be used as the keying material;
+ * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
+ size_t len);
+void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_free_station(struct sta_info *sta);
+
+void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized);
+void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta);
+int ieee802_1x_init(struct hostapd_data *hapd);
+void ieee802_1x_deinit(struct hostapd_data *hapd);
+int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *buf, size_t len, int ack);
+u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len);
+u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
+ int idx);
+const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
+void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
+ int enabled);
+void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
+ int valid);
+void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth);
+int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen);
+void hostapd_get_ntp_timestamp(u8 *buf);
+char *eap_type_text(u8 type);
+
+const char *radius_mode_txt(struct hostapd_data *hapd);
+int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta);
+
+#endif /* IEEE802_1X_H */
diff --git a/src/ap/p2p_hostapd.c b/src/ap/p2p_hostapd.c
new file mode 100644
index 0000000..6f8b778
--- /dev/null
+++ b/src/ap/p2p_hostapd.c
@@ -0,0 +1,120 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "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/src/ap/p2p_hostapd.h b/src/ap/p2p_hostapd.h
new file mode 100644
index 0000000..95b31d9
--- /dev/null
+++ b/src/ap/p2p_hostapd.h
@@ -0,0 +1,41 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#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/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c
new file mode 100644
index 0000000..b8fa5a9
--- /dev/null
+++ b/src/ap/peerkey_auth.c
@@ -0,0 +1,402 @@
+/*
+ * hostapd - PeerKey for Direct Link Setup (DLS)
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "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/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
new file mode 100644
index 0000000..22f44b7
--- /dev/null
+++ b/src/ap/pmksa_cache_auth.c
@@ -0,0 +1,425 @@
+/*
+ * hostapd - PMKSA cache for IEEE 802.11i RSN
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "pmksa_cache_auth.h"
+
+
+static const int pmksa_cache_max_entries = 1024;
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct rsn_pmksa_cache {
+#define PMKID_HASH_SIZE 128
+#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
+ struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
+ struct rsn_pmksa_cache_entry *pmksa;
+ int pmksa_count;
+
+ void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
+ void *ctx;
+};
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
+
+
+static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
+{
+ if (entry == NULL)
+ return;
+ os_free(entry->identity);
+#ifndef CONFIG_NO_RADIUS
+ radius_free_class(&entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+ os_free(entry);
+}
+
+
+static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ struct rsn_pmksa_cache_entry *pos, *prev;
+
+ pmksa->pmksa_count--;
+ pmksa->free_cb(entry, pmksa->ctx);
+ pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
+ prev = NULL;
+ while (pos) {
+ if (pos == entry) {
+ if (prev != NULL) {
+ prev->hnext = pos->hnext;
+ } else {
+ pmksa->pmkid[PMKID_HASH(entry->pmkid)] =
+ pos->hnext;
+ }
+ break;
+ }
+ prev = pos;
+ pos = pos->hnext;
+ }
+
+ pos = pmksa->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (pos == entry) {
+ if (prev != NULL)
+ prev->next = pos->next;
+ else
+ pmksa->pmksa = pos->next;
+ break;
+ }
+ prev = pos;
+ pos = pos->next;
+ }
+ _pmksa_cache_free_entry(entry);
+}
+
+
+static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+ struct rsn_pmksa_cache *pmksa = eloop_ctx;
+ struct os_time now;
+
+ os_get_time(&now);
+ while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+ pmksa->pmksa = entry->next;
+ wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
+ MACSTR, MAC2STR(entry->spa));
+ pmksa_cache_free_entry(pmksa, entry);
+ }
+
+ pmksa_cache_set_expiration(pmksa);
+}
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
+{
+ int sec;
+ struct os_time now;
+
+ eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+ if (pmksa->pmksa == NULL)
+ return;
+ os_get_time(&now);
+ sec = pmksa->pmksa->expiration - now.sec;
+ if (sec < 0)
+ sec = 0;
+ eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
+}
+
+
+static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
+ struct eapol_state_machine *eapol)
+{
+ if (eapol == NULL)
+ return;
+
+ if (eapol->identity) {
+ entry->identity = os_malloc(eapol->identity_len);
+ if (entry->identity) {
+ entry->identity_len = eapol->identity_len;
+ os_memcpy(entry->identity, eapol->identity,
+ eapol->identity_len);
+ }
+ }
+
+#ifndef CONFIG_NO_RADIUS
+ radius_copy_class(&entry->radius_class, &eapol->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+
+ entry->eap_type_authsrv = eapol->eap_type_authsrv;
+ entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id;
+}
+
+
+void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
+ struct eapol_state_machine *eapol)
+{
+ if (entry == NULL || eapol == NULL)
+ return;
+
+ if (entry->identity) {
+ os_free(eapol->identity);
+ eapol->identity = os_malloc(entry->identity_len);
+ if (eapol->identity) {
+ eapol->identity_len = entry->identity_len;
+ os_memcpy(eapol->identity, entry->identity,
+ entry->identity_len);
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
+ eapol->identity, eapol->identity_len);
+ }
+
+#ifndef CONFIG_NO_RADIUS
+ radius_free_class(&eapol->radius_class);
+ radius_copy_class(&eapol->radius_class, &entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+ if (eapol->radius_class.attr) {
+ wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
+ "PMKSA", (unsigned long) eapol->radius_class.count);
+ }
+
+ eapol->eap_type_authsrv = entry->eap_type_authsrv;
+ ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
+}
+
+
+static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ struct rsn_pmksa_cache_entry *pos, *prev;
+
+ /* Add the new entry; order by expiration time */
+ pos = pmksa->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (pos->expiration > entry->expiration)
+ break;
+ prev = pos;
+ pos = pos->next;
+ }
+ if (prev == NULL) {
+ entry->next = pmksa->pmksa;
+ pmksa->pmksa = entry;
+ } else {
+ entry->next = prev->next;
+ prev->next = entry;
+ }
+ entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
+ pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
+
+ pmksa->pmksa_count++;
+ wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
+ MAC2STR(entry->spa));
+ wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
+}
+
+
+/**
+ * pmksa_cache_auth_add - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @session_timeout: Session timeout
+ * @eapol: Pointer to EAPOL state machine data
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
+ * cache. If an old entry is already in the cache for the same Supplicant,
+ * this entry will be replaced with the new entry. PMKID will be calculated
+ * based on the PMK.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
+ const u8 *pmk, size_t pmk_len,
+ const u8 *aa, const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp)
+{
+ struct rsn_pmksa_cache_entry *entry, *pos;
+ struct os_time now;
+
+ if (pmk_len > PMK_LEN)
+ return NULL;
+
+ entry = os_zalloc(sizeof(*entry));
+ if (entry == NULL)
+ return NULL;
+ os_memcpy(entry->pmk, pmk, pmk_len);
+ entry->pmk_len = pmk_len;
+ rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
+ wpa_key_mgmt_sha256(akmp));
+ os_get_time(&now);
+ entry->expiration = now.sec;
+ if (session_timeout > 0)
+ entry->expiration += session_timeout;
+ else
+ entry->expiration += dot11RSNAConfigPMKLifetime;
+ entry->akmp = akmp;
+ os_memcpy(entry->spa, spa, ETH_ALEN);
+ pmksa_cache_from_eapol_data(entry, eapol);
+
+ /* Replace an old entry for the same STA (if found) with the new entry
+ */
+ pos = pmksa_cache_auth_get(pmksa, spa, NULL);
+ if (pos)
+ pmksa_cache_free_entry(pmksa, pos);
+
+ if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
+ /* Remove the oldest entry to make room for the new entry */
+ wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
+ "entry (for " MACSTR ") to make room for new one",
+ MAC2STR(pmksa->pmksa->spa));
+ pmksa_cache_free_entry(pmksa, pmksa->pmksa);
+ }
+
+ pmksa_cache_link_entry(pmksa, entry);
+
+ return entry;
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
+ const struct rsn_pmksa_cache_entry *old_entry,
+ const u8 *aa, const u8 *pmkid)
+{
+ struct rsn_pmksa_cache_entry *entry;
+
+ entry = os_zalloc(sizeof(*entry));
+ if (entry == NULL)
+ return NULL;
+ os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
+ os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
+ entry->pmk_len = old_entry->pmk_len;
+ entry->expiration = old_entry->expiration;
+ entry->akmp = old_entry->akmp;
+ os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
+ entry->opportunistic = 1;
+ if (old_entry->identity) {
+ entry->identity = os_malloc(old_entry->identity_len);
+ if (entry->identity) {
+ entry->identity_len = old_entry->identity_len;
+ os_memcpy(entry->identity, old_entry->identity,
+ old_entry->identity_len);
+ }
+ }
+#ifndef CONFIG_NO_RADIUS
+ radius_copy_class(&entry->radius_class, &old_entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+ entry->eap_type_authsrv = old_entry->eap_type_authsrv;
+ entry->vlan_id = old_entry->vlan_id;
+ entry->opportunistic = 1;
+
+ pmksa_cache_link_entry(pmksa, entry);
+
+ return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_deinit - Free all entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ */
+void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
+{
+ struct rsn_pmksa_cache_entry *entry, *prev;
+ int i;
+
+ if (pmksa == NULL)
+ return;
+
+ entry = pmksa->pmksa;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ _pmksa_cache_free_entry(prev);
+ }
+ eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+ for (i = 0; i < PMKID_HASH_SIZE; i++)
+ pmksa->pmkid[i] = NULL;
+ os_free(pmksa);
+}
+
+
+/**
+ * pmksa_cache_auth_get - Fetch a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @spa: Supplicant address or %NULL to match any
+ * @pmkid: PMKID or %NULL to match any
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
+ const u8 *spa, const u8 *pmkid)
+{
+ struct rsn_pmksa_cache_entry *entry;
+
+ if (pmkid)
+ entry = pmksa->pmkid[PMKID_HASH(pmkid)];
+ else
+ entry = pmksa->pmksa;
+ while (entry) {
+ if ((spa == NULL ||
+ os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
+ (pmkid == NULL ||
+ os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
+ return entry;
+ entry = pmkid ? entry->hnext : entry->next;
+ }
+ return NULL;
+}
+
+
+/**
+ * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: PMKID
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ *
+ * Use opportunistic key caching (OKC) to find a PMK for a supplicant.
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
+ struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
+ const u8 *pmkid)
+{
+ struct rsn_pmksa_cache_entry *entry;
+ u8 new_pmkid[PMKID_LEN];
+
+ entry = pmksa->pmksa;
+ while (entry) {
+ if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
+ continue;
+ rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
+ wpa_key_mgmt_sha256(entry->akmp));
+ if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
+ return entry;
+ entry = entry->next;
+ }
+ return NULL;
+}
+
+
+/**
+ * pmksa_cache_auth_init - Initialize PMKSA cache
+ * @free_cb: Callback function to be called when a PMKSA cache entry is freed
+ * @ctx: Context pointer for free_cb function
+ * Returns: Pointer to PMKSA cache data or %NULL on failure
+ */
+struct rsn_pmksa_cache *
+pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx), void *ctx)
+{
+ struct rsn_pmksa_cache *pmksa;
+
+ pmksa = os_zalloc(sizeof(*pmksa));
+ if (pmksa) {
+ pmksa->free_cb = free_cb;
+ pmksa->ctx = ctx;
+ }
+
+ return pmksa;
+}
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
new file mode 100644
index 0000000..9628b13
--- /dev/null
+++ b/src/ap/pmksa_cache_auth.h
@@ -0,0 +1,64 @@
+/*
+ * hostapd - PMKSA cache for IEEE 802.11i RSN
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PMKSA_CACHE_H
+#define PMKSA_CACHE_H
+
+#include "radius/radius.h"
+
+/**
+ * struct rsn_pmksa_cache_entry - PMKSA cache entry
+ */
+struct rsn_pmksa_cache_entry {
+ struct rsn_pmksa_cache_entry *next, *hnext;
+ u8 pmkid[PMKID_LEN];
+ u8 pmk[PMK_LEN];
+ size_t pmk_len;
+ os_time_t expiration;
+ int akmp; /* WPA_KEY_MGMT_* */
+ u8 spa[ETH_ALEN];
+
+ u8 *identity;
+ size_t identity_len;
+ struct radius_class_data radius_class;
+ u8 eap_type_authsrv;
+ int vlan_id;
+ int opportunistic;
+};
+
+struct rsn_pmksa_cache;
+
+struct rsn_pmksa_cache *
+pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx), void *ctx);
+void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
+ const u8 *spa, const u8 *pmkid);
+struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
+ struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa,
+ const u8 *pmkid);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
+ const u8 *pmk, size_t pmk_len,
+ const u8 *aa, const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
+ const struct rsn_pmksa_cache_entry *old_entry,
+ const u8 *aa, const u8 *pmkid);
+void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
+ struct eapol_state_machine *eapol);
+
+#endif /* PMKSA_CACHE_H */
diff --git a/src/ap/preauth_auth.c b/src/ap/preauth_auth.c
new file mode 100644
index 0000000..8e13315
--- /dev/null
+++ b/src/ap/preauth_auth.c
@@ -0,0 +1,279 @@
+/*
+ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#ifdef CONFIG_RSN_PREAUTH
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "common/wpa_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_1x.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+
+#ifndef ETH_P_PREAUTH
+#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
+#endif /* ETH_P_PREAUTH */
+
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct rsn_preauth_interface {
+ struct rsn_preauth_interface *next;
+ struct hostapd_data *hapd;
+ struct l2_packet_data *l2;
+ char *ifname;
+ int ifindex;
+};
+
+
+static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct rsn_preauth_interface *piface = ctx;
+ struct hostapd_data *hapd = piface->hapd;
+ struct ieee802_1x_hdr *hdr;
+ struct sta_info *sta;
+ struct l2_ethhdr *ethhdr;
+
+ wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet "
+ "from interface '%s'", piface->ifname);
+ if (len < sizeof(*ethhdr) + sizeof(*hdr)) {
+ wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet "
+ "(len=%lu)", (unsigned long) len);
+ return;
+ }
+
+ ethhdr = (struct l2_ethhdr *) buf;
+ hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+
+ if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address "
+ MACSTR, MAC2STR(ethhdr->h_dest));
+ return;
+ }
+
+ sta = ap_get_sta(hapd, ethhdr->h_source);
+ if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association "
+ "STA " MACSTR, MAC2STR(sta->addr));
+ return;
+ }
+ if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) {
+ sta = ap_sta_add(hapd, ethhdr->h_source);
+ if (sta == NULL)
+ return;
+ sta->flags = WLAN_STA_PREAUTH;
+
+ ieee802_1x_new_station(hapd, sta);
+ if (sta->eapol_sm == NULL) {
+ ap_free_sta(hapd, sta);
+ sta = NULL;
+ } else {
+ sta->eapol_sm->radius_identifier = -1;
+ sta->eapol_sm->portValid = TRUE;
+ sta->eapol_sm->flags |= EAPOL_SM_PREAUTH;
+ }
+ }
+ if (sta == NULL)
+ return;
+ sta->preauth_iface = piface;
+ ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1),
+ len - sizeof(*ethhdr));
+}
+
+
+static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname)
+{
+ struct rsn_preauth_interface *piface;
+
+ wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname);
+
+ piface = os_zalloc(sizeof(*piface));
+ if (piface == NULL)
+ return -1;
+ piface->hapd = hapd;
+
+ piface->ifname = os_strdup(ifname);
+ if (piface->ifname == NULL) {
+ goto fail1;
+ }
+
+ piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH,
+ rsn_preauth_receive, piface, 1);
+ if (piface->l2 == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to open register layer 2 access "
+ "to ETH_P_PREAUTH");
+ goto fail2;
+ }
+
+ piface->next = hapd->preauth_iface;
+ hapd->preauth_iface = piface;
+ return 0;
+
+fail2:
+ os_free(piface->ifname);
+fail1:
+ os_free(piface);
+ return -1;
+}
+
+
+void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+ struct rsn_preauth_interface *piface, *prev;
+
+ piface = hapd->preauth_iface;
+ hapd->preauth_iface = NULL;
+ while (piface) {
+ prev = piface;
+ piface = piface->next;
+ l2_packet_deinit(prev->l2);
+ os_free(prev->ifname);
+ os_free(prev);
+ }
+}
+
+
+int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+ char *tmp, *start, *end;
+
+ if (hapd->conf->rsn_preauth_interfaces == NULL)
+ return 0;
+
+ tmp = os_strdup(hapd->conf->rsn_preauth_interfaces);
+ if (tmp == NULL)
+ return -1;
+ start = tmp;
+ for (;;) {
+ while (*start == ' ')
+ start++;
+ if (*start == '\0')
+ break;
+ end = os_strchr(start, ' ');
+ if (end)
+ *end = '\0';
+
+ if (rsn_preauth_iface_add(hapd, start)) {
+ rsn_preauth_iface_deinit(hapd);
+ os_free(tmp);
+ return -1;
+ }
+
+ if (end)
+ start = end + 1;
+ else
+ break;
+ }
+ os_free(tmp);
+ return 0;
+}
+
+
+static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+ wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for "
+ MACSTR, MAC2STR(sta->addr));
+ ap_free_sta(hapd, sta);
+}
+
+
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+ int success)
+{
+ const u8 *key;
+ size_t len;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_INFO, "pre-authentication %s",
+ success ? "succeeded" : "failed");
+
+ key = ieee802_1x_get_key(sta->eapol_sm, &len);
+ if (len > PMK_LEN)
+ len = PMK_LEN;
+ if (success && key) {
+ if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len,
+ sta->addr,
+ dot11RSNAConfigPMKLifetime,
+ sta->eapol_sm) == 0) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "added PMKSA cache entry (pre-auth)");
+ } else {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+ HOSTAPD_LEVEL_DEBUG,
+ "failed to add PMKSA cache entry "
+ "(pre-auth)");
+ }
+ }
+
+ /*
+ * Finish STA entry removal from timeout in order to avoid freeing
+ * STA data before the caller has finished processing.
+ */
+ eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta);
+}
+
+
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *buf, size_t len)
+{
+ struct rsn_preauth_interface *piface;
+ struct l2_ethhdr *ethhdr;
+
+ piface = hapd->preauth_iface;
+ while (piface) {
+ if (piface == sta->preauth_iface)
+ break;
+ piface = piface->next;
+ }
+
+ if (piface == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication "
+ "interface for " MACSTR, MAC2STR(sta->addr));
+ return;
+ }
+
+ ethhdr = os_malloc(sizeof(*ethhdr) + len);
+ if (ethhdr == NULL)
+ return;
+
+ os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN);
+ os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN);
+ ethhdr->h_proto = host_to_be16(ETH_P_PREAUTH);
+ os_memcpy(ethhdr + 1, buf, len);
+
+ if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr,
+ sizeof(*ethhdr) + len) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to send preauth packet using "
+ "l2_packet_send\n");
+ }
+ os_free(ethhdr);
+}
+
+
+void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta);
+}
+
+#endif /* CONFIG_RSN_PREAUTH */
diff --git a/src/ap/preauth_auth.h b/src/ap/preauth_auth.h
new file mode 100644
index 0000000..5348bee
--- /dev/null
+++ b/src/ap/preauth_auth.h
@@ -0,0 +1,58 @@
+/*
+ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PREAUTH_H
+#define PREAUTH_H
+
+#ifdef CONFIG_RSN_PREAUTH
+
+int rsn_preauth_iface_init(struct hostapd_data *hapd);
+void rsn_preauth_iface_deinit(struct hostapd_data *hapd);
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+ int success);
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 *buf, size_t len);
+void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta);
+
+#else /* CONFIG_RSN_PREAUTH */
+
+static inline int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline void rsn_preauth_finished(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ int success)
+{
+}
+
+static inline void rsn_preauth_send(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ u8 *buf, size_t len)
+{
+}
+
+static inline void rsn_preauth_free_station(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_RSN_PREAUTH */
+
+#endif /* PREAUTH_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
new file mode 100644
index 0000000..e829447
--- /dev/null
+++ b/src/ap/sta_info.c
@@ -0,0 +1,796 @@
+/*
+ * hostapd / Station table
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "drivers/driver.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "accounting.h"
+#include "ieee802_1x.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+#include "ap_config.h"
+#include "beacon.h"
+#include "ap_mlme.h"
+#include "vlan_init.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+
+static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
+ struct sta_info *sta);
+static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx);
+#ifdef CONFIG_IEEE80211W
+static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
+#endif /* CONFIG_IEEE80211W */
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+ int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+ void *ctx),
+ void *ctx)
+{
+ struct sta_info *sta;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (cb(hapd, sta, ctx))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+{
+ struct sta_info *s;
+
+ s = hapd->sta_hash[STA_HASH(sta)];
+ while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+ s = s->hnext;
+ return s;
+}
+
+
+static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct sta_info *tmp;
+
+ if (hapd->sta_list == sta) {
+ hapd->sta_list = sta->next;
+ return;
+ }
+
+ tmp = hapd->sta_list;
+ while (tmp != NULL && tmp->next != sta)
+ tmp = tmp->next;
+ if (tmp == NULL) {
+ wpa_printf(MSG_DEBUG, "Could not remove STA " MACSTR " from "
+ "list.", MAC2STR(sta->addr));
+ } else
+ tmp->next = sta->next;
+}
+
+
+void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)];
+ hapd->sta_hash[STA_HASH(sta->addr)] = sta;
+}
+
+
+static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct sta_info *s;
+
+ s = hapd->sta_hash[STA_HASH(sta->addr)];
+ if (s == NULL) return;
+ if (os_memcmp(s->addr, sta->addr, 6) == 0) {
+ hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext;
+ return;
+ }
+
+ while (s->hnext != NULL &&
+ os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
+ s = s->hnext;
+ if (s->hnext != NULL)
+ s->hnext = s->hnext->hnext;
+ else
+ wpa_printf(MSG_DEBUG, "AP: could not remove STA " MACSTR
+ " from hash table", MAC2STR(sta->addr));
+}
+
+
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int set_beacon = 0;
+
+ accounting_sta_stop(hapd, sta);
+
+ /* just in case */
+ ap_sta_set_authorized(hapd, sta, 0);
+
+ if (sta->flags & WLAN_STA_WDS)
+ hostapd_set_wds_sta(hapd, 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);
+
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+
+ ieee802_1x_free_station(sta);
+ wpa_auth_sta_deinit(sta->wpa_sm);
+ rsn_preauth_free_station(hapd, sta);
+#ifndef CONFIG_NO_RADIUS
+ radius_client_flush_auth(hapd->radius, sta->addr);
+#endif /* CONFIG_NO_RADIUS */
+
+ os_free(sta->last_assoc_req);
+ os_free(sta->challenge);
+
+#ifdef CONFIG_IEEE80211W
+ os_free(sta->sa_query_trans_id);
+ eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_P2P
+ p2p_group_notif_disassoc(hapd->p2p_group, sta->addr);
+#endif /* CONFIG_P2P */
+
+ wpabuf_free(sta->wps_ie);
+ wpabuf_free(sta->p2p_ie);
+
+ os_free(sta->ht_capabilities);
+
+ os_free(sta);
+}
+
+
+void hostapd_free_stas(struct hostapd_data *hapd)
+{
+ struct sta_info *sta, *prev;
+
+ sta = hapd->sta_list;
+
+ while (sta) {
+ prev = sta;
+ if (sta->flags & WLAN_STA_AUTH) {
+ mlme_deauthenticate_indication(
+ hapd, sta, WLAN_REASON_UNSPECIFIED);
+ }
+ sta = sta->next;
+ wpa_printf(MSG_DEBUG, "Removing station " MACSTR,
+ MAC2STR(prev->addr));
+ ap_free_sta(hapd, prev);
+ }
+}
+
+
+/**
+ * ap_handle_timer - Per STA timer handler
+ * @eloop_ctx: struct hostapd_data *
+ * @timeout_ctx: struct sta_info *
+ *
+ * This function is called to check station activity and to remove inactive
+ * stations.
+ */
+void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+ unsigned long next_time = 0;
+
+ if (sta->timeout_next == STA_REMOVE) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ "local deauth request");
+ ap_free_sta(hapd, sta);
+ return;
+ }
+
+ if ((sta->flags & WLAN_STA_ASSOC) &&
+ (sta->timeout_next == STA_NULLFUNC ||
+ sta->timeout_next == STA_DISASSOC)) {
+ int inactive_sec;
+ inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr);
+ if (inactive_sec == -1) {
+ wpa_msg(hapd, MSG_DEBUG, "Check inactivity: Could not "
+ "get station info rom kernel driver for "
+ MACSTR, MAC2STR(sta->addr));
+ } else if (inactive_sec < hapd->conf->ap_max_inactivity &&
+ sta->flags & WLAN_STA_ASSOC) {
+ /* station activity detected; reset timeout state */
+ wpa_msg(hapd, 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 -
+ inactive_sec;
+ } else {
+ wpa_msg(hapd, 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 ((sta->flags & WLAN_STA_ASSOC) &&
+ sta->timeout_next == STA_DISASSOC &&
+ !(sta->flags & WLAN_STA_PENDING_POLL)) {
+ wpa_msg(hapd, 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) {
+ eloop_register_timeout(next_time, 0, ap_handle_timer, hapd,
+ sta);
+ return;
+ }
+
+ if (sta->timeout_next == STA_NULLFUNC &&
+ (sta->flags & WLAN_STA_ASSOC)) {
+#ifndef CONFIG_NATIVE_WINDOWS
+ /* send data frame to poll STA and check whether this frame
+ * is ACKed */
+ struct ieee80211_hdr hdr;
+
+ wpa_printf(MSG_DEBUG, " Polling STA with data frame");
+ sta->flags |= WLAN_STA_PENDING_POLL;
+
+ os_memset(&hdr, 0, sizeof(hdr));
+ if (hapd->driver &&
+ os_strcmp(hapd->driver->name, "hostap") == 0) {
+ /*
+ * WLAN_FC_STYPE_NULLFUNC would be more appropriate,
+ * but it is apparently not retried so TX Exc events
+ * are not received for it.
+ */
+ hdr.frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA,
+ WLAN_FC_STYPE_DATA);
+ } else {
+ hdr.frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA,
+ WLAN_FC_STYPE_NULLFUNC);
+ }
+
+ hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
+ os_memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN);
+ os_memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr,
+ ETH_ALEN);
+ os_memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN);
+
+ if (hostapd_drv_send_mlme(hapd, &hdr, sizeof(hdr)) < 0)
+ perror("ap_handle_timer: send");
+#endif /* CONFIG_NATIVE_WINDOWS */
+ } else if (sta->timeout_next != STA_REMOVE) {
+ int deauth = sta->timeout_next == STA_DEAUTH;
+
+ wpa_printf(MSG_DEBUG, "Sending %s info to STA " MACSTR,
+ deauth ? "deauthentication" : "disassociation",
+ MAC2STR(sta->addr));
+
+ if (deauth) {
+ hostapd_drv_sta_deauth(
+ hapd, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ } else {
+ hostapd_drv_sta_disassoc(
+ hapd, sta->addr,
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+ }
+ }
+
+ switch (sta->timeout_next) {
+ case STA_NULLFUNC:
+ sta->timeout_next = STA_DISASSOC;
+ eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer,
+ hapd, sta);
+ break;
+ case STA_DISASSOC:
+ sta->flags &= ~WLAN_STA_ASSOC;
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ if (!sta->acct_terminate_cause)
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+ accounting_sta_stop(hapd, sta);
+ ieee802_1x_free_station(sta);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "disassociated due to "
+ "inactivity");
+ sta->timeout_next = STA_DEAUTH;
+ eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
+ hapd, sta);
+ mlme_disassociate_indication(
+ hapd, sta, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+ break;
+ case STA_DEAUTH:
+ case STA_REMOVE:
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ "inactivity");
+ if (!sta->acct_terminate_cause)
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+ mlme_deauthenticate_indication(
+ hapd, sta,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ ap_free_sta(hapd, sta);
+ break;
+ }
+}
+
+
+static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+ u8 addr[ETH_ALEN];
+
+ if (!(sta->flags & WLAN_STA_AUTH))
+ return;
+
+ mlme_deauthenticate_indication(hapd, sta,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+ "session timeout");
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
+ os_memcpy(addr, sta->addr, ETH_ALEN);
+ ap_free_sta(hapd, sta);
+ hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+}
+
+
+void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+ u32 session_timeout)
+{
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d "
+ "seconds", session_timeout);
+ eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+ eloop_register_timeout(session_timeout, 0, ap_handle_session_timer,
+ hapd, sta);
+}
+
+
+void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+}
+
+
+struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ return sta;
+
+ wpa_printf(MSG_DEBUG, " New STA");
+ if (hapd->num_sta >= hapd->conf->max_num_sta) {
+ /* FIX: might try to remove some old STAs first? */
+ wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)",
+ hapd->num_sta, hapd->conf->max_num_sta);
+ return NULL;
+ }
+
+ sta = os_zalloc(sizeof(struct sta_info));
+ if (sta == NULL) {
+ wpa_printf(MSG_ERROR, "malloc failed");
+ return NULL;
+ }
+ sta->acct_interim_interval = hapd->conf->acct_interim_interval;
+
+ /* initialize STA info data */
+ eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+ ap_handle_timer, hapd, sta);
+ os_memcpy(sta->addr, addr, ETH_ALEN);
+ sta->next = hapd->sta_list;
+ hapd->sta_list = sta;
+ hapd->num_sta++;
+ ap_sta_hash_add(hapd, sta);
+ sta->ssid = &hapd->conf->ssid;
+ ap_sta_remove_in_other_bss(hapd, sta);
+
+ return sta;
+}
+
+
+static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+
+ wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
+ MAC2STR(sta->addr));
+ if (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);
+ }
+}
+
+
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason)
+{
+ wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ sta->flags &= ~WLAN_STA_ASSOC;
+ ap_sta_remove(hapd, sta);
+ sta->timeout_next = STA_DEAUTH;
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
+ ap_handle_timer, hapd, sta);
+ accounting_sta_stop(hapd, sta);
+ ieee802_1x_free_station(sta);
+
+ mlme_disassociate_indication(hapd, sta, reason);
+}
+
+
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason)
+{
+ wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ ap_sta_remove(hapd, sta);
+ sta->timeout_next = STA_REMOVE;
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+ ap_handle_timer, hapd, sta);
+ accounting_sta_stop(hapd, sta);
+ ieee802_1x_free_station(sta);
+
+ mlme_deauthenticate_indication(hapd, sta, reason);
+}
+
+
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+ int old_vlanid)
+{
+#ifndef CONFIG_NO_VLAN
+ const char *iface;
+ struct hostapd_vlan *vlan = NULL;
+ int ret;
+
+ /*
+ * Do not proceed furthur if the vlan id remains same. We do not want
+ * duplicate dynamic vlan entries.
+ */
+ if (sta->vlan_id == old_vlanid)
+ return 0;
+
+ /*
+ * During 1x reauth, if the vlan id changes, then remove the old id and
+ * proceed furthur to add the new one.
+ */
+ if (old_vlanid > 0)
+ vlan_remove_dynamic(hapd, old_vlanid);
+
+ iface = hapd->conf->iface;
+ if (sta->ssid->vlan[0])
+ iface = sta->ssid->vlan;
+
+ if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+ sta->vlan_id = 0;
+ else if (sta->vlan_id > 0) {
+ vlan = hapd->conf->vlan;
+ while (vlan) {
+ if (vlan->vlan_id == sta->vlan_id ||
+ vlan->vlan_id == VLAN_ID_WILDCARD) {
+ iface = vlan->ifname;
+ break;
+ }
+ vlan = vlan->next;
+ }
+ }
+
+ if (sta->vlan_id > 0 && vlan == NULL) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "could not find VLAN for "
+ "binding station to (vlan_id=%d)",
+ sta->vlan_id);
+ return -1;
+ } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) {
+ vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id);
+ if (vlan == NULL) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "could not add "
+ "dynamic VLAN interface for vlan_id=%d",
+ sta->vlan_id);
+ return -1;
+ }
+
+ iface = vlan->ifname;
+ if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "could not "
+ "configure encryption for dynamic VLAN "
+ "interface for vlan_id=%d",
+ sta->vlan_id);
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN "
+ "interface '%s'", iface);
+ } else if (vlan && vlan->vlan_id == sta->vlan_id) {
+ if (sta->vlan_id > 0) {
+ vlan->dynamic_vlan++;
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "updated existing "
+ "dynamic VLAN interface '%s'", iface);
+ }
+
+ /*
+ * Update encryption configuration for statically generated
+ * VLAN interface. This is only used for static WEP
+ * configuration for the case where hostapd did not yet know
+ * which keys are to be used when the interface was added.
+ */
+ if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "could not "
+ "configure encryption for VLAN "
+ "interface for vlan_id=%d",
+ sta->vlan_id);
+ }
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "binding station to interface "
+ "'%s'", iface);
+
+ if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0)
+ wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA");
+
+ ret = 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(sta->sa_query_trans_id,
+ (sta->sa_query_count + 1) * WLAN_SA_QUERY_TR_ID_LEN);
+ if (nbuf == NULL)
+ return;
+ if (sta->sa_query_count == 0) {
+ /* Starting a new SA Query procedure */
+ os_get_time(&sta->sa_query_start);
+ }
+ trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
+ sta->sa_query_trans_id = nbuf;
+ sta->sa_query_count++;
+
+ os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ timeout = hapd->conf->assoc_sa_query_retry_timeout;
+ sec = ((timeout / 1000) * 1024) / 1000;
+ usec = (timeout % 1000) * 1024;
+ eloop_register_timeout(sec, usec, ap_sa_query_timer, hapd, sta);
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "association SA Query attempt %d", sta->sa_query_count);
+
+#ifdef NEED_AP_MLME
+ ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id);
+#endif /* NEED_AP_MLME */
+}
+
+
+void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ ap_sa_query_timer(hapd, sta);
+}
+
+
+void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+ os_free(sta->sa_query_trans_id);
+ sta->sa_query_trans_id = NULL;
+ sta->sa_query_count = 0;
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
+void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized)
+{
+ if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
+ return;
+
+ if (authorized)
+ sta->flags |= WLAN_STA_AUTHORIZED;
+ else
+ sta->flags &= ~WLAN_STA_AUTHORIZED;
+
+ if (hapd->sta_authorized_cb)
+ hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
+ sta->addr, authorized);
+}
+
+
+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);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta);
+ sta->timeout_next = STA_REMOVE;
+}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
new file mode 100644
index 0000000..9ec4fe3
--- /dev/null
+++ b/src/ap/sta_info.h
@@ -0,0 +1,165 @@
+/*
+ * hostapd / Station table
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef STA_INFO_H
+#define STA_INFO_H
+
+/* STA flags */
+#define WLAN_STA_AUTH BIT(0)
+#define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_PS BIT(2)
+#define WLAN_STA_TIM BIT(3)
+#define WLAN_STA_PERM BIT(4)
+#define WLAN_STA_AUTHORIZED BIT(5)
+#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
+#define WLAN_STA_SHORT_PREAMBLE BIT(7)
+#define WLAN_STA_PREAUTH BIT(8)
+#define WLAN_STA_WMM BIT(9)
+#define WLAN_STA_MFP BIT(10)
+#define WLAN_STA_HT BIT(11)
+#define WLAN_STA_WPS BIT(12)
+#define WLAN_STA_MAYBE_WPS BIT(13)
+#define WLAN_STA_WDS BIT(14)
+#define WLAN_STA_ASSOC_REQ_OK BIT(15)
+#define WLAN_STA_NONERP BIT(31)
+
+/* Maximum number of supported rates (from both Supported Rates and Extended
+ * Supported Rates IEs). */
+#define WLAN_SUPP_RATES_MAX 32
+
+
+struct sta_info {
+ struct sta_info *next; /* next entry in sta list */
+ struct sta_info *hnext; /* next entry in hash table list */
+ u8 addr[6];
+ u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+ u32 flags; /* Bitfield of WLAN_STA_* */
+ u16 capability;
+ u16 listen_interval; /* or beacon_int for APs */
+ u8 supported_rates[WLAN_SUPP_RATES_MAX];
+ int supported_rates_len;
+
+ unsigned int nonerp_set:1;
+ unsigned int no_short_slot_time_set:1;
+ unsigned int no_short_preamble_set:1;
+ unsigned int no_ht_gf_set:1;
+ unsigned int no_ht_set:1;
+ unsigned int ht_20mhz_set:1;
+ unsigned int no_p2p_set:1;
+
+ u16 auth_alg;
+ u8 previous_ap[6];
+
+ enum {
+ STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE
+ } timeout_next;
+
+ /* IEEE 802.1X related data */
+ struct eapol_state_machine *eapol_sm;
+
+ /* IEEE 802.11f (IAPP) related data */
+ struct ieee80211_mgmt *last_assoc_req;
+
+ u32 acct_session_id_hi;
+ u32 acct_session_id_lo;
+ time_t acct_session_start;
+ int acct_session_started;
+ int acct_terminate_cause; /* Acct-Terminate-Cause */
+ int acct_interim_interval; /* Acct-Interim-Interval */
+
+ unsigned long last_rx_bytes;
+ unsigned long last_tx_bytes;
+ u32 acct_input_gigawords; /* Acct-Input-Gigawords */
+ u32 acct_output_gigawords; /* Acct-Output-Gigawords */
+
+ u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
+
+ struct wpa_state_machine *wpa_sm;
+ struct rsn_preauth_interface *preauth_iface;
+
+ struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */
+ struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */
+
+ int vlan_id;
+
+ struct ieee80211_ht_capabilities *ht_capabilities;
+
+#ifdef CONFIG_IEEE80211W
+ int sa_query_count; /* number of pending SA Query requests;
+ * 0 = no SA Query in progress */
+ int sa_query_timed_out;
+ u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
+ * sa_query_count octets of pending SA Query
+ * transaction identifiers */
+ struct os_time sa_query_start;
+#endif /* CONFIG_IEEE80211W */
+
+ struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
+ struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
+};
+
+
+/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has
+ * passed since last received frame from the station, a nullfunc data frame is
+ * sent to the station. If this frame is not acknowledged and no other frames
+ * have been received, the station will be disassociated after
+ * AP_DISASSOC_DELAY seconds. Similarily, the station will be deauthenticated
+ * after AP_DEAUTH_DELAY seconds has passed after disassociation. */
+#define AP_MAX_INACTIVITY (5 * 60)
+#define AP_DISASSOC_DELAY (1)
+#define AP_DEAUTH_DELAY (1)
+/* Number of seconds to keep STA entry with Authenticated flag after it has
+ * been disassociated. */
+#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30)
+/* Number of seconds to keep STA entry after it has been deauthenticated. */
+#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
+
+
+struct hostapd_data;
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+ int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+ void *ctx),
+ void *ctx);
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
+void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
+void hostapd_free_stas(struct hostapd_data *hapd);
+void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
+void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+ u32 session_timeout);
+void ap_sta_no_session_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta);
+struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr);
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason);
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason);
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+ int old_vlanid);
+void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *addr, u16 reason);
+
+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;
+}
+
+#endif /* STA_INFO_H */
diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c
new file mode 100644
index 0000000..1925217
--- /dev/null
+++ b/src/ap/tkip_countermeasures.c
@@ -0,0 +1,94 @@
+/*
+ * hostapd / TKIP countermeasures
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_mlme.h"
+#include "wpa_auth.h"
+#include "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);
+ for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ }
+}
+
+
+void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
+{
+ time_t now;
+
+ if (addr && local) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ if (sta != NULL) {
+ wpa_auth_sta_local_mic_failure_report(sta->wpa_sm);
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Michael MIC failure detected in "
+ "received frame");
+ mlme_michaelmicfailure_indication(hapd, addr);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "MLME-MICHAELMICFAILURE.indication "
+ "for not associated STA (" MACSTR
+ ") ignored", MAC2STR(addr));
+ return;
+ }
+ }
+
+ time(&now);
+ if (now > hapd->michael_mic_failure + 60) {
+ hapd->michael_mic_failures = 1;
+ } else {
+ hapd->michael_mic_failures++;
+ if (hapd->michael_mic_failures > 1)
+ ieee80211_tkip_countermeasures_start(hapd);
+ }
+ hapd->michael_mic_failure = now;
+}
diff --git a/src/ap/tkip_countermeasures.h b/src/ap/tkip_countermeasures.h
new file mode 100644
index 0000000..5a1afce
--- /dev/null
+++ b/src/ap/tkip_countermeasures.h
@@ -0,0 +1,20 @@
+/*
+ * hostapd / TKIP countermeasures
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef TKIP_COUNTERMEASURES_H
+#define TKIP_COUNTERMEASURES_H
+
+void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
+
+#endif /* TKIP_COUNTERMEASURES_H */
diff --git a/src/ap/utils.c b/src/ap/utils.c
new file mode 100644
index 0000000..0ff48ae
--- /dev/null
+++ b/src/ap/utils.c
@@ -0,0 +1,88 @@
+/*
+ * AP mode helper functions
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "sta_info.h"
+#include "hostapd.h"
+
+
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+ int (*cb)(void *ctx, const u8 *sa,
+ const u8 *ie, size_t ie_len),
+ void *ctx)
+{
+ struct hostapd_probereq_cb *n;
+
+ n = os_realloc(hapd->probereq_cb, (hapd->num_probereq_cb + 1) *
+ sizeof(struct hostapd_probereq_cb));
+ if (n == NULL)
+ return -1;
+
+ hapd->probereq_cb = n;
+ n = &hapd->probereq_cb[hapd->num_probereq_cb];
+ hapd->num_probereq_cb++;
+
+ n->cb = cb;
+ n->ctx = ctx;
+
+ return 0;
+}
+
+
+struct prune_data {
+ struct hostapd_data *hapd;
+ const u8 *addr;
+};
+
+static int prune_associations(struct hostapd_iface *iface, void *ctx)
+{
+ struct prune_data *data = ctx;
+ struct sta_info *osta;
+ struct hostapd_data *ohapd;
+ size_t j;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ ohapd = iface->bss[j];
+ if (ohapd == data->hapd)
+ continue;
+ osta = ap_get_sta(ohapd, data->addr);
+ if (!osta)
+ continue;
+
+ ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED);
+ }
+
+ return 0;
+}
+
+/**
+ * hostapd_prune_associations - Remove extraneous associations
+ * @hapd: Pointer to BSS data for the most recent association
+ * @addr: Associated STA address
+ *
+ * This function looks through all radios and BSS's for previous
+ * (stale) associations of STA. If any are found they are removed.
+ */
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct prune_data data;
+ data.hapd = hapd;
+ data.addr = addr;
+ if (hapd->iface->for_each_interface)
+ hapd->iface->for_each_interface(hapd->iface->interfaces,
+ prune_associations, &data);
+}
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
new file mode 100644
index 0000000..f2f766f
--- /dev/null
+++ b/src/ap/vlan_init.c
@@ -0,0 +1,905 @@
+/*
+ * hostapd / VLAN initialization
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "vlan_init.h"
+
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <linux/if_vlan.h>
+#include <linux/if_bridge.h>
+
+#include "drivers/priv_netlink.h"
+#include "utils/eloop.h"
+
+
+struct full_dynamic_vlan {
+ int s; /* socket on which to listen for new/removed interfaces. */
+};
+
+
+static int ifconfig_helper(const char *if_name, int up)
+{
+ int fd;
+ struct ifreq ifr;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
+ "for interface %s: %s",
+ __func__, if_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (up)
+ ifr.ifr_flags |= IFF_UP;
+ else
+ ifr.ifr_flags &= ~IFF_UP;
+
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
+ "for interface %s (up=%d): %s",
+ __func__, if_name, up, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+static int ifconfig_up(const char *if_name)
+{
+ wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
+ return ifconfig_helper(if_name, 1);
+}
+
+
+static int ifconfig_down(const char *if_name)
+{
+ wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
+ return ifconfig_helper(if_name, 0);
+}
+
+
+/*
+ * These are only available in recent linux headers (without the leading
+ * underscore).
+ */
+#define _GET_VLAN_REALDEV_NAME_CMD 8
+#define _GET_VLAN_VID_CMD 9
+
+/* This value should be 256 ONLY. If it is something else, then hostapd
+ * might crash!, as this value has been hard-coded in 2.4.x kernel
+ * bridging code.
+ */
+#define MAX_BR_PORTS 256
+
+static int br_delif(const char *br_name, const char *if_name)
+{
+ int fd;
+ struct ifreq ifr;
+ unsigned long args[2];
+ int if_index;
+
+ wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ if_index = if_nametoindex(if_name);
+
+ if (if_index == 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+ "interface index for '%s'",
+ __func__, if_name);
+ close(fd);
+ return -1;
+ }
+
+ args[0] = BRCTL_DEL_IF;
+ args[1] = if_index;
+
+ os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (__caddr_t) args;
+
+ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
+ /* No error if interface already removed. */
+ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+ "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
+ "%s", __func__, br_name, if_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+/*
+ Add interface 'if_name' to the bridge 'br_name'
+
+ returns -1 on error
+ returns 1 if the interface is already part of the bridge
+ returns 0 otherwise
+*/
+static int br_addif(const char *br_name, const char *if_name)
+{
+ int fd;
+ struct ifreq ifr;
+ unsigned long args[2];
+ int if_index;
+
+ wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ if_index = if_nametoindex(if_name);
+
+ if (if_index == 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+ "interface index for '%s'",
+ __func__, if_name);
+ close(fd);
+ return -1;
+ }
+
+ args[0] = BRCTL_ADD_IF;
+ args[1] = if_index;
+
+ os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (__caddr_t) args;
+
+ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+ if (errno == EBUSY) {
+ /* The interface is already added. */
+ close(fd);
+ return 1;
+ }
+
+ wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+ "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
+ "%s", __func__, br_name, if_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+static int br_delbr(const char *br_name)
+{
+ int fd;
+ unsigned long arg[2];
+
+ wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ arg[0] = BRCTL_DEL_BRIDGE;
+ arg[1] = (unsigned long) br_name;
+
+ if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
+ /* No error if bridge already removed. */
+ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
+ "%s: %s", __func__, br_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+/*
+ Add a bridge with the name 'br_name'.
+
+ returns -1 on error
+ returns 1 if the bridge already exists
+ returns 0 otherwise
+*/
+static int br_addbr(const char *br_name)
+{
+ int fd;
+ unsigned long arg[4];
+ struct ifreq ifr;
+
+ wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ arg[0] = BRCTL_ADD_BRIDGE;
+ arg[1] = (unsigned long) br_name;
+
+ if (ioctl(fd, SIOCGIFBR, arg) < 0) {
+ if (errno == EEXIST) {
+ /* The bridge is already added. */
+ close(fd);
+ return 1;
+ } else {
+ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
+ "failed for %s: %s",
+ __func__, br_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ }
+
+ /* Decrease forwarding delay to avoid EAPOL timeouts. */
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
+ arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
+ arg[1] = 1;
+ arg[2] = 0;
+ arg[3] = 0;
+ ifr.ifr_data = (char *) &arg;
+ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: "
+ "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
+ "%s: %s", __func__, br_name, strerror(errno));
+ /* Continue anyway */
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+static int br_getnumports(const char *br_name)
+{
+ int fd;
+ int i;
+ int port_cnt = 0;
+ unsigned long arg[4];
+ int ifindices[MAX_BR_PORTS];
+ struct ifreq ifr;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ arg[0] = BRCTL_GET_PORT_LIST;
+ arg[1] = (unsigned long) ifindices;
+ arg[2] = MAX_BR_PORTS;
+ arg[3] = 0;
+
+ os_memset(ifindices, 0, sizeof(ifindices));
+ os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (__caddr_t) arg;
+
+ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
+ "failed for %s: %s",
+ __func__, br_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ for (i = 1; i < MAX_BR_PORTS; i++) {
+ if (ifindices[i] > 0) {
+ port_cnt++;
+ }
+ }
+
+ close(fd);
+ return port_cnt;
+}
+
+
+static int vlan_rem(const char *if_name)
+{
+ int fd;
+ struct vlan_ioctl_args if_request;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
+ if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+ wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+ if_name);
+ return -1;
+ }
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ os_memset(&if_request, 0, sizeof(if_request));
+
+ os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+ if_request.cmd = DEL_VLAN_CMD;
+
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
+ "%s", __func__, if_name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+/*
+ Add a vlan interface with VLAN ID 'vid' and tagged interface
+ 'if_name'.
+
+ returns -1 on error
+ returns 1 if the interface already exists
+ returns 0 otherwise
+*/
+static int vlan_add(const char *if_name, int vid)
+{
+ int fd;
+ struct vlan_ioctl_args if_request;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
+ if_name, vid);
+ ifconfig_up(if_name);
+
+ if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+ wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+ if_name);
+ return -1;
+ }
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ os_memset(&if_request, 0, sizeof(if_request));
+
+ /* Determine if a suitable vlan device already exists. */
+
+ os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
+ vid);
+
+ if_request.cmd = _GET_VLAN_VID_CMD;
+
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) {
+
+ if (if_request.u.VID == vid) {
+ if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD;
+
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
+ os_strncmp(if_request.u.device2, if_name,
+ sizeof(if_request.u.device2)) == 0) {
+ close(fd);
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_add: "
+ "if_name %s exists already",
+ if_request.device1);
+ return 1;
+ }
+ }
+ }
+
+ /* A suitable vlan device does not already exist, add one. */
+
+ os_memset(&if_request, 0, sizeof(if_request));
+ os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+ if_request.u.VID = vid;
+ if_request.cmd = ADD_VLAN_CMD;
+
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: "
+ "%s",
+ __func__, if_request.device1, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+static int vlan_set_name_type(unsigned int name_type)
+{
+ int fd;
+ struct vlan_ioctl_args if_request;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
+ name_type);
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+ "failed: %s", __func__, strerror(errno));
+ return -1;
+ }
+
+ os_memset(&if_request, 0, sizeof(if_request));
+
+ if_request.u.name_type = name_type;
+ if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD "
+ "name_type=%u failed: %s",
+ __func__, name_type, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
+{
+ char vlan_ifname[IFNAMSIZ];
+ char br_name[IFNAMSIZ];
+ struct hostapd_vlan *vlan = hapd->conf->vlan;
+ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
+
+ while (vlan) {
+ if (os_strcmp(ifname, vlan->ifname) == 0) {
+
+ os_snprintf(br_name, sizeof(br_name), "brvlan%d",
+ vlan->vlan_id);
+
+ if (!br_addbr(br_name))
+ vlan->clean |= DVLAN_CLEAN_BR;
+
+ ifconfig_up(br_name);
+
+ if (tagged_interface) {
+
+ if (!vlan_add(tagged_interface, vlan->vlan_id))
+ vlan->clean |= DVLAN_CLEAN_VLAN;
+
+ os_snprintf(vlan_ifname, sizeof(vlan_ifname),
+ "vlan%d", vlan->vlan_id);
+
+ if (!br_addif(br_name, vlan_ifname))
+ vlan->clean |= DVLAN_CLEAN_VLAN_PORT;
+
+ ifconfig_up(vlan_ifname);
+ }
+
+ if (!br_addif(br_name, ifname))
+ vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+
+ ifconfig_up(ifname);
+
+ break;
+ }
+ vlan = vlan->next;
+ }
+}
+
+
+static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
+{
+ char vlan_ifname[IFNAMSIZ];
+ char br_name[IFNAMSIZ];
+ struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
+ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
+
+ first = prev = vlan;
+
+ while (vlan) {
+ if (os_strcmp(ifname, vlan->ifname) == 0) {
+ os_snprintf(br_name, sizeof(br_name), "brvlan%d",
+ vlan->vlan_id);
+
+ if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
+ br_delif(br_name, vlan->ifname);
+
+ if (tagged_interface) {
+ os_snprintf(vlan_ifname, sizeof(vlan_ifname),
+ "vlan%d", vlan->vlan_id);
+ if (vlan->clean & DVLAN_CLEAN_VLAN_PORT)
+ br_delif(br_name, vlan_ifname);
+ ifconfig_down(vlan_ifname);
+
+ if (vlan->clean & DVLAN_CLEAN_VLAN)
+ vlan_rem(vlan_ifname);
+ }
+
+ if ((vlan->clean & DVLAN_CLEAN_BR) &&
+ br_getnumports(br_name) == 0) {
+ ifconfig_down(br_name);
+ br_delbr(br_name);
+ }
+
+ if (vlan == first) {
+ hapd->conf->vlan = vlan->next;
+ } else {
+ prev->next = vlan->next;
+ }
+ os_free(vlan);
+
+ break;
+ }
+ prev = vlan;
+ vlan = vlan->next;
+ }
+}
+
+
+static void
+vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
+ struct hostapd_data *hapd)
+{
+ struct ifinfomsg *ifi;
+ int attrlen, nlmsg_len, rta_len;
+ struct rtattr *attr;
+
+ if (len < sizeof(*ifi))
+ return;
+
+ ifi = NLMSG_DATA(h);
+
+ nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
+
+ attrlen = h->nlmsg_len - nlmsg_len;
+ if (attrlen < 0)
+ return;
+
+ attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
+
+ rta_len = RTA_ALIGN(sizeof(struct rtattr));
+ while (RTA_OK(attr, attrlen)) {
+ char ifname[IFNAMSIZ + 1];
+
+ if (attr->rta_type == IFLA_IFNAME) {
+ int n = attr->rta_len - rta_len;
+ if (n < 0)
+ break;
+
+ os_memset(ifname, 0, sizeof(ifname));
+
+ if ((size_t) n > sizeof(ifname))
+ n = sizeof(ifname);
+ os_memcpy(ifname, ((char *) attr) + rta_len, n);
+
+ if (del)
+ vlan_dellink(ifname, hapd);
+ else
+ vlan_newlink(ifname, hapd);
+ }
+
+ attr = RTA_NEXT(attr, attrlen);
+ }
+}
+
+
+static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ char buf[8192];
+ int left;
+ struct sockaddr_nl from;
+ socklen_t fromlen;
+ struct nlmsghdr *h;
+ struct hostapd_data *hapd = eloop_ctx;
+
+ fromlen = sizeof(from);
+ left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+ (struct sockaddr *) &from, &fromlen);
+ if (left < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
+ __func__, strerror(errno));
+ return;
+ }
+
+ h = (struct nlmsghdr *) buf;
+ while (left >= (int) sizeof(*h)) {
+ int len, plen;
+
+ len = h->nlmsg_len;
+ plen = len - sizeof(*h);
+ if (len > left || plen < 0) {
+ wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
+ "message: len=%d left=%d plen=%d",
+ len, left, plen);
+ break;
+ }
+
+ switch (h->nlmsg_type) {
+ case RTM_NEWLINK:
+ vlan_read_ifnames(h, plen, 0, hapd);
+ break;
+ case RTM_DELLINK:
+ vlan_read_ifnames(h, plen, 1, hapd);
+ break;
+ }
+
+ len = NLMSG_ALIGN(len);
+ left -= len;
+ h = (struct nlmsghdr *) ((char *) h + len);
+ }
+
+ if (left > 0) {
+ wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
+ "netlink message", __func__, left);
+ }
+}
+
+
+static struct full_dynamic_vlan *
+full_dynamic_vlan_init(struct hostapd_data *hapd)
+{
+ struct sockaddr_nl local;
+ struct full_dynamic_vlan *priv;
+
+ priv = os_zalloc(sizeof(*priv));
+ if (priv == NULL)
+ return NULL;
+
+ vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
+
+ priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (priv->s < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
+ "NETLINK_ROUTE) failed: %s",
+ __func__, strerror(errno));
+ os_free(priv);
+ return NULL;
+ }
+
+ os_memset(&local, 0, sizeof(local));
+ local.nl_family = AF_NETLINK;
+ local.nl_groups = RTMGRP_LINK;
+ if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
+ __func__, strerror(errno));
+ close(priv->s);
+ os_free(priv);
+ return NULL;
+ }
+
+ if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
+ {
+ close(priv->s);
+ os_free(priv);
+ return NULL;
+ }
+
+ return priv;
+}
+
+
+static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
+{
+ if (priv == NULL)
+ return;
+ eloop_unregister_read_sock(priv->s);
+ close(priv->s);
+ os_free(priv);
+}
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+
+int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
+ struct hostapd_ssid *mssid, const char *dyn_vlan)
+{
+ int i;
+
+ if (dyn_vlan == NULL)
+ return 0;
+
+ /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own
+ * functions for setting up dynamic broadcast keys. */
+ for (i = 0; i < 4; i++) {
+ if (mssid->wep.key[i] &&
+ 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 (vlan_dynamic_add(hapd, hapd->conf->vlan))
+ return -1;
+
+ return 0;
+}
+
+
+void vlan_deinit(struct hostapd_data *hapd)
+{
+ vlan_dynamic_remove(hapd, hapd->conf->vlan);
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+}
+
+
+struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan,
+ int vlan_id)
+{
+ struct hostapd_vlan *n;
+ char *ifname, *pos;
+
+ if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
+ vlan->vlan_id != VLAN_ID_WILDCARD)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
+ __func__, vlan_id, vlan->ifname);
+ ifname = os_strdup(vlan->ifname);
+ if (ifname == NULL)
+ return NULL;
+ pos = os_strchr(ifname, '#');
+ if (pos == NULL) {
+ os_free(ifname);
+ return NULL;
+ }
+ *pos++ = '\0';
+
+ n = os_zalloc(sizeof(*n));
+ if (n == NULL) {
+ os_free(ifname);
+ return NULL;
+ }
+
+ n->vlan_id = vlan_id;
+ n->dynamic_vlan = 1;
+
+ os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
+ pos);
+ os_free(ifname);
+
+ if (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/src/ap/vlan_init.h b/src/ap/vlan_init.h
new file mode 100644
index 0000000..382d5de
--- /dev/null
+++ b/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/src/ap/wmm.c b/src/ap/wmm.c
new file mode 100644
index 0000000..a6d9b89
--- /dev/null
+++ b/src/ap/wmm.c
@@ -0,0 +1,327 @@
+/*
+ * hostapd / WMM (Wi-Fi Multimedia)
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "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)
+ 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 zero on success or non-zero on any
+ * error in WMM element. eid does not include Element ID and Length octets. */
+int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
+{
+ struct wmm_information_element *wmm;
+
+ wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len);
+
+ if (len < sizeof(struct wmm_information_element)) {
+ wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
+ (unsigned long) len);
+ return -1;
+ }
+
+ wmm = (struct wmm_information_element *) eid;
+ wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x "
+ "OUI type %d OUI sub-type %d version %d QoS info 0x%x",
+ wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type,
+ wmm->oui_subtype, wmm->version, wmm->qos_info);
+ if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
+ wmm->version != WMM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
+ const struct wmm_tspec_element *tspec,
+ u8 action_code, u8 dialogue_token, u8 status_code)
+{
+ u8 buf[256];
+ struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf;
+ struct wmm_tspec_element *t = (struct wmm_tspec_element *)
+ m->u.action.u.wmm_action.variable;
+ int len;
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "action response - reason %d", status_code);
+ os_memset(buf, 0, sizeof(buf));
+ m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(m->da, addr, ETH_ALEN);
+ os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
+ m->u.action.category = WLAN_ACTION_WMM;
+ m->u.action.u.wmm_action.action_code = action_code;
+ m->u.action.u.wmm_action.dialog_token = dialogue_token;
+ m->u.action.u.wmm_action.status_code = status_code;
+ os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
+ len = ((u8 *) (t + 1)) - buf;
+
+ if (hostapd_drv_send_mlme(hapd, m, len) < 0)
+ perror("wmm_send_action: send");
+}
+
+
+int wmm_process_tspec(struct wmm_tspec_element *tspec)
+{
+ int medium_time, pps, duration;
+ int up, psb, dir, tid;
+ u16 val, surplus;
+
+ up = (tspec->ts_info[1] >> 3) & 0x07;
+ psb = (tspec->ts_info[1] >> 2) & 0x01;
+ dir = (tspec->ts_info[0] >> 5) & 0x03;
+ tid = (tspec->ts_info[0] >> 1) & 0x0f;
+ wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d",
+ up, psb, dir, tid);
+ val = le_to_host16(tspec->nominal_msdu_size);
+ wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s",
+ val & 0x7fff, val & 0x8000 ? " (fixed)" : "");
+ wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps",
+ le_to_host32(tspec->mean_data_rate));
+ wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps",
+ le_to_host32(tspec->minimum_phy_rate));
+ val = le_to_host16(tspec->surplus_bandwidth_allowance);
+ wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u",
+ val >> 13, 10000 * (val & 0x1fff) / 0x2000);
+
+ val = le_to_host16(tspec->nominal_msdu_size);
+ if (val == 0) {
+ wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)");
+ return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+ }
+ /* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */
+ pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val;
+ wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d",
+ pps);
+
+ if (le_to_host32(tspec->minimum_phy_rate) < 1000000) {
+ wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate");
+ return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+ }
+
+ duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 /
+ (le_to_host32(tspec->minimum_phy_rate) / 1000000) +
+ 50 /* FIX: proper SIFS + ACK duration */;
+
+ /* unsigned binary number with an implicit binary point after the
+ * leftmost 3 bits, i.e., 0x2000 = 1.0 */
+ surplus = le_to_host16(tspec->surplus_bandwidth_allowance);
+ if (surplus <= 0x2000) {
+ wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not "
+ "greater than unity");
+ return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+ }
+
+ medium_time = surplus * pps * duration / 0x2000;
+ wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time);
+
+ /*
+ * TODO: store list of granted (and still active) TSPECs and check
+ * whether there is available medium time for this request. For now,
+ * just refuse requests that would by themselves take very large
+ * portion of the available bandwidth.
+ */
+ if (medium_time > 750000) {
+ wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over "
+ "75%% of available bandwidth");
+ return WMM_ADDTS_STATUS_REFUSED;
+ }
+
+ /* Convert to 32 microseconds per second unit */
+ tspec->medium_time = host_to_le16(medium_time / 32);
+
+ return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED;
+}
+
+
+static void wmm_addts_req(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ struct wmm_tspec_element *tspec, size_t len)
+{
+ const u8 *end = ((const u8 *) mgmt) + len;
+ int res;
+
+ if ((const u8 *) (tspec + 1) > end) {
+ wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC "
+ "from " MACSTR,
+ mgmt->u.action.u.wmm_action.dialog_token,
+ MAC2STR(mgmt->sa));
+
+ res = wmm_process_tspec(tspec);
+ wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
+
+ wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP,
+ mgmt->u.action.u.wmm_action.dialog_token, res);
+}
+
+
+void hostapd_wmm_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ int action_code;
+ int left = len - IEEE80211_HDRLEN - 4;
+ const u8 *pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 4;
+ struct ieee802_11_elems elems;
+ struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);
+
+ /* check that the request comes from a valid station */
+ if (!sta ||
+ (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) !=
+ (WLAN_STA_ASSOC | WLAN_STA_WMM)) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "wmm action received is not from associated wmm"
+ " station");
+ /* TODO: respond with action frame refused status code */
+ return;
+ }
+
+ /* extract the tspec info element */
+ if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "hostapd_wmm_action - could not parse wmm "
+ "action");
+ /* TODO: respond with action frame invalid parameters status
+ * code */
+ return;
+ }
+
+ if (!elems.wmm_tspec ||
+ elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "hostapd_wmm_action - missing or wrong length "
+ "tspec");
+ /* TODO: respond with action frame invalid parameters status
+ * code */
+ return;
+ }
+
+ /* TODO: check the request is for an AC with ACM set, if not, refuse
+ * request */
+
+ action_code = mgmt->u.action.u.wmm_action.action_code;
+ switch (action_code) {
+ case WMM_ACTION_CODE_ADDTS_REQ:
+ wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *)
+ (elems.wmm_tspec - 2), len);
+ return;
+#if 0
+ /* TODO: needed for client implementation */
+ case WMM_ACTION_CODE_ADDTS_RESP:
+ wmm_setup_request(hapd, mgmt, len);
+ return;
+ /* TODO: handle station teardown requests */
+ case WMM_ACTION_CODE_DELTS:
+ wmm_teardown(hapd, mgmt, len);
+ return;
+#endif
+ }
+
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "hostapd_wmm_action - unknown action code %d",
+ action_code);
+}
diff --git a/src/ap/wmm.h b/src/ap/wmm.h
new file mode 100644
index 0000000..96b04e8
--- /dev/null
+++ b/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/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
new file mode 100644
index 0000000..cfb2cad
--- /dev/null
+++ b/src/ap/wpa_auth.c
@@ -0,0 +1,2838 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/state_machine.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "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 */
+
+/* TODO: make these configurable */
+static const int dot11RSNAConfigPMKLifetime = 43200;
+static const int dot11RSNAConfigPMKReauthThreshold = 70;
+static const int dot11RSNAConfigSATimeout = 60;
+
+
+static inline void wpa_auth_mic_failure_report(
+ struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+ if (wpa_auth->cb.mic_failure_report)
+ wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr);
+}
+
+
+static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, wpa_eapol_variable var,
+ int value)
+{
+ if (wpa_auth->cb.set_eapol)
+ wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value);
+}
+
+
+static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, wpa_eapol_variable var)
+{
+ if (wpa_auth->cb.get_eapol == NULL)
+ return -1;
+ return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var);
+}
+
+
+static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, const u8 *prev_psk)
+{
+ if (wpa_auth->cb.get_psk == NULL)
+ return NULL;
+ return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk);
+}
+
+
+static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, u8 *msk, size_t *len)
+{
+ if (wpa_auth->cb.get_msk == NULL)
+ return -1;
+ return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len);
+}
+
+
+static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
+ int vlan_id,
+ enum wpa_alg alg, const u8 *addr, int idx,
+ u8 *key, size_t key_len)
+{
+ if (wpa_auth->cb.set_key == NULL)
+ return -1;
+ return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
+ key, key_len);
+}
+
+
+static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, int idx, u8 *seq)
+{
+ if (wpa_auth->cb.get_seqnum == NULL)
+ return -1;
+ return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
+}
+
+
+static inline int
+wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *data, size_t data_len, int encrypt)
+{
+ if (wpa_auth->cb.send_eapol == NULL)
+ return -1;
+ return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len,
+ encrypt);
+}
+
+
+int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
+ int (*cb)(struct wpa_state_machine *sm, void *ctx),
+ void *cb_ctx)
+{
+ if (wpa_auth->cb.for_each_sta == NULL)
+ return 0;
+ return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx);
+}
+
+
+int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
+ int (*cb)(struct wpa_authenticator *a, void *ctx),
+ void *cb_ctx)
+{
+ if (wpa_auth->cb.for_each_auth == NULL)
+ return 0;
+ return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx);
+}
+
+
+void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ logger_level level, const char *txt)
+{
+ if (wpa_auth->cb.logger == NULL)
+ return;
+ wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt);
+}
+
+
+void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ logger_level level, const char *fmt, ...)
+{
+ char *format;
+ int maxlen;
+ va_list ap;
+
+ if (wpa_auth->cb.logger == NULL)
+ return;
+
+ maxlen = os_strlen(fmt) + 100;
+ format = os_malloc(maxlen);
+ if (!format)
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(format, maxlen, fmt, ap);
+ va_end(ap);
+
+ wpa_auth_logger(wpa_auth, addr, level, format);
+
+ os_free(format);
+}
+
+
+static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
+ const u8 *addr)
+{
+ if (wpa_auth->cb.disconnect == NULL)
+ return;
+ wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+}
+
+
+static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
+{
+ int ret = 0;
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+ ret = 1;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
+ ret = 1;
+#endif /* CONFIG_IEEE80211W */
+ return ret;
+}
+
+
+static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+
+ if (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 void wpa_group_set_key_len(struct wpa_group *group, int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP:
+ group->GTK_len = 16;
+ break;
+ case WPA_CIPHER_TKIP:
+ group->GTK_len = 32;
+ break;
+ case WPA_CIPHER_WEP104:
+ group->GTK_len = 13;
+ break;
+ case WPA_CIPHER_WEP40:
+ group->GTK_len = 5;
+ break;
+ }
+}
+
+
+static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ u8 buf[ETH_ALEN + 8 + sizeof(group)];
+ u8 rkey[32];
+
+ 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);
+ os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
+ 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)
+{
+ struct wpa_group *group;
+
+ group = os_zalloc(sizeof(struct wpa_group));
+ if (group == NULL)
+ return NULL;
+
+ group->GTKAuthenticator = TRUE;
+ group->vlan_id = vlan_id;
+
+ wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+
+ 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;
+ wpa_group_sm_step(wpa_auth, group);
+ group->GInit = FALSE;
+ wpa_group_sm_step(wpa_auth, group);
+
+ return group;
+}
+
+
+/**
+ * wpa_init - Initialize WPA authenticator
+ * @addr: Authenticator address
+ * @conf: Configuration for WPA authenticator
+ * @cb: Callback functions for WPA authenticator
+ * Returns: Pointer to WPA authenticator data or %NULL on failure
+ */
+struct wpa_authenticator * wpa_init(const u8 *addr,
+ struct wpa_auth_config *conf,
+ struct wpa_auth_callbacks *cb)
+{
+ struct wpa_authenticator *wpa_auth;
+
+ wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
+ if (wpa_auth == NULL)
+ return NULL;
+ os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
+ os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+ os_memcpy(&wpa_auth->cb, cb, sizeof(*cb));
+
+ if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+ wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+ os_free(wpa_auth);
+ return NULL;
+ }
+
+ wpa_auth->group = wpa_group_init(wpa_auth, 0);
+ if (wpa_auth->group == NULL) {
+ os_free(wpa_auth->wpa_ie);
+ os_free(wpa_auth);
+ return NULL;
+ }
+
+ wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb,
+ wpa_auth);
+ if (wpa_auth->pmksa == NULL) {
+ wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
+ os_free(wpa_auth->wpa_ie);
+ os_free(wpa_auth);
+ return NULL;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
+ if (wpa_auth->ft_pmk_cache == NULL) {
+ wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
+ os_free(wpa_auth->wpa_ie);
+ pmksa_cache_auth_deinit(wpa_auth->pmksa);
+ os_free(wpa_auth);
+ return NULL;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ if (wpa_auth->conf.wpa_gmk_rekey) {
+ eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
+ wpa_rekey_gmk, wpa_auth, NULL);
+ }
+
+ if (wpa_auth->conf.wpa_group_rekey) {
+ eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
+ wpa_rekey_gtk, wpa_auth, NULL);
+ }
+
+ return wpa_auth;
+}
+
+
+/**
+ * wpa_deinit - Deinitialize WPA authenticator
+ * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
+ */
+void wpa_deinit(struct wpa_authenticator *wpa_auth)
+{
+ struct wpa_group *group, *prev;
+
+ eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
+ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+
+#ifdef CONFIG_PEERKEY
+ while (wpa_auth->stsl_negotiations)
+ wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations);
+#endif /* CONFIG_PEERKEY */
+
+ pmksa_cache_auth_deinit(wpa_auth->pmksa);
+
+#ifdef CONFIG_IEEE80211R
+ wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
+ wpa_auth->ft_pmk_cache = NULL;
+#endif /* CONFIG_IEEE80211R */
+
+ os_free(wpa_auth->wpa_ie);
+
+ group = wpa_auth->group;
+ while (group) {
+ prev = group;
+ group = group->next;
+ os_free(prev);
+ }
+
+ os_free(wpa_auth);
+}
+
+
+/**
+ * wpa_reconfig - Update WPA authenticator configuration
+ * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
+ * @conf: Configuration for WPA authenticator
+ */
+int wpa_reconfig(struct wpa_authenticator *wpa_auth,
+ struct wpa_auth_config *conf)
+{
+ struct wpa_group *group;
+ if (wpa_auth == NULL)
+ return 0;
+
+ os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+ if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+ wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+ return -1;
+ }
+
+ /*
+ * Reinitialize GTK to make sure it is suitable for the new
+ * configuration.
+ */
+ group = wpa_auth->group;
+ wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+ group->GInit = TRUE;
+ wpa_group_sm_step(wpa_auth, group);
+ group->GInit = FALSE;
+ wpa_group_sm_step(wpa_auth, group);
+
+ return 0;
+}
+
+
+struct wpa_state_machine *
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+ struct wpa_state_machine *sm;
+
+ sm = os_zalloc(sizeof(struct wpa_state_machine));
+ if (sm == NULL)
+ return NULL;
+ os_memcpy(sm->addr, addr, ETH_ALEN);
+
+ sm->wpa_auth = wpa_auth;
+ sm->group = wpa_auth->group;
+
+ return sm;
+}
+
+
+int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm)
+{
+ if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
+ return -1;
+
+#ifdef CONFIG_IEEE80211R
+ if (sm->ft_completed) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "FT authentication already completed - do not "
+ "start 4-way handshake");
+ return 0;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ if (sm->started) {
+ os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
+ sm->ReAuthenticationRequest = TRUE;
+ return wpa_sm_step(sm);
+ }
+
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "start authentication");
+ sm->started = 1;
+
+ sm->Init = TRUE;
+ if (wpa_sm_step(sm) == 1)
+ return 1; /* should not really happen */
+ sm->Init = FALSE;
+ sm->AuthenticationRequest = TRUE;
+ return wpa_sm_step(sm);
+}
+
+
+void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
+{
+ /* WPA/RSN was not used - clear WPA state. This is needed if the STA
+ * reassociates back to the same AP while the previous entry for the
+ * STA has not yet been removed. */
+ if (sm == NULL)
+ return;
+
+ sm->wpa_key_mgmt = 0;
+}
+
+
+static void wpa_free_sta_sm(struct wpa_state_machine *sm)
+{
+ 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_state_machine *sm,
+ const u8 *replay_counter)
+{
+ int i;
+ for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+ if (!sm->key_replay[i].valid)
+ break;
+ if (os_memcmp(replay_counter, sm->key_replay[i].counter,
+ WPA_REPLAY_COUNTER_LEN) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ struct wpa_eapol_ie_parse *kde)
+{
+ struct wpa_ie_data ie;
+ struct rsn_mdie *mdie;
+
+ if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
+ ie.num_pmkid != 1 || ie.pmkid == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in "
+ "FT 4-way handshake message 2/4");
+ return -1;
+ }
+
+ os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
+ sm->sup_pmk_r1_name, PMKID_LEN);
+
+ if (!kde->mdie || !kde->ftie) {
+ wpa_printf(MSG_DEBUG, "FT: No %s in FT 4-way handshake "
+ "message 2/4", kde->mdie ? "FTIE" : "MDIE");
+ return -1;
+ }
+
+ mdie = (struct rsn_mdie *) (kde->mdie + 2);
+ if (kde->mdie[1] < sizeof(struct rsn_mdie) ||
+ os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
+ return -1;
+ }
+
+ if (sm->assoc_resp_ftie &&
+ (kde->ftie[1] != sm->assoc_resp_ftie[1] ||
+ os_memcmp(kde->ftie, sm->assoc_resp_ftie,
+ 2 + sm->assoc_resp_ftie[1]) != 0)) {
+ wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
+ wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4",
+ kde->ftie, kde->ftie_len);
+ wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp",
+ sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]);
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+void wpa_receive(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ u8 *data, size_t data_len)
+{
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ u16 key_info, key_data_length;
+ enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
+ SMK_M1, SMK_M3, SMK_ERROR } msg;
+ char *msgtxt;
+ struct wpa_eapol_ie_parse kde;
+ int ft;
+ const u8 *eapol_key_ie;
+ size_t eapol_key_ie_len;
+
+ if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
+ return;
+
+ if (data_len < sizeof(*hdr) + sizeof(*key))
+ return;
+
+ hdr = (struct ieee802_1x_hdr *) data;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ key_info = WPA_GET_BE16(key->key_info);
+ key_data_length = WPA_GET_BE16(key->key_data_length);
+ if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) {
+ wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
+ "key_data overflow (%d > %lu)",
+ key_data_length,
+ (unsigned long) (data_len - sizeof(*hdr) -
+ sizeof(*key)));
+ return;
+ }
+
+ if (sm->wpa == WPA_VERSION_WPA2) {
+ if (key->type != EAPOL_KEY_TYPE_RSN) {
+ wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with "
+ "unexpected type %d in RSN mode",
+ key->type);
+ return;
+ }
+ } else {
+ if (key->type != EAPOL_KEY_TYPE_WPA) {
+ wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with "
+ "unexpected type %d in WPA mode",
+ key->type);
+ return;
+ }
+ }
+
+ 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) {
+ if (wpa_use_aes_cmac(sm) &&
+ ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_auth_logger(wpa_auth, sm->addr,
+ LOGGER_WARNING,
+ "advertised support for "
+ "AES-128-CMAC, but did not "
+ "use it");
+ return;
+ }
+
+ if (!wpa_use_aes_cmac(sm) &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_auth_logger(wpa_auth, sm->addr,
+ LOGGER_WARNING,
+ "did not use HMAC-SHA1-AES "
+ "with CCMP");
+ return;
+ }
+ }
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ if (sm->req_replay_counter_used &&
+ os_memcmp(key->replay_counter, sm->req_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) <= 0) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+ "received EAPOL-Key request with "
+ "replayed counter");
+ return;
+ }
+ }
+
+ if (!(key_info & WPA_KEY_INFO_REQUEST) &&
+ !wpa_replay_counter_valid(sm, key->replay_counter)) {
+ int i;
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key %s with unexpected "
+ "replay counter", msgtxt);
+ for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+ if (!sm->key_replay[i].valid)
+ break;
+ wpa_hexdump(MSG_DEBUG, "pending replay counter",
+ sm->key_replay[i].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ }
+ wpa_hexdump(MSG_DEBUG, "received replay counter",
+ key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+ return;
+ }
+
+ switch (msg) {
+ case PAIRWISE_2:
+ if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
+ sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key msg 2/4 in "
+ "invalid state (%d) - dropped",
+ sm->wpa_ptk_state);
+ return;
+ }
+ 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");
+ sm->group->reject_4way_hs_for_entropy = FALSE;
+ random_mark_pool_ready();
+ sm->group->first_sta_seen = FALSE;
+ 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) {
+ 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) {
+ /* Supplicant reported a Michael MIC error */
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key Error Request "
+ "(STA detected Michael MIC failure)");
+ wpa_auth_mic_failure_report(wpa_auth, sm->addr);
+ sm->dot11RSNAStatsTKIPRemoteMICFailures++;
+ wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
+ /* Error report is not a request for a new key
+ * handshake, but since Authenticator may do it, let's
+ * change the keys now anyway. */
+ wpa_request_new_ptk(sm);
+ } else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key Request for new "
+ "4-Way Handshake");
+ wpa_request_new_ptk(sm);
+#ifdef CONFIG_PEERKEY
+ } else if (msg == SMK_M1) {
+ wpa_smk_m1(wpa_auth, sm, key);
+#endif /* CONFIG_PEERKEY */
+ } else if (key_data_length > 0 &&
+ wpa_parse_kde_ies((const u8 *) (key + 1),
+ key_data_length, &kde) == 0 &&
+ kde.mac_addr) {
+ } else {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key Request for GTK "
+ "rekeying");
+ /* FIX: why was this triggering PTK rekeying for the
+ * STA that requested Group Key rekeying?? */
+ /* wpa_request_new_ptk(sta->wpa_sm); */
+ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+ wpa_rekey_gtk(wpa_auth, NULL);
+ }
+ } else {
+ /* Do not allow the same key replay counter to be reused. This
+ * does also invalidate all other pending replay counters if
+ * retransmissions were used, i.e., we will only process one of
+ * the pending replies and ignore rest if more than one is
+ * received. */
+ sm->key_replay[0].valid = FALSE;
+ }
+
+#ifdef CONFIG_PEERKEY
+ if (msg == SMK_M3) {
+ wpa_smk_m3(wpa_auth, sm, key);
+ return;
+ }
+#endif /* CONFIG_PEERKEY */
+
+ os_free(sm->last_rx_eapol_key);
+ sm->last_rx_eapol_key = os_malloc(data_len);
+ if (sm->last_rx_eapol_key == NULL)
+ return;
+ os_memcpy(sm->last_rx_eapol_key, data, data_len);
+ sm->last_rx_eapol_key_len = data_len;
+
+ sm->EAPOLKeyReceived = TRUE;
+ sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
+ sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
+ os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
+ wpa_sm_step(sm);
+}
+
+
+static 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_CCMP)
+ version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
+
+ wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d "
+ "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d "
+ "encr=%d)",
+ version,
+ (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0,
+ (key_info & WPA_KEY_INFO_MIC) ? 1 : 0,
+ (key_info & WPA_KEY_INFO_ACK) ? 1 : 0,
+ (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0,
+ pairwise, (unsigned long) kde_len, keyidx, encr);
+
+ key_data_len = kde_len;
+
+ if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+ version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
+ pad_len = key_data_len % 8;
+ if (pad_len)
+ pad_len = 8 - pad_len;
+ key_data_len += pad_len + 8;
+ }
+
+ len += key_data_len;
+
+ hdr = os_zalloc(len);
+ if (hdr == NULL)
+ return;
+ hdr->version = wpa_auth->conf.eapol_version;
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = host_to_be16(len - sizeof(*hdr));
+ key = (struct wpa_eapol_key *) (hdr + 1);
+
+ key->type = sm->wpa == WPA_VERSION_WPA2 ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ key_info |= version;
+ if (encr && sm->wpa == WPA_VERSION_WPA2)
+ key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+ if (sm->wpa != WPA_VERSION_WPA2)
+ key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT;
+ WPA_PUT_BE16(key->key_info, key_info);
+
+ alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
+ switch (alg) {
+ case WPA_CIPHER_CCMP:
+ WPA_PUT_BE16(key->key_length, 16);
+ break;
+ case WPA_CIPHER_TKIP:
+ WPA_PUT_BE16(key->key_length, 32);
+ break;
+ case WPA_CIPHER_WEP40:
+ WPA_PUT_BE16(key->key_length, 5);
+ break;
+ case WPA_CIPHER_WEP104:
+ WPA_PUT_BE16(key->key_length, 13);
+ break;
+ }
+ if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
+ WPA_PUT_BE16(key->key_length, 0);
+
+ /* FIX: STSL: what to use as key_replay_counter? */
+ for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
+ sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
+ os_memcpy(sm->key_replay[i].counter,
+ sm->key_replay[i - 1].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ }
+ inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN);
+ os_memcpy(key->replay_counter, sm->key_replay[0].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ sm->key_replay[0].valid = TRUE;
+
+ if (nonce)
+ os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN);
+
+ if (key_rsc)
+ os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
+
+ if (kde && !encr) {
+ os_memcpy(key + 1, kde, kde_len);
+ WPA_PUT_BE16(key->key_data_length, kde_len);
+ } else if (encr && kde) {
+ buf = os_zalloc(key_data_len);
+ if (buf == NULL) {
+ os_free(hdr);
+ return;
+ }
+ pos = buf;
+ os_memcpy(pos, kde, kde_len);
+ pos += kde_len;
+
+ if (pad_len)
+ *pos++ = 0xdd;
+
+ wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+ buf, key_data_len);
+ if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+ version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf,
+ (u8 *) (key + 1))) {
+ os_free(hdr);
+ os_free(buf);
+ return;
+ }
+ WPA_PUT_BE16(key->key_data_length, key_data_len);
+ } else {
+ u8 ek[32];
+ os_memcpy(key->key_iv,
+ sm->group->Counter + WPA_NONCE_LEN - 16, 16);
+ inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
+ os_memcpy(ek, key->key_iv, 16);
+ os_memcpy(ek + 16, sm->PTK.kek, 16);
+ os_memcpy(key + 1, buf, key_data_len);
+ rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len);
+ WPA_PUT_BE16(key->key_data_length, key_data_len);
+ }
+ os_free(buf);
+ }
+
+ if (key_info & WPA_KEY_INFO_MIC) {
+ if (!sm->PTK_valid) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PTK not valid when sending EAPOL-Key "
+ "frame");
+ os_free(hdr);
+ return;
+ }
+ wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len,
+ key->key_mic);
+ }
+
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx,
+ 1);
+ wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len,
+ sm->pairwise_set);
+ os_free(hdr);
+}
+
+
+static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int key_info,
+ const u8 *key_rsc, const u8 *nonce,
+ const u8 *kde, size_t kde_len,
+ int keyidx, int encr)
+{
+ int timeout_ms;
+ int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
+ int ctr;
+
+ if (sm == NULL)
+ return;
+
+ __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
+ keyidx, encr, 0);
+
+ ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
+ if (ctr == 1 && wpa_auth->conf.tx_status)
+ timeout_ms = eapol_key_timeout_first;
+ 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);
+}
+
+
+static enum wpa_alg wpa_alg_enum(int alg)
+{
+ switch (alg) {
+ case WPA_CIPHER_CCMP:
+ return WPA_ALG_CCMP;
+ case WPA_CIPHER_TKIP:
+ return WPA_ALG_TKIP;
+ case WPA_CIPHER_WEP104:
+ case WPA_CIPHER_WEP40:
+ return WPA_ALG_WEP;
+ default:
+ return WPA_ALG_NONE;
+ }
+}
+
+
+SM_STATE(WPA_PTK, INITIALIZE)
+{
+ SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk);
+ if (sm->Init) {
+ /* Init flag is not cleared here, so avoid busy
+ * loop by claiming nothing changed. */
+ sm->changed = FALSE;
+ }
+
+ sm->keycount = 0;
+ if (sm->GUpdateStationKeys)
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = FALSE;
+ if (sm->wpa == WPA_VERSION_WPA)
+ sm->PInitAKeys = FALSE;
+ if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and
+ * Local AA > Remote AA)) */) {
+ sm->Pair = TRUE;
+ }
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0);
+ wpa_remove_ptk(sm);
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
+ sm->TimeoutCtr = 0;
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+ WPA_EAPOL_authorized, 0);
+ }
+}
+
+
+SM_STATE(WPA_PTK, DISCONNECT)
+{
+ SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
+ sm->Disconnect = FALSE;
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+}
+
+
+SM_STATE(WPA_PTK, DISCONNECTED)
+{
+ SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk);
+ sm->DeauthenticationRequest = FALSE;
+}
+
+
+SM_STATE(WPA_PTK, AUTHENTICATION)
+{
+ SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk);
+ os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+ sm->PTK_valid = FALSE;
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto,
+ 1);
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1);
+ sm->AuthenticationRequest = FALSE;
+}
+
+
+static void wpa_group_first_station(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ /*
+ * 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;
+ }
+ 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);
+
+ if (!sm->group->first_sta_seen) {
+ wpa_group_first_station(sm->wpa_auth, sm->group);
+ sm->group->first_sta_seen = TRUE;
+ }
+
+ os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
+ WPA_NONCE_LEN);
+ inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
+ sm->ReAuthenticationRequest = FALSE;
+ /* IEEE 802.11i does not clear TimeoutCtr here, but this is more
+ * logical place than INITIALIZE since AUTHENTICATION2 can be
+ * re-entered on ReAuthenticationRequest without going through
+ * INITIALIZE. */
+ sm->TimeoutCtr = 0;
+}
+
+
+SM_STATE(WPA_PTK, INITPMK)
+{
+ u8 msk[2 * PMK_LEN];
+ size_t len = 2 * PMK_LEN;
+
+ SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
+#ifdef CONFIG_IEEE80211R
+ sm->xxkey_len = 0;
+#endif /* CONFIG_IEEE80211R */
+ if (sm->pmksa) {
+ wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
+ os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN);
+ } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
+ wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine "
+ "(len=%lu)", (unsigned long) len);
+ os_memcpy(sm->PMK, msk, PMK_LEN);
+#ifdef CONFIG_IEEE80211R
+ if (len >= 2 * PMK_LEN) {
+ os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
+ sm->xxkey_len = PMK_LEN;
+ }
+#endif /* CONFIG_IEEE80211R */
+ } else {
+ wpa_printf(MSG_DEBUG, "WPA: Could not get PMK");
+ }
+
+ sm->req_replay_counter_used = 0;
+ /* IEEE 802.11i does not set keyRun to FALSE, but not doing this
+ * will break reauthentication since EAPOL state machines may not be
+ * get into AUTHENTICATING state that clears keyRun before WPA state
+ * machine enters AUTHENTICATION2 state and goes immediately to INITPMK
+ * state and takes PMK from the previously used AAA Key. This will
+ * eventually fail in 4-Way Handshake because Supplicant uses PMK
+ * derived from the new AAA Key. Setting keyRun = FALSE here seems to
+ * be good workaround for this issue. */
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0);
+}
+
+
+SM_STATE(WPA_PTK, INITPSK)
+{
+ const u8 *psk;
+ SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
+ psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL);
+ if (psk) {
+ os_memcpy(sm->PMK, psk, PMK_LEN);
+#ifdef CONFIG_IEEE80211R
+ os_memcpy(sm->xxkey, psk, PMK_LEN);
+ sm->xxkey_len = PMK_LEN;
+#endif /* CONFIG_IEEE80211R */
+ }
+ sm->req_replay_counter_used = 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKSTART)
+{
+ u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL;
+ size_t pmkid_len = 0;
+
+ SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
+ sm->PTKRequest = FALSE;
+ sm->TimeoutEvt = FALSE;
+
+ sm->TimeoutCtr++;
+ if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+ /* No point in sending the EAPOL-Key - we will disconnect
+ * immediately following this. */
+ return;
+ }
+
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "sending 1/4 msg of 4-Way Handshake");
+ /*
+ * TODO: Could add PMKID even with WPA2-PSK, but only if there is only
+ * one possible PSK for this STA.
+ */
+ if (sm->wpa == WPA_VERSION_WPA2 &&
+ wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) {
+ pmkid = buf;
+ pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+ pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
+ pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
+ RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
+ if (sm->pmksa)
+ os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+ sm->pmksa->pmkid, PMKID_LEN);
+ else {
+ /*
+ * Calculate PMKID since no PMKSA cache entry was
+ * available with pre-calculated PMKID.
+ */
+ rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr,
+ sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
+ wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
+ }
+ }
+ wpa_send_eapol(sm->wpa_auth, sm,
+ WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
+ sm->ANonce, pmkid, pmkid_len, 0, 0);
+}
+
+
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+ struct wpa_ptk *ptk)
+{
+ size_t ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64;
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+ return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len);
+#endif /* CONFIG_IEEE80211R */
+
+ wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
+ sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce,
+ (u8 *) ptk, ptk_len,
+ wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
+
+ return 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
+{
+ struct wpa_ptk PTK;
+ int ok = 0;
+ const u8 *pmk = NULL;
+
+ SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
+ sm->EAPOLKeyReceived = FALSE;
+
+ /* WPA with IEEE 802.1X: use the derived PMK from EAP
+ * WPA-PSK: iterate through possible PSKs and select the one matching
+ * the packet */
+ for (;;) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+ pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk);
+ if (pmk == NULL)
+ break;
+ } else
+ pmk = sm->PMK;
+
+ wpa_derive_ptk(sm, pmk, &PTK);
+
+ if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key,
+ sm->last_rx_eapol_key_len) == 0) {
+ ok = 1;
+ break;
+ }
+
+ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+ break;
+ }
+
+ if (!ok) {
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "invalid MIC in msg 2/4 of 4-Way Handshake");
+ return;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ /*
+ * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
+ * with the value we derived.
+ */
+ if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0) {
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PMKR1Name mismatch in FT 4-way "
+ "handshake");
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from "
+ "Supplicant",
+ sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+ return;
+ }
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ 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;
+
+ if (!sm->mgmt_frame_prot)
+ return pos;
+
+ igtk.keyid[0] = gsm->GN_igtk;
+ igtk.keyid[1] = 0;
+ if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0)
+ os_memset(igtk.pn, 0, sizeof(igtk.pn));
+ os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
+ (const u8 *) &igtk, sizeof(igtk), NULL, 0);
+
+ return pos;
+}
+
+#else /* CONFIG_IEEE80211W */
+
+static int ieee80211w_kde_len(struct wpa_state_machine *sm)
+{
+ return 0;
+}
+
+
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
+{
+ return pos;
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
+SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
+{
+ u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
+ size_t gtk_len, kde_len;
+ struct wpa_group *gsm = sm->group;
+ u8 *wpa_ie;
+ int wpa_ie_len, secure, keyidx, encr = 0;
+
+ SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
+ sm->TimeoutEvt = FALSE;
+
+ sm->TimeoutCtr++;
+ if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+ /* No point in sending the EAPOL-Key - we will disconnect
+ * immediately following this. */
+ return;
+ }
+
+ /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+ GTK[GN], IGTK, [FTIE], [TIE * 2])
+ */
+ os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+ /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
+ wpa_ie = sm->wpa_auth->wpa_ie;
+ wpa_ie_len = sm->wpa_auth->wpa_ie_len;
+ if (sm->wpa == WPA_VERSION_WPA &&
+ (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
+ /* WPA-only STA, remove RSN IE */
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ wpa_ie_len = wpa_ie[1] + 2;
+ }
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "sending 3/4 msg of 4-Way Handshake");
+ if (sm->wpa == WPA_VERSION_WPA2) {
+ /* WPA2 send GTK in the 4-way handshake */
+ secure = 1;
+ gtk = gsm->GTK[gsm->GN - 1];
+ gtk_len = gsm->GTK_len;
+ keyidx = gsm->GN;
+ _rsc = rsc;
+ encr = 1;
+ } else {
+ /* WPA does not include GTK in msg 3/4 */
+ secure = 0;
+ gtk = NULL;
+ gtk_len = 0;
+ keyidx = 0;
+ _rsc = NULL;
+ }
+
+ kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
+ if (gtk)
+ kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+ kde_len += 300; /* FTIE + 2 * TIE */
+ }
+#endif /* CONFIG_IEEE80211R */
+ kde = os_malloc(kde_len);
+ if (kde == NULL)
+ return;
+
+ pos = kde;
+ os_memcpy(pos, wpa_ie, wpa_ie_len);
+ pos += wpa_ie_len;
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to insert "
+ "PMKR1Name into RSN IE in EAPOL-Key data");
+ os_free(kde);
+ return;
+ }
+ pos += res;
+ }
+#endif /* CONFIG_IEEE80211R */
+ if (gtk) {
+ u8 hdr[2];
+ hdr[0] = keyidx & 0x03;
+ hdr[1] = 0;
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gtk_len);
+ }
+ pos = ieee80211w_kde_add(sm, pos);
+
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res;
+ struct wpa_auth_config *conf;
+
+ conf = &sm->wpa_auth->conf;
+ res = wpa_write_ftie(conf, conf->r0_key_holder,
+ conf->r0_key_holder_len,
+ NULL, NULL, pos, kde + kde_len - pos,
+ NULL, 0);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
+ "into EAPOL-Key Key Data");
+ os_free(kde);
+ return;
+ }
+ pos += res;
+
+ /* TIE[ReassociationDeadline] (TU) */
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+ WPA_PUT_LE32(pos, conf->reassociation_deadline);
+ pos += 4;
+
+ /* TIE[KeyLifetime] (seconds) */
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+ WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+ pos += 4;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ wpa_send_eapol(sm->wpa_auth, sm,
+ (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
+ WPA_KEY_INFO_KEY_TYPE,
+ _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+ os_free(kde);
+}
+
+
+SM_STATE(WPA_PTK, PTKINITDONE)
+{
+ SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk);
+ sm->EAPOLKeyReceived = FALSE;
+ if (sm->Pair) {
+ enum wpa_alg alg;
+ int klen;
+ if (sm->pairwise == WPA_CIPHER_TKIP) {
+ alg = WPA_ALG_TKIP;
+ klen = 32;
+ } else {
+ alg = WPA_ALG_CCMP;
+ klen = 16;
+ }
+ if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+ sm->PTK.tk1, klen)) {
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+ return;
+ }
+ /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+ sm->pairwise_set = TRUE;
+
+ if (sm->wpa_auth->conf.wpa_ptk_rekey) {
+ eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+ eloop_register_timeout(sm->wpa_auth->conf.
+ wpa_ptk_rekey, 0, wpa_rekey_ptk,
+ sm->wpa_auth, sm);
+ }
+
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+ WPA_EAPOL_authorized, 1);
+ }
+ }
+
+ if (0 /* IBSS == TRUE */) {
+ sm->keycount++;
+ if (sm->keycount == 2) {
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+ WPA_EAPOL_portValid, 1);
+ }
+ } else {
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid,
+ 1);
+ }
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0);
+ wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1);
+ if (sm->wpa == WPA_VERSION_WPA)
+ sm->PInitAKeys = TRUE;
+ else
+ sm->has_GTK = TRUE;
+ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+ "pairwise key handshake completed (%s)",
+ sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
+
+#ifdef CONFIG_IEEE80211R
+ wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+SM_STEP(WPA_PTK)
+{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+
+ if (sm->Init)
+ SM_ENTER(WPA_PTK, INITIALIZE);
+ else if (sm->Disconnect
+ /* || FIX: dot11RSNAConfigSALifetime timeout */) {
+ 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, 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->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;
+
+ SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
+
+ sm->GTimeoutCtr++;
+ if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) {
+ /* No point in sending the EAPOL-Key - we will disconnect
+ * immediately following this. */
+ return;
+ }
+
+ if (sm->wpa == WPA_VERSION_WPA)
+ sm->PInitAKeys = FALSE;
+ sm->TimeoutEvt = FALSE;
+ /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
+ os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+ if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE)
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "sending 1/2 msg of Group Key Handshake");
+
+ if (sm->wpa == WPA_VERSION_WPA2) {
+ kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+ ieee80211w_kde_len(sm);
+ kde = os_malloc(kde_len);
+ if (kde == NULL)
+ return;
+
+ pos = kde;
+ hdr[0] = gsm->GN & 0x03;
+ hdr[1] = 0;
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+ pos = ieee80211w_kde_add(sm, pos);
+ } else {
+ kde = gsm->GTK[gsm->GN - 1];
+ pos = kde + gsm->GTK_len;
+ }
+
+ wpa_send_eapol(sm->wpa_auth, sm,
+ WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_ACK |
+ (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
+ rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1);
+ if (sm->wpa == WPA_VERSION_WPA2)
+ os_free(kde);
+}
+
+
+SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
+{
+ SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group);
+ sm->EAPOLKeyReceived = FALSE;
+ if (sm->GUpdateStationKeys)
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = FALSE;
+ sm->GTimeoutCtr = 0;
+ /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
+ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+ "group key handshake completed (%s)",
+ sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
+ sm->has_GTK = TRUE;
+}
+
+
+SM_STATE(WPA_PTK_GROUP, KEYERROR)
+{
+ SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
+ if (sm->GUpdateStationKeys)
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = FALSE;
+ sm->Disconnect = TRUE;
+}
+
+
+SM_STEP(WPA_PTK_GROUP)
+{
+ if (sm->Init || sm->PtkGroupInit) {
+ SM_ENTER(WPA_PTK_GROUP, IDLE);
+ sm->PtkGroupInit = FALSE;
+ } else switch (sm->wpa_ptk_group_state) {
+ case WPA_PTK_GROUP_IDLE:
+ if (sm->GUpdateStationKeys ||
+ (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys))
+ SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+ break;
+ case WPA_PTK_GROUP_REKEYNEGOTIATING:
+ if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+ !sm->EAPOLKeyPairwise && sm->MICVerified)
+ SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
+ else if (sm->GTimeoutCtr >
+ (int) dot11RSNAConfigGroupUpdateCount)
+ SM_ENTER(WPA_PTK_GROUP, KEYERROR);
+ else if (sm->TimeoutEvt)
+ SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+ break;
+ case WPA_PTK_GROUP_KEYERROR:
+ SM_ENTER(WPA_PTK_GROUP, IDLE);
+ break;
+ case WPA_PTK_GROUP_REKEYESTABLISHED:
+ SM_ENTER(WPA_PTK_GROUP, IDLE);
+ break;
+ }
+}
+
+
+static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ int ret = 0;
+
+ 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 (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");
+ }
+
+ sm->group->GKeyDoneStations++;
+ sm->GUpdateStationKeys = TRUE;
+
+ wpa_sm_step(sm);
+ return 0;
+}
+
+
+static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ int tmp;
+
+ wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
+ "SETKEYS (VLAN-ID %d)", group->vlan_id);
+ group->changed = TRUE;
+ group->wpa_group_state = WPA_GROUP_SETKEYS;
+ group->GTKReKey = FALSE;
+ tmp = group->GM;
+ group->GM = group->GN;
+ group->GN = tmp;
+#ifdef CONFIG_IEEE80211W
+ tmp = group->GM_igtk;
+ group->GM_igtk = group->GN_igtk;
+ group->GN_igtk = tmp;
+#endif /* CONFIG_IEEE80211W */
+ /* "GKeyDoneStations = GNoStations" is done in more robust way by
+ * counting the STAs that are marked with GUpdateStationKeys instead of
+ * including all STAs that could be in not-yet-completed state. */
+ wpa_gtk_update(wpa_auth, group);
+
+ 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, NULL);
+ 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_alg_enum(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);
+ }
+}
+
+
+static const char * wpa_bool_txt(int bool)
+{
+ return bool ? "TRUE" : "FALSE";
+}
+
+
+static int wpa_cipher_bits(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP:
+ return 128;
+ case WPA_CIPHER_TKIP:
+ return 256;
+ case WPA_CIPHER_WEP104:
+ return 104;
+ case WPA_CIPHER_WEP40:
+ return 40;
+ default:
+ return 0;
+ }
+}
+
+
+#define RSN_SUITE "%02x-%02x-%02x-%d"
+#define RSN_SUITE_ARG(s) \
+((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+
+int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
+{
+ int len = 0, ret;
+ char pmkid_txt[PMKID_LEN * 2 + 1];
+#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_bits(wpa_auth->conf.wpa_group),
+ dot11RSNAConfigPMKLifetime,
+ dot11RSNAConfigPMKReauthThreshold,
+ dot11RSNAConfigSATimeout,
+ RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected),
+ RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected),
+ RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected),
+ pmkid_txt,
+ RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested),
+ RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested),
+ RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested),
+ wpa_auth->dot11RSNATKIPCounterMeasuresInvoked,
+ wpa_auth->dot11RSNA4WayHandshakeFailures);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ /* TODO: dot11RSNAConfigPairwiseCiphersTable */
+ /* TODO: dot11RSNAConfigAuthenticationSuitesTable */
+
+ /* Private MIB */
+ ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n",
+ wpa_auth->group->wpa_group_state);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ return len;
+}
+
+
+int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
+{
+ int len = 0, ret;
+ u32 pairwise = 0;
+
+ if (sm == NULL)
+ return 0;
+
+ /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */
+
+ /* dot11RSNAStatsEntry */
+
+ if (sm->wpa == WPA_VERSION_WPA) {
+ if (sm->pairwise == WPA_CIPHER_CCMP)
+ pairwise = WPA_CIPHER_SUITE_CCMP;
+ else if (sm->pairwise == WPA_CIPHER_TKIP)
+ pairwise = WPA_CIPHER_SUITE_TKIP;
+ else if (sm->pairwise == WPA_CIPHER_WEP104)
+ pairwise = WPA_CIPHER_SUITE_WEP104;
+ else if (sm->pairwise == WPA_CIPHER_WEP40)
+ pairwise = WPA_CIPHER_SUITE_WEP40;
+ else if (sm->pairwise == WPA_CIPHER_NONE)
+ pairwise = WPA_CIPHER_SUITE_NONE;
+ } else if (sm->wpa == WPA_VERSION_WPA2) {
+ if (sm->pairwise == WPA_CIPHER_CCMP)
+ pairwise = RSN_CIPHER_SUITE_CCMP;
+ else if (sm->pairwise == WPA_CIPHER_TKIP)
+ pairwise = RSN_CIPHER_SUITE_TKIP;
+ else if (sm->pairwise == WPA_CIPHER_WEP104)
+ pairwise = RSN_CIPHER_SUITE_WEP104;
+ else if (sm->pairwise == WPA_CIPHER_WEP40)
+ pairwise = RSN_CIPHER_SUITE_WEP40;
+ else if (sm->pairwise == WPA_CIPHER_NONE)
+ pairwise = RSN_CIPHER_SUITE_NONE;
+ } else
+ return 0;
+
+ ret = os_snprintf(
+ buf + len, buflen - len,
+ /* TODO: dot11RSNAStatsIndex */
+ "dot11RSNAStatsSTAAddress=" MACSTR "\n"
+ "dot11RSNAStatsVersion=1\n"
+ "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n"
+ /* TODO: dot11RSNAStatsTKIPICVErrors */
+ "dot11RSNAStatsTKIPLocalMICFailures=%u\n"
+ "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)
+ return -1;
+
+ if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
+ sm->wpa_auth->addr, sm->addr, session_timeout,
+ eapol, sm->wpa_key_mgmt))
+ return 0;
+
+ return -1;
+}
+
+
+int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
+ const u8 *pmk, size_t len, const u8 *sta_addr,
+ int session_timeout,
+ struct eapol_state_machine *eapol)
+{
+ if (wpa_auth == NULL)
+ return -1;
+
+ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr,
+ sta_addr, session_timeout, eapol,
+ WPA_KEY_MGMT_IEEE8021X))
+ return 0;
+
+ return -1;
+}
+
+
+static struct wpa_group *
+wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+ struct wpa_group *group;
+
+ if (wpa_auth == NULL || wpa_auth->group == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d",
+ vlan_id);
+ group = wpa_group_init(wpa_auth, vlan_id);
+ if (group == NULL)
+ return NULL;
+
+ group->next = wpa_auth->group->next;
+ wpa_auth->group->next = group;
+
+ return group;
+}
+
+
+int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
+{
+ struct wpa_group *group;
+
+ if (sm == NULL || sm->wpa_auth == NULL)
+ return 0;
+
+ group = sm->wpa_auth->group;
+ while (group) {
+ if (group->vlan_id == vlan_id)
+ break;
+ group = group->next;
+ }
+
+ if (group == NULL) {
+ group = wpa_auth_add_group(sm->wpa_auth, vlan_id);
+ if (group == NULL)
+ return -1;
+ }
+
+ if (sm->group == group)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state "
+ "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id);
+
+ sm->group = group;
+ return 0;
+}
+
+
+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);
+ }
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
new file mode 100644
index 0000000..b3e1ff0
--- /dev/null
+++ b/src/ap/wpa_auth.h
@@ -0,0 +1,285 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_AUTH_H
+#define WPA_AUTH_H
+
+#include "common/defs.h"
+#include "common/eapol_common.h"
+#include "common/wpa_common.h"
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition
+ */
+struct ft_rrb_frame {
+ u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+ u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */
+ le16 action_length; /* little endian length of action_frame */
+ u8 ap_address[ETH_ALEN];
+ /*
+ * Followed by action_length bytes of FT Action frame (from Category
+ * field to the end of Action Frame body.
+ */
+} STRUCT_PACKED;
+
+#define RSN_REMOTE_FRAME_TYPE_FT_RRB 1
+
+#define FT_PACKET_REQUEST 0
+#define FT_PACKET_RESPONSE 1
+/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */
+#define FT_PACKET_R0KH_R1KH_PULL 200
+#define FT_PACKET_R0KH_R1KH_RESP 201
+#define FT_PACKET_R0KH_R1KH_PUSH 202
+
+#define FT_R0KH_R1KH_PULL_DATA_LEN 44
+#define FT_R0KH_R1KH_RESP_DATA_LEN 76
+#define FT_R0KH_R1KH_PUSH_DATA_LEN 88
+
+struct ft_r0kh_r1kh_pull_frame {
+ u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+ u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */
+ le16 data_length; /* little endian length of data (44) */
+ u8 ap_address[ETH_ALEN];
+
+ u8 nonce[16];
+ u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 r1kh_id[FT_R1KH_ID_LEN];
+ u8 s1kh_id[ETH_ALEN];
+ u8 pad[4]; /* 8-octet boundary for AES key wrap */
+ u8 key_wrap_extra[8];
+} STRUCT_PACKED;
+
+struct ft_r0kh_r1kh_resp_frame {
+ u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+ u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */
+ le16 data_length; /* little endian length of data (76) */
+ u8 ap_address[ETH_ALEN];
+
+ u8 nonce[16]; /* copied from pull */
+ u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
+ u8 s1kh_id[ETH_ALEN]; /* copied from pull */
+ u8 pmk_r1[PMK_LEN];
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ le16 pairwise;
+ u8 pad[2]; /* 8-octet boundary for AES key wrap */
+ u8 key_wrap_extra[8];
+} STRUCT_PACKED;
+
+struct ft_r0kh_r1kh_push_frame {
+ u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+ u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */
+ le16 data_length; /* little endian length of data (88) */
+ u8 ap_address[ETH_ALEN];
+
+ /* Encrypted with AES key-wrap */
+ u8 timestamp[4]; /* current time in seconds since unix epoch, little
+ * endian */
+ u8 r1kh_id[FT_R1KH_ID_LEN];
+ u8 s1kh_id[ETH_ALEN];
+ u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN];
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ le16 pairwise;
+ u8 pad[6]; /* 8-octet boundary for AES key wrap */
+ u8 key_wrap_extra[8];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* per STA state machine data */
+
+struct wpa_authenticator;
+struct wpa_state_machine;
+struct rsn_pmksa_cache_entry;
+struct eapol_state_machine;
+
+
+struct ft_remote_r0kh {
+ struct ft_remote_r0kh *next;
+ u8 addr[ETH_ALEN];
+ u8 id[FT_R0KH_ID_MAX_LEN];
+ size_t id_len;
+ u8 key[16];
+};
+
+
+struct ft_remote_r1kh {
+ struct ft_remote_r1kh *next;
+ u8 addr[ETH_ALEN];
+ u8 id[FT_R1KH_ID_LEN];
+ u8 key[16];
+};
+
+
+struct wpa_auth_config {
+ int wpa;
+ int wpa_key_mgmt;
+ int wpa_pairwise;
+ int wpa_group;
+ int wpa_group_rekey;
+ int wpa_strict_rekey;
+ int wpa_gmk_rekey;
+ int wpa_ptk_rekey;
+ int rsn_pairwise;
+ int rsn_preauth;
+ int eapol_version;
+ int peerkey;
+ int wmm_enabled;
+ int wmm_uapsd;
+ int okc;
+ 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 */
+};
+
+typedef enum {
+ LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING
+} logger_level;
+
+typedef enum {
+ WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized,
+ WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable,
+ WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx
+} wpa_eapol_variable;
+
+struct wpa_auth_callbacks {
+ void *ctx;
+ void (*logger)(void *ctx, const u8 *addr, logger_level level,
+ const char *txt);
+ void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
+ void (*mic_failure_report)(void *ctx, const u8 *addr);
+ void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var,
+ int value);
+ int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
+ const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *prev_psk);
+ int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
+ int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
+ const u8 *addr, int idx, u8 *key, size_t key_len);
+ int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq);
+ int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data,
+ size_t data_len, int encrypt);
+ int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm,
+ void *ctx), void *cb_ctx);
+ int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a,
+ void *ctx), void *cb_ctx);
+ int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
+ size_t data_len);
+#ifdef CONFIG_IEEE80211R
+ struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
+ int (*send_ft_action)(void *ctx, const u8 *dst,
+ const u8 *data, size_t data_len);
+#endif /* CONFIG_IEEE80211R */
+};
+
+struct wpa_authenticator * wpa_init(const u8 *addr,
+ struct wpa_auth_config *conf,
+ struct wpa_auth_callbacks *cb);
+void wpa_deinit(struct wpa_authenticator *wpa_auth);
+int wpa_reconfig(struct wpa_authenticator *wpa_auth,
+ struct wpa_auth_config *conf);
+
+enum {
+ WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
+ WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
+ WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
+ WPA_INVALID_MDIE, WPA_INVALID_PROTO
+};
+
+int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ const u8 *mdie, size_t mdie_len);
+int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
+struct wpa_state_machine *
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr);
+int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm);
+void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm);
+void wpa_auth_sta_deinit(struct wpa_state_machine *sm);
+void wpa_receive(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ u8 *data, size_t data_len);
+typedef enum {
+ WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
+ WPA_REAUTH_EAPOL, WPA_ASSOC_FT
+} wpa_event;
+void wpa_remove_ptk(struct wpa_state_machine *sm);
+int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event);
+void wpa_auth_sm_notify(struct wpa_state_machine *sm);
+void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth);
+int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen);
+int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen);
+void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth);
+int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
+int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
+int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
+int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
+int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
+ struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
+wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm);
+void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm);
+const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth,
+ size_t *len);
+int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+ int session_timeout, struct eapol_state_machine *eapol);
+int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
+ const u8 *pmk, size_t len, const u8 *sta_addr,
+ int session_timeout,
+ struct eapol_state_machine *eapol);
+int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
+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 */
+
+#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
new file mode 100644
index 0000000..65f5f4c
--- /dev/null
+++ b/src/ap/wpa_auth_ft.c
@@ -0,0 +1,1779 @@
+/*
+ * hostapd - IEEE 802.11r - Fast BSS Transition
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wmm.h"
+#include "wpa_auth.h"
+#include "wpa_auth_i.h"
+#include "wpa_auth_ie.h"
+
+
+#ifdef CONFIG_IEEE80211R
+
+struct wpa_ft_ies {
+ const u8 *mdie;
+ size_t mdie_len;
+ const u8 *ftie;
+ size_t ftie_len;
+ const u8 *r1kh_id;
+ const u8 *gtk;
+ size_t gtk_len;
+ const u8 *r0kh_id;
+ size_t r0kh_id_len;
+ const u8 *rsn;
+ size_t rsn_len;
+ const u8 *rsn_pmkid;
+ const u8 *ric;
+ size_t ric_len;
+};
+
+
+static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+ struct wpa_ft_ies *parse);
+
+
+static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
+ const u8 *data, size_t data_len)
+{
+ if (wpa_auth->cb.send_ether == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
+ return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB,
+ data, data_len);
+}
+
+
+static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
+ const u8 *dst, const u8 *data, size_t data_len)
+{
+ if (wpa_auth->cb.send_ft_action == NULL)
+ return -1;
+ return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst,
+ data, data_len);
+}
+
+
+static struct wpa_state_machine *
+wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+{
+ if (wpa_auth->cb.add_sta == NULL)
+ return NULL;
+ return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr);
+}
+
+
+int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+ u8 *pos = buf;
+ u8 capab;
+ if (len < 2 + sizeof(struct rsn_mdie))
+ return -1;
+
+ *pos++ = WLAN_EID_MOBILITY_DOMAIN;
+ *pos++ = MOBILITY_DOMAIN_ID_LEN + 1;
+ os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
+ pos += MOBILITY_DOMAIN_ID_LEN;
+ capab = 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);
+
+ if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+ f.nonce, frame.nonce) < 0)
+ return -1;
+
+ wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
+
+ return 0;
+}
+
+
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
+ struct wpa_ptk *ptk, size_t ptk_len)
+{
+ u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN];
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
+ const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
+ size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len;
+ const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
+ const u8 *ssid = sm->wpa_auth->conf.ssid;
+ size_t ssid_len = sm->wpa_auth->conf.ssid_len;
+
+
+ if (sm->xxkey_len == 0) {
+ wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
+ "derivation");
+ return -1;
+ }
+
+ wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
+ r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
+ wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
+ sm->pairwise);
+
+ wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
+ pmk_r1, sm->pmk_r1_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN);
+ wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
+ sm->pairwise);
+
+ wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+ sm->wpa_auth->addr, sm->pmk_r1_name,
+ (u8 *) ptk, ptk_len, ptk_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+ return 0;
+}
+
+
+static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, int idx, u8 *seq)
+{
+ if (wpa_auth->cb.get_seqnum == NULL)
+ return -1;
+ return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
+}
+
+
+static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+ u8 *subelem;
+ struct wpa_group *gsm = sm->group;
+ size_t subelem_len, pad_len;
+ const u8 *key;
+ size_t key_len;
+ u8 keybuf[32];
+
+ key_len = gsm->GTK_len;
+ if (key_len > sizeof(keybuf))
+ return NULL;
+
+ /*
+ * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less
+ * than 16 bytes.
+ */
+ pad_len = key_len % 8;
+ if (pad_len)
+ pad_len = 8 - pad_len;
+ if (key_len + pad_len < 16)
+ pad_len += 8;
+ if (pad_len) {
+ os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
+ os_memset(keybuf + key_len, 0, pad_len);
+ keybuf[key_len] = 0xdd;
+ key_len += pad_len;
+ key = keybuf;
+ } else
+ key = gsm->GTK[gsm->GN - 1];
+
+ /*
+ * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
+ * Key[5..32].
+ */
+ subelem_len = 13 + key_len + 8;
+ subelem = os_zalloc(subelem_len);
+ if (subelem == NULL)
+ return NULL;
+
+ subelem[0] = FTIE_SUBELEM_GTK;
+ subelem[1] = 11 + key_len + 8;
+ /* Key ID in B0-B1 of Key Info */
+ WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
+ subelem[4] = gsm->GTK_len;
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
+ if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) {
+ os_free(subelem);
+ return NULL;
+ }
+
+ *len = subelem_len;
+ return subelem;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+ u8 *subelem, *pos;
+ struct wpa_group *gsm = sm->group;
+ size_t subelem_len;
+
+ /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
+ * Key[16+8] */
+ subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8;
+ subelem = os_zalloc(subelem_len);
+ if (subelem == NULL)
+ return NULL;
+
+ pos = subelem;
+ *pos++ = FTIE_SUBELEM_IGTK;
+ *pos++ = subelem_len - 2;
+ WPA_PUT_LE16(pos, gsm->GN_igtk);
+ pos += 2;
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
+ pos += 6;
+ *pos++ = WPA_IGTK_LEN;
+ if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8,
+ gsm->IGTK[gsm->GN_igtk - 4], pos)) {
+ os_free(subelem);
+ return NULL;
+ }
+
+ *len = subelem_len;
+ return subelem;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count,
+ const u8 *ies, size_t ies_len)
+{
+ struct ieee802_11_elems parse;
+ struct rsn_rdie *rdie;
+
+ wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d",
+ id, descr_count);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)",
+ ies, ies_len);
+
+ if (end - pos < (int) sizeof(*rdie)) {
+ wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE");
+ return pos;
+ }
+
+ *pos++ = WLAN_EID_RIC_DATA;
+ *pos++ = sizeof(*rdie);
+ rdie = (struct rsn_rdie *) pos;
+ rdie->id = id;
+ rdie->descr_count = 0;
+ rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+ pos += sizeof(*rdie);
+
+ if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs");
+ rdie->status_code =
+ host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+ return pos;
+ }
+
+#ifdef NEED_AP_MLME
+ if (parse.wmm_tspec) {
+ struct wmm_tspec_element *tspec;
+ int res;
+
+ if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) {
+ wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE "
+ "(%d)", (int) parse.wmm_tspec_len);
+ rdie->status_code =
+ host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+ return pos;
+ }
+ if (end - pos < (int) sizeof(*tspec)) {
+ wpa_printf(MSG_ERROR, "FT: Not enough room for "
+ "response TSPEC");
+ rdie->status_code =
+ host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+ return pos;
+ }
+ tspec = (struct wmm_tspec_element *) pos;
+ os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
+ res = wmm_process_tspec(tspec);
+ wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res);
+ if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS)
+ rdie->status_code =
+ host_to_le16(WLAN_STATUS_INVALID_PARAMETERS);
+ else if (res == WMM_ADDTS_STATUS_REFUSED)
+ rdie->status_code =
+ host_to_le16(WLAN_STATUS_REQUEST_DECLINED);
+ else {
+ /* TSPEC accepted; include updated TSPEC in response */
+ rdie->descr_count = 1;
+ pos += sizeof(*tspec);
+ }
+ return pos;
+ }
+#endif /* NEED_AP_MLME */
+
+ wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
+ rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+ return pos;
+}
+
+
+static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len)
+{
+ const u8 *rpos, *start;
+ const struct rsn_rdie *rdie;
+
+ wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len);
+
+ rpos = ric;
+ while (rpos + sizeof(*rdie) < ric + ric_len) {
+ if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) ||
+ rpos + 2 + rpos[1] > ric + ric_len)
+ break;
+ rdie = (const struct rsn_rdie *) (rpos + 2);
+ rpos += 2 + rpos[1];
+ start = rpos;
+
+ while (rpos + 2 <= ric + ric_len &&
+ rpos + 2 + rpos[1] <= ric + ric_len) {
+ if (rpos[0] == WLAN_EID_RIC_DATA)
+ break;
+ rpos += 2 + rpos[1];
+ }
+ pos = wpa_ft_process_rdie(pos, end, rdie->id,
+ rdie->descr_count,
+ start, rpos - start);
+ }
+
+ return pos;
+}
+
+
+u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
+ size_t max_len, int auth_alg,
+ const u8 *req_ies, size_t req_ies_len)
+{
+ u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
+ size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
+ int res;
+ struct wpa_auth_config *conf;
+ struct rsn_ftie *_ftie;
+ struct wpa_ft_ies parse;
+ u8 *ric_start;
+ u8 *anonce, *snonce;
+
+ if (sm == NULL)
+ return pos;
+
+ conf = &sm->wpa_auth->conf;
+
+ if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
+ sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK)
+ return pos;
+
+ end = pos + max_len;
+
+ if (auth_alg == WLAN_AUTH_FT) {
+ /*
+ * RSN (only present if this is a Reassociation Response and
+ * part of a fast BSS transition)
+ */
+ res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
+ if (res < 0)
+ return pos;
+ rsnie = pos;
+ rsnie_len = res;
+ pos += res;
+ }
+
+ /* Mobility Domain Information */
+ res = wpa_write_mdie(conf, pos, end - pos);
+ if (res < 0)
+ return pos;
+ mdie = pos;
+ mdie_len = res;
+ pos += res;
+
+ /* Fast BSS Transition Information */
+ if (auth_alg == WLAN_AUTH_FT) {
+ subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
+ r0kh_id = sm->r0kh_id;
+ r0kh_id_len = sm->r0kh_id_len;
+ anonce = sm->ANonce;
+ snonce = sm->SNonce;
+#ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_frame_prot) {
+ u8 *igtk;
+ size_t igtk_len;
+ u8 *nbuf;
+ igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
+ if (igtk == NULL) {
+ os_free(subelem);
+ return pos;
+ }
+ nbuf = os_realloc(subelem, subelem_len + igtk_len);
+ if (nbuf == NULL) {
+ os_free(subelem);
+ os_free(igtk);
+ return pos;
+ }
+ subelem = nbuf;
+ os_memcpy(subelem + subelem_len, igtk, igtk_len);
+ subelem_len += igtk_len;
+ os_free(igtk);
+ }
+#endif /* CONFIG_IEEE80211W */
+ } else {
+ r0kh_id = conf->r0_key_holder;
+ r0kh_id_len = conf->r0_key_holder_len;
+ anonce = NULL;
+ snonce = NULL;
+ }
+ res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos,
+ end - pos, subelem, subelem_len);
+ os_free(subelem);
+ if (res < 0)
+ return pos;
+ ftie = pos;
+ ftie_len = res;
+ pos += res;
+
+ os_free(sm->assoc_resp_ftie);
+ sm->assoc_resp_ftie = os_malloc(ftie_len);
+ if (sm->assoc_resp_ftie)
+ os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
+
+ _ftie = (struct rsn_ftie *) (ftie + 2);
+ if (auth_alg == WLAN_AUTH_FT)
+ _ftie->mic_control[1] = 3; /* Information element count */
+
+ ric_start = pos;
+ if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
+ pos = wpa_ft_process_ric(pos, end, parse.ric, parse.ric_len);
+ if (auth_alg == WLAN_AUTH_FT)
+ _ftie->mic_control[1] +=
+ ieee802_11_ie_count(ric_start,
+ pos - ric_start);
+ }
+ if (ric_start == pos)
+ ric_start = NULL;
+
+ if (auth_alg == WLAN_AUTH_FT &&
+ wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6,
+ mdie, mdie_len, ftie, ftie_len,
+ rsnie, rsnie_len,
+ ric_start, ric_start ? pos - ric_start : 0,
+ _ftie->mic) < 0)
+ wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+
+ return pos;
+}
+
+
+static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
+ struct wpa_ft_ies *parse)
+{
+ const u8 *end, *pos;
+
+ parse->ftie = ie;
+ parse->ftie_len = ie_len;
+
+ pos = ie + sizeof(struct rsn_ftie);
+ end = ie + ie_len;
+
+ while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+ switch (pos[0]) {
+ case FTIE_SUBELEM_R1KH_ID:
+ if (pos[1] != FT_R1KH_ID_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
+ "length in FTIE: %d", pos[1]);
+ return -1;
+ }
+ parse->r1kh_id = pos + 2;
+ break;
+ case FTIE_SUBELEM_GTK:
+ parse->gtk = pos + 2;
+ parse->gtk_len = pos[1];
+ break;
+ case FTIE_SUBELEM_R0KH_ID:
+ if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
+ "length in FTIE: %d", pos[1]);
+ return -1;
+ }
+ parse->r0kh_id = pos + 2;
+ parse->r0kh_id_len = pos[1];
+ break;
+ }
+
+ pos += 2 + pos[1];
+ }
+
+ return 0;
+}
+
+
+static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+ struct wpa_ft_ies *parse)
+{
+ const u8 *end, *pos;
+ struct wpa_ie_data data;
+ int ret;
+ const struct rsn_ftie *ftie;
+ int prot_ie_count = 0;
+
+ os_memset(parse, 0, sizeof(*parse));
+ if (ies == NULL)
+ return 0;
+
+ pos = ies;
+ end = ies + ies_len;
+ while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+ switch (pos[0]) {
+ case WLAN_EID_RSN:
+ parse->rsn = pos + 2;
+ parse->rsn_len = pos[1];
+ ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
+ parse->rsn_len + 2,
+ &data);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse "
+ "RSN IE: %d", ret);
+ return -1;
+ }
+ if (data.num_pmkid == 1 && data.pmkid)
+ parse->rsn_pmkid = data.pmkid;
+ break;
+ case WLAN_EID_MOBILITY_DOMAIN:
+ parse->mdie = pos + 2;
+ parse->mdie_len = pos[1];
+ break;
+ case WLAN_EID_FAST_BSS_TRANSITION:
+ if (pos[1] < sizeof(*ftie))
+ return -1;
+ ftie = (const struct rsn_ftie *) (pos + 2);
+ prot_ie_count = ftie->mic_control[1];
+ if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+ return -1;
+ break;
+ case WLAN_EID_RIC_DATA:
+ if (parse->ric == NULL)
+ parse->ric = pos;
+ }
+
+ pos += 2 + pos[1];
+ }
+
+ if (prot_ie_count == 0)
+ return 0; /* no MIC */
+
+ /*
+ * Check that the protected IE count matches with IEs included in the
+ * frame.
+ */
+ if (parse->rsn)
+ prot_ie_count--;
+ if (parse->mdie)
+ prot_ie_count--;
+ if (parse->ftie)
+ prot_ie_count--;
+ if (prot_ie_count < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
+ "the protected IE count");
+ return -1;
+ }
+
+ if (prot_ie_count == 0 && parse->ric) {
+ wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
+ "included in protected IE count");
+ return -1;
+ }
+
+ /* Determine the end of the RIC IE(s) */
+ pos = parse->ric;
+ while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
+ prot_ie_count) {
+ prot_ie_count--;
+ pos += 2 + pos[1];
+ }
+ parse->ric_len = pos - parse->ric;
+ if (prot_ie_count) {
+ wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
+ "frame", (int) prot_ie_count);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
+ int vlan_id,
+ enum wpa_alg alg, const u8 *addr, int idx,
+ u8 *key, size_t key_len)
+{
+ if (wpa_auth->cb.set_key == NULL)
+ return -1;
+ return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
+ key, key_len);
+}
+
+
+void wpa_ft_install_ptk(struct wpa_state_machine *sm)
+{
+ enum wpa_alg alg;
+ int klen;
+
+ /* MLME-SETKEYS.request(PTK) */
+ if (sm->pairwise == WPA_CIPHER_TKIP) {
+ alg = WPA_ALG_TKIP;
+ klen = 32;
+ } else if (sm->pairwise == WPA_CIPHER_CCMP) {
+ alg = WPA_ALG_CCMP;
+ klen = 16;
+ } else {
+ wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip "
+ "PTK configuration", sm->pairwise);
+ return;
+ }
+
+ /* FIX: add STA entry to kernel/driver here? The set_key will fail
+ * most likely without this.. At the moment, STA entry is added only
+ * after association has been completed. This function will be called
+ * again after association to get the PTK configured, but that could be
+ * optimized by adding the STA entry earlier.
+ */
+ if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+ sm->PTK.tk1, klen))
+ return;
+
+ /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+ sm->pairwise_set = TRUE;
+}
+
+
+static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ u8 **resp_ies, size_t *resp_ies_len)
+{
+ struct rsn_mdie *mdie;
+ struct rsn_ftie *ftie;
+ u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ struct wpa_auth_config *conf;
+ struct wpa_ft_ies parse;
+ size_t buflen, ptk_len;
+ int ret;
+ u8 *pos, *end;
+ int pairwise;
+
+ *resp_ies = NULL;
+ *resp_ies_len = 0;
+
+ sm->pmk_r1_name_valid = 0;
+ conf = &sm->wpa_auth->conf;
+
+ wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
+ ies, ies_len);
+
+ if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ mdie = (struct rsn_mdie *) parse.mdie;
+ if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+ os_memcmp(mdie->mobility_domain,
+ sm->wpa_auth->conf.mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+ return WLAN_STATUS_INVALID_MDIE;
+ }
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+
+ if (parse.r0kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID",
+ parse.r0kh_id, parse.r0kh_id_len);
+ os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
+ sm->r0kh_id_len = parse.r0kh_id_len;
+
+ if (parse.rsn_pmkid == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
+ parse.rsn_pmkid, WPA_PMK_NAME_LEN);
+ wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+ sm->wpa_auth->conf.r1_key_holder, sm->addr,
+ pmk_r1_name);
+ wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
+ pmk_r1_name, WPA_PMK_NAME_LEN);
+
+ if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
+ &pairwise) < 0) {
+ if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id,
+ sm->r0kh_id_len, parse.rsn_pmkid) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Did not have matching "
+ "PMK-R1 and unknown R0KH-ID");
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ /*
+ * TODO: Should return "status pending" (and the caller should
+ * not send out response now). The real response will be sent
+ * once the response from R0KH is received.
+ */
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
+ sm->pmk_r1_name_valid = 1;
+ os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+
+ if (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_CCMP ? 64 : 48;
+ wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+ sm->wpa_auth->addr, pmk_r1_name,
+ (u8 *) &sm->PTK, ptk_len, ptk_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
+ (u8 *) &sm->PTK, ptk_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+ sm->pairwise = pairwise;
+ wpa_ft_install_ptk(sm);
+
+ buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+ 2 + FT_R1KH_ID_LEN + 200;
+ *resp_ies = os_zalloc(buflen);
+ if (*resp_ies == NULL) {
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ pos = *resp_ies;
+ end = *resp_ies + buflen;
+
+ ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid);
+ if (ret < 0) {
+ os_free(*resp_ies);
+ *resp_ies = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ pos += ret;
+
+ ret = wpa_write_mdie(conf, pos, end - pos);
+ if (ret < 0) {
+ os_free(*resp_ies);
+ *resp_ies = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ pos += ret;
+
+ ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len,
+ sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0);
+ if (ret < 0) {
+ os_free(*resp_ies);
+ *resp_ies = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ pos += ret;
+
+ *resp_ies_len = pos - *resp_ies;
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+ u16 auth_transaction, const u8 *ies, size_t ies_len,
+ void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
+ u16 auth_transaction, u16 status,
+ const u8 *ies, size_t ies_len),
+ void *ctx)
+{
+ u16 status;
+ u8 *resp_ies;
+ size_t resp_ies_len;
+
+ if (sm == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
+ "WPA SM not available");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
+ " BSSID=" MACSTR " transaction=%d",
+ MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
+ status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
+ &resp_ies_len);
+
+ wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
+ " auth_transaction=%d status=%d",
+ MAC2STR(sm->addr), auth_transaction + 1, status);
+ wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
+ cb(ctx, sm->addr, bssid, auth_transaction + 1, status,
+ resp_ies, resp_ies_len);
+ os_free(resp_ies);
+}
+
+
+u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
+ size_t ies_len)
+{
+ struct wpa_ft_ies parse;
+ struct rsn_mdie *mdie;
+ struct rsn_ftie *ftie;
+ u8 mic[16];
+ unsigned int count;
+
+ if (sm == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
+
+ if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (parse.rsn == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (parse.rsn_pmkid == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match "
+ "with the PMKR1Name derived from auth request");
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+
+ mdie = (struct rsn_mdie *) parse.mdie;
+ if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+ os_memcmp(mdie->mobility_domain,
+ sm->wpa_auth->conf.mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+ return WLAN_STATUS_INVALID_MDIE;
+ }
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+ ftie->snonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+ sm->SNonce, WPA_NONCE_LEN);
+ return -1;
+ }
+
+ if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+ ftie->anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+ sm->ANonce, WPA_NONCE_LEN);
+ return -1;
+ }
+
+
+ if (parse.r0kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+ return -1;
+ }
+
+ if (parse.r0kh_id_len != sm->r0kh_id_len ||
+ os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
+ "the current R0KH-ID");
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
+ parse.r0kh_id, parse.r0kh_id_len);
+ wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+ sm->r0kh_id, sm->r0kh_id_len);
+ return -1;
+ }
+
+ if (parse.r1kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+ return -1;
+ }
+
+ if (os_memcmp(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
+ FT_R1KH_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
+ "ReassocReq");
+ wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE",
+ parse.r1kh_id, FT_R1KH_ID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
+ sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
+ return -1;
+ }
+
+ if (parse.rsn_pmkid == NULL ||
+ os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) {
+ wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
+ "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
+ return -1;
+ }
+
+ count = 3;
+ if (parse.ric)
+ count++;
+ if (ftie->mic_control[1] != count) {
+ wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
+ "Control: received %u expected %u",
+ ftie->mic_control[1], count);
+ return -1;
+ }
+
+ if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 5,
+ parse.mdie - 2, parse.mdie_len + 2,
+ parse.ftie - 2, parse.ftie_len + 2,
+ parse.rsn - 2, parse.rsn_len + 2,
+ parse.ric, parse.ric_len,
+ mic) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (os_memcmp(mic, ftie->mic, 16) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
+{
+ const u8 *sta_addr, *target_ap;
+ const u8 *ies;
+ size_t ies_len;
+ u8 action;
+ struct ft_rrb_frame *frame;
+
+ if (sm == NULL)
+ return -1;
+
+ /*
+ * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
+ * FT Request action frame body[variable]
+ */
+
+ if (len < 14) {
+ wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame "
+ "(len=%lu)", (unsigned long) len);
+ return -1;
+ }
+
+ action = data[1];
+ sta_addr = data + 2;
+ target_ap = data + 8;
+ ies = data + 14;
+ ies_len = len - 14;
+
+ wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR
+ " Target AP=" MACSTR " Action=%d)",
+ MAC2STR(sta_addr), MAC2STR(target_ap), action);
+
+ if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: "
+ "STA=" MACSTR " STA-Address=" MACSTR,
+ MAC2STR(sm->addr), MAC2STR(sta_addr));
+ return -1;
+ }
+
+ /*
+ * Do some sanity checking on the target AP address (not own and not
+ * broadcast. This could be extended to filter based on a list of known
+ * APs in the MD (if such a list were configured).
+ */
+ if ((target_ap[0] & 0x01) ||
+ os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action "
+ "frame");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len);
+
+ /* RRB - Forward action frame to the target AP */
+ frame = os_malloc(sizeof(*frame) + len);
+ frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+ frame->packet_type = FT_PACKET_REQUEST;
+ frame->action_length = host_to_le16(len);
+ os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN);
+ os_memcpy(frame + 1, data, len);
+
+ wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame,
+ sizeof(*frame) + len);
+ os_free(frame);
+
+ return 0;
+}
+
+
+static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
+ const u8 *current_ap, const u8 *sta_addr,
+ const u8 *body, size_t len)
+{
+ struct wpa_state_machine *sm;
+ u16 status;
+ u8 *resp_ies, *pos;
+ size_t resp_ies_len, rlen;
+ struct ft_rrb_frame *frame;
+
+ sm = wpa_ft_add_sta(wpa_auth, sta_addr);
+ if (sm == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on "
+ "RRB Request");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len);
+
+ status = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
+ &resp_ies_len);
+
+ wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
+ " CurrentAP=" MACSTR " status=%d",
+ MAC2STR(sm->addr), MAC2STR(current_ap), status);
+ wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
+
+ /* RRB - Forward action frame response to the Current AP */
+
+ /*
+ * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
+ * Status_Code[2] FT Request action frame body[variable]
+ */
+ rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
+
+ frame = os_malloc(sizeof(*frame) + rlen);
+ frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+ frame->packet_type = FT_PACKET_RESPONSE;
+ frame->action_length = host_to_le16(rlen);
+ os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN);
+ pos = (u8 *) (frame + 1);
+ *pos++ = WLAN_ACTION_FT;
+ *pos++ = 2; /* Action: Response */
+ os_memcpy(pos, sta_addr, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, wpa_auth->addr, ETH_ALEN);
+ pos += ETH_ALEN;
+ WPA_PUT_LE16(pos, status);
+ pos += 2;
+ if (resp_ies) {
+ os_memcpy(pos, resp_ies, resp_ies_len);
+ os_free(resp_ies);
+ }
+
+ wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame,
+ sizeof(*frame) + rlen);
+ os_free(frame);
+
+ return 0;
+}
+
+
+static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *data, size_t data_len)
+{
+ struct ft_r0kh_r1kh_pull_frame *frame, f;
+ struct ft_remote_r1kh *r1kh;
+ struct ft_r0kh_r1kh_resp_frame resp, r;
+ u8 pmk_r0[PMK_LEN];
+ int pairwise;
+
+ wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
+
+ if (data_len < sizeof(*frame))
+ return -1;
+
+ r1kh = wpa_auth->conf.r1kh_list;
+ while (r1kh) {
+ if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ r1kh = r1kh->next;
+ }
+ if (r1kh == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for "
+ "PMK-R1 pull source address " MACSTR,
+ MAC2STR(src_addr));
+ return -1;
+ }
+
+ frame = (struct ft_r0kh_r1kh_pull_frame *) data;
+ /* aes_unwrap() does not support inplace decryption, so use a temporary
+ * buffer for the data. */
+ if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+ frame->nonce, f.nonce) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
+ "request from " MACSTR, MAC2STR(src_addr));
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
+ f.nonce, sizeof(f.nonce));
+ wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
+ f.pmk_r0_name, WPA_PMK_NAME_LEN);
+ wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID="
+ MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
+
+ os_memset(&resp, 0, sizeof(resp));
+ resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+ resp.packet_type = FT_PACKET_R0KH_R1KH_RESP;
+ resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN);
+ os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN);
+
+ /* aes_wrap() does not support inplace encryption, so use a temporary
+ * buffer for the data. */
+ os_memcpy(r.nonce, f.nonce, sizeof(f.nonce));
+ os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN);
+ os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN);
+ if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0,
+ &pairwise) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for "
+ "PMK-R1 pull");
+ return -1;
+ }
+
+ wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
+ r.pmk_r1, r.pmk_r1_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
+ WPA_PMK_NAME_LEN);
+ r.pairwise = host_to_le16(pairwise);
+
+ if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+ r.nonce, resp.nonce) < 0) {
+ os_memset(pmk_r0, 0, PMK_LEN);
+ return -1;
+ }
+
+ os_memset(pmk_r0, 0, PMK_LEN);
+
+ wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp));
+
+ return 0;
+}
+
+
+static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *data, size_t data_len)
+{
+ struct ft_r0kh_r1kh_resp_frame *frame, f;
+ struct ft_remote_r0kh *r0kh;
+ int pairwise;
+
+ wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
+
+ if (data_len < sizeof(*frame))
+ return -1;
+
+ r0kh = wpa_auth->conf.r0kh_list;
+ while (r0kh) {
+ if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ r0kh = r0kh->next;
+ }
+ if (r0kh == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
+ "PMK-R0 pull response source address " MACSTR,
+ MAC2STR(src_addr));
+ return -1;
+ }
+
+ frame = (struct ft_r0kh_r1kh_resp_frame *) data;
+ /* aes_unwrap() does not support inplace decryption, so use a temporary
+ * buffer for the data. */
+ if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+ frame->nonce, f.nonce) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
+ "response from " MACSTR, MAC2STR(src_addr));
+ return -1;
+ }
+
+ if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN)
+ != 0) {
+ wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a "
+ "matching R1KH-ID");
+ return -1;
+ }
+
+ /* TODO: verify that <nonce,s1kh_id> matches with a pending request
+ * and call this requests callback function to finish request
+ * processing */
+
+ pairwise = le_to_host16(f.pairwise);
+ wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
+ f.nonce, sizeof(f.nonce));
+ wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID="
+ MACSTR " pairwise=0x%x",
+ MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
+ f.pmk_r1, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
+ f.pmk_r1_name, WPA_PMK_NAME_LEN);
+
+ wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
+ pairwise);
+ os_memset(f.pmk_r1, 0, PMK_LEN);
+
+ return 0;
+}
+
+
+static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *data, size_t data_len)
+{
+ struct ft_r0kh_r1kh_push_frame *frame, f;
+ struct ft_remote_r0kh *r0kh;
+ struct os_time now;
+ os_time_t tsend;
+ int pairwise;
+
+ wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
+
+ if (data_len < sizeof(*frame))
+ return -1;
+
+ r0kh = wpa_auth->conf.r0kh_list;
+ while (r0kh) {
+ if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ r0kh = r0kh->next;
+ }
+ if (r0kh == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
+ "PMK-R0 push source address " MACSTR,
+ MAC2STR(src_addr));
+ return -1;
+ }
+
+ frame = (struct ft_r0kh_r1kh_push_frame *) data;
+ /* aes_unwrap() does not support inplace decryption, so use a temporary
+ * buffer for the data. */
+ if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+ frame->timestamp, f.timestamp) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
+ MACSTR, MAC2STR(src_addr));
+ return -1;
+ }
+
+ os_get_time(&now);
+ tsend = WPA_GET_LE32(f.timestamp);
+ if ((now.sec > tsend && now.sec - tsend > 60) ||
+ (now.sec < tsend && tsend - now.sec > 60)) {
+ wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid "
+ "timestamp: sender time %d own time %d\n",
+ (int) tsend, (int) now.sec);
+ return -1;
+ }
+
+ if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN)
+ != 0) {
+ wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching "
+ "R1KH-ID (received " MACSTR " own " MACSTR ")",
+ MAC2STR(f.r1kh_id),
+ MAC2STR(wpa_auth->conf.r1_key_holder));
+ return -1;
+ }
+
+ pairwise = le_to_host16(f.pairwise);
+ wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID="
+ MACSTR " pairwise=0x%x",
+ MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1",
+ f.pmk_r1, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name",
+ f.pmk_r1_name, WPA_PMK_NAME_LEN);
+
+ wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
+ pairwise);
+ os_memset(f.pmk_r1, 0, PMK_LEN);
+
+ return 0;
+}
+
+
+int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+ const u8 *data, size_t data_len)
+{
+ struct ft_rrb_frame *frame;
+ u16 alen;
+ const u8 *pos, *end, *start;
+ u8 action;
+ const u8 *sta_addr, *target_ap_addr;
+
+ wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR,
+ MAC2STR(src_addr));
+
+ if (data_len < sizeof(*frame)) {
+ wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)",
+ (unsigned long) data_len);
+ return -1;
+ }
+
+ pos = data;
+ frame = (struct ft_rrb_frame *) pos;
+ pos += sizeof(*frame);
+
+ alen = le_to_host16(frame->action_length);
+ wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d "
+ "action_length=%d ap_address=" MACSTR,
+ frame->frame_type, frame->packet_type, alen,
+ MAC2STR(frame->ap_address));
+
+ if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) {
+ /* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */
+ wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with "
+ "unrecognized type %d", frame->frame_type);
+ return -1;
+ }
+
+ if (alen > data_len - sizeof(*frame)) {
+ wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action "
+ "frame");
+ return -1;
+ }
+
+ if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL)
+ return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len);
+ if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP)
+ return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len);
+ if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH)
+ return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len);
+
+ wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
+
+ if (alen < 1 + 1 + 2 * ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough "
+ "room for Action Frame body); alen=%lu",
+ (unsigned long) alen);
+ return -1;
+ }
+ start = pos;
+ end = pos + alen;
+
+ if (*pos != WLAN_ACTION_FT) {
+ wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category "
+ "%d", *pos);
+ return -1;
+ }
+
+ pos++;
+ action = *pos++;
+ sta_addr = pos;
+ pos += ETH_ALEN;
+ target_ap_addr = pos;
+ pos += ETH_ALEN;
+ wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr="
+ MACSTR " target_ap_addr=" MACSTR,
+ action, MAC2STR(sta_addr), MAC2STR(target_ap_addr));
+
+ if (frame->packet_type == FT_PACKET_REQUEST) {
+ wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request");
+
+ if (action != 1) {
+ wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in "
+ "RRB Request", action);
+ return -1;
+ }
+
+ if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Target AP address in the "
+ "RRB Request does not match with own "
+ "address");
+ return -1;
+ }
+
+ if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address,
+ sta_addr, pos, end - pos) < 0)
+ return -1;
+ } else if (frame->packet_type == FT_PACKET_RESPONSE) {
+ u16 status_code;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "FT: Not enough room for status "
+ "code in RRB Response");
+ return -1;
+ }
+ status_code = WPA_GET_LE16(pos);
+ pos += 2;
+
+ wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response "
+ "(status_code=%d)", status_code);
+
+ if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0)
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown "
+ "packet_type %d", frame->packet_type);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
+ struct wpa_ft_pmk_r0_sa *pmk_r0,
+ struct ft_remote_r1kh *r1kh,
+ const u8 *s1kh_id, int pairwise)
+{
+ struct ft_r0kh_r1kh_push_frame frame, f;
+ struct os_time now;
+
+ os_memset(&frame, 0, sizeof(frame));
+ frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+ frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH;
+ frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN);
+ os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
+
+ /* aes_wrap() does not support inplace encryption, so use a temporary
+ * buffer for the data. */
+ os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN);
+ os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
+ os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN);
+ wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
+ s1kh_id, f.pmk_r1, f.pmk_r1_name);
+ wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id));
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name,
+ WPA_PMK_NAME_LEN);
+ os_get_time(&now);
+ WPA_PUT_LE32(f.timestamp, now.sec);
+ f.pairwise = host_to_le16(pairwise);
+ if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+ f.timestamp, frame.timestamp) < 0)
+ return;
+
+ wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame));
+}
+
+
+void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+ struct wpa_ft_pmk_r0_sa *r0;
+ struct ft_remote_r1kh *r1kh;
+
+ if (!wpa_auth->conf.pmk_r1_push)
+ return;
+
+ r0 = wpa_auth->ft_pmk_cache->pmk_r0;
+ while (r0) {
+ if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0)
+ break;
+ r0 = r0->next;
+ }
+
+ if (r0 == NULL || r0->pmk_r1_pushed)
+ return;
+ r0->pmk_r1_pushed = 1;
+
+ wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
+ "for STA " MACSTR, MAC2STR(addr));
+
+ r1kh = wpa_auth->conf.r1kh_list;
+ while (r1kh) {
+ wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise);
+ r1kh = r1kh->next;
+ }
+}
+
+#endif /* CONFIG_IEEE80211R */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
new file mode 100644
index 0000000..b35b7ba
--- /dev/null
+++ b/src/ap/wpa_auth_glue.c
@@ -0,0 +1,571 @@
+/*
+ * hostapd / WPA authenticator glue code
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "eap_server/eap.h"
+#include "l2_packet/l2_packet.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "preauth_auth.h"
+#include "sta_info.h"
+#include "tkip_countermeasures.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "wpa_auth.h"
+
+
+static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
+ struct wpa_auth_config *wconf)
+{
+ wconf->wpa = conf->wpa;
+ wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
+ wconf->wpa_pairwise = conf->wpa_pairwise;
+ wconf->wpa_group = conf->wpa_group;
+ wconf->wpa_group_rekey = conf->wpa_group_rekey;
+ wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
+ wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
+ wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
+ wconf->rsn_pairwise = conf->rsn_pairwise;
+ wconf->rsn_preauth = conf->rsn_preauth;
+ wconf->eapol_version = conf->eapol_version;
+ wconf->peerkey = conf->peerkey;
+ wconf->wmm_enabled = conf->wmm_enabled;
+ wconf->wmm_uapsd = conf->wmm_uapsd;
+ wconf->okc = conf->okc;
+#ifdef CONFIG_IEEE80211W
+ wconf->ieee80211w = conf->ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+ wconf->ssid_len = conf->ssid.ssid_len;
+ if (wconf->ssid_len > SSID_LEN)
+ wconf->ssid_len = SSID_LEN;
+ os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len);
+ os_memcpy(wconf->mobility_domain, conf->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+ if (conf->nas_identifier &&
+ os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) {
+ wconf->r0_key_holder_len = os_strlen(conf->nas_identifier);
+ os_memcpy(wconf->r0_key_holder, conf->nas_identifier,
+ wconf->r0_key_holder_len);
+ }
+ os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
+ wconf->r0_key_lifetime = conf->r0_key_lifetime;
+ wconf->reassociation_deadline = conf->reassociation_deadline;
+ wconf->r0kh_list = conf->r0kh_list;
+ wconf->r1kh_list = conf->r1kh_list;
+ wconf->pmk_r1_push = conf->pmk_r1_push;
+ wconf->ft_over_ds = conf->ft_over_ds;
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr,
+ logger_level level, const char *txt)
+{
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+ struct hostapd_data *hapd = ctx;
+ int hlevel;
+
+ switch (level) {
+ case LOGGER_WARNING:
+ hlevel = HOSTAPD_LEVEL_WARNING;
+ break;
+ case LOGGER_INFO:
+ hlevel = HOSTAPD_LEVEL_INFO;
+ break;
+ case LOGGER_DEBUG:
+ default:
+ hlevel = HOSTAPD_LEVEL_DEBUG;
+ break;
+ }
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+}
+
+
+static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr,
+ u16 reason)
+{
+ struct hostapd_data *hapd = ctx;
+ wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: "
+ "STA " MACSTR " reason %d",
+ __func__, MAC2STR(addr), reason);
+ ap_sta_disconnect(hapd, NULL, addr, reason);
+}
+
+
+static void hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr)
+{
+ struct hostapd_data *hapd = ctx;
+ michael_mic_failure(hapd, addr, 0);
+}
+
+
+static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr,
+ wpa_eapol_variable var, int value)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ if (sta == NULL)
+ return;
+ switch (var) {
+ case WPA_EAPOL_portEnabled:
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, value);
+ break;
+ case WPA_EAPOL_portValid:
+ ieee802_1x_notify_port_valid(sta->eapol_sm, value);
+ break;
+ case WPA_EAPOL_authorized:
+ ieee802_1x_set_sta_authorized(hapd, sta, value);
+ break;
+ case WPA_EAPOL_portControl_Auto:
+ if (sta->eapol_sm)
+ sta->eapol_sm->portControl = Auto;
+ break;
+ case WPA_EAPOL_keyRun:
+ if (sta->eapol_sm)
+ sta->eapol_sm->keyRun = value ? TRUE : FALSE;
+ break;
+ case WPA_EAPOL_keyAvailable:
+ if (sta->eapol_sm)
+ sta->eapol_sm->eap_if->eapKeyAvailable =
+ value ? TRUE : FALSE;
+ break;
+ case WPA_EAPOL_keyDone:
+ if (sta->eapol_sm)
+ sta->eapol_sm->keyDone = value ? TRUE : FALSE;
+ break;
+ case WPA_EAPOL_inc_EapolFramesTx:
+ if (sta->eapol_sm)
+ sta->eapol_sm->dot1xAuthEapolFramesTx++;
+ break;
+ }
+}
+
+
+static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr,
+ wpa_eapol_variable var)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ if (sta == NULL || sta->eapol_sm == NULL)
+ return -1;
+ switch (var) {
+ case WPA_EAPOL_keyRun:
+ return sta->eapol_sm->keyRun;
+ case WPA_EAPOL_keyAvailable:
+ return sta->eapol_sm->eap_if->eapKeyAvailable;
+ default:
+ return -1;
+ }
+}
+
+
+static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
+ const u8 *prev_psk)
+{
+ struct hostapd_data *hapd = ctx;
+ return hostapd_get_psk(hapd->conf, addr, prev_psk);
+}
+
+
+static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk,
+ size_t *len)
+{
+ struct hostapd_data *hapd = ctx;
+ const u8 *key;
+ size_t keylen;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL)
+ return -1;
+
+ key = ieee802_1x_get_key(sta->eapol_sm, &keylen);
+ if (key == NULL)
+ return -1;
+
+ if (keylen > *len)
+ keylen = *len;
+ os_memcpy(msk, key, keylen);
+ *len = keylen;
+
+ return 0;
+}
+
+
+static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
+ const u8 *addr, int idx, u8 *key,
+ size_t key_len)
+{
+ struct hostapd_data *hapd = ctx;
+ const char *ifname = hapd->conf->iface;
+
+ if (vlan_id > 0) {
+ ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
+ if (ifname == NULL)
+ return -1;
+ }
+
+ return 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->for_each_interface == NULL)
+ return -1;
+ data.cb = cb;
+ data.cb_ctx = cb_ctx;
+ return hapd->iface->for_each_interface(hapd->iface->interfaces,
+ wpa_auth_iface_iter, &data);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+struct wpa_auth_ft_iface_iter_data {
+ struct hostapd_data *src_hapd;
+ const u8 *dst;
+ const u8 *data;
+ size_t data_len;
+};
+
+
+static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
+{
+ struct wpa_auth_ft_iface_iter_data *idata = ctx;
+ struct hostapd_data *hapd;
+ size_t j;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (hapd == idata->src_hapd)
+ continue;
+ if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to "
+ "locally managed BSS " MACSTR "@%s -> "
+ MACSTR "@%s",
+ MAC2STR(idata->src_hapd->own_addr),
+ idata->src_hapd->conf->iface,
+ MAC2STR(hapd->own_addr), hapd->conf->iface);
+ 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->for_each_interface) {
+ int res;
+ struct wpa_auth_ft_iface_iter_data idata;
+ idata.src_hapd = hapd;
+ idata.dst = dst;
+ idata.data = data;
+ idata.data_len = data_len;
+ res = hapd->iface->for_each_interface(hapd->iface->interfaces,
+ hostapd_wpa_auth_ft_iter,
+ &idata);
+ if (res == 1)
+ return data_len;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ if (hapd->driver && hapd->driver->send_ether)
+ return hapd->driver->send_ether(hapd->drv_priv, dst,
+ hapd->own_addr, proto,
+ data, data_len);
+ if (hapd->l2 == NULL)
+ return -1;
+
+ 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 -1;
+}
+
+
+#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);
+ os_free(m);
+ return res;
+}
+
+
+static struct wpa_state_machine *
+hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_sta_add(hapd, sta_addr);
+ if (sta == NULL)
+ return NULL;
+ if (sta->wpa_sm) {
+ sta->auth_alg = WLAN_AUTH_FT;
+ return sta->wpa_sm;
+ }
+
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr);
+ if (sta->wpa_sm == NULL) {
+ ap_free_sta(hapd, sta);
+ return NULL;
+ }
+ sta->auth_alg = WLAN_AUTH_FT;
+
+ return sta->wpa_sm;
+}
+
+
+static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+ size_t len)
+{
+ struct hostapd_data *hapd = ctx;
+ 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));
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+int hostapd_setup_wpa(struct hostapd_data *hapd)
+{
+ struct wpa_auth_config _conf;
+ struct wpa_auth_callbacks cb;
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+
+ hostapd_wpa_auth_conf(hapd->conf, &_conf);
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
+ _conf.tx_status = 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;
+#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, &wpa_auth_conf);
+ wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
+}
+
+
+void hostapd_deinit_wpa(struct hostapd_data *hapd)
+{
+ rsn_preauth_iface_deinit(hapd);
+ if (hapd->wpa_auth) {
+ wpa_deinit(hapd->wpa_auth);
+ hapd->wpa_auth = NULL;
+
+ if (hostapd_set_privacy(hapd, 0)) {
+ wpa_printf(MSG_DEBUG, "Could not disable "
+ "PrivacyInvoked for interface %s",
+ hapd->conf->iface);
+ }
+
+ if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
+ wpa_printf(MSG_DEBUG, "Could not remove generic "
+ "information element from interface %s",
+ hapd->conf->iface);
+ }
+ }
+ ieee802_1x_deinit(hapd);
+
+#ifdef CONFIG_IEEE80211R
+ l2_packet_deinit(hapd->l2);
+#endif /* CONFIG_IEEE80211R */
+}
diff --git a/src/ap/wpa_auth_glue.h b/src/ap/wpa_auth_glue.h
new file mode 100644
index 0000000..79d7e05
--- /dev/null
+++ b/src/ap/wpa_auth_glue.h
@@ -0,0 +1,22 @@
+/*
+ * hostapd / WPA authenticator glue code
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_AUTH_GLUE_H
+#define WPA_AUTH_GLUE_H
+
+int hostapd_setup_wpa(struct hostapd_data *hapd);
+void hostapd_reconfig_wpa(struct hostapd_data *hapd);
+void hostapd_deinit_wpa(struct hostapd_data *hapd);
+
+#endif /* WPA_AUTH_GLUE_H */
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
new file mode 100644
index 0000000..67a5c3b
--- /dev/null
+++ b/src/ap/wpa_auth_i.h
@@ -0,0 +1,234 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_AUTH_I_H
+#define WPA_AUTH_I_H
+
+/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
+#define RSNA_MAX_EAPOL_RETRIES 4
+
+struct wpa_group;
+
+struct wpa_stsl_negotiation {
+ struct wpa_stsl_negotiation *next;
+ u8 initiator[ETH_ALEN];
+ u8 peer[ETH_ALEN];
+};
+
+
+struct wpa_state_machine {
+ struct wpa_authenticator *wpa_auth;
+ struct wpa_group *group;
+
+ u8 addr[ETH_ALEN];
+
+ enum {
+ WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
+ WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2,
+ WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART,
+ WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2,
+ WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE
+ } wpa_ptk_state;
+
+ enum {
+ WPA_PTK_GROUP_IDLE = 0,
+ WPA_PTK_GROUP_REKEYNEGOTIATING,
+ WPA_PTK_GROUP_REKEYESTABLISHED,
+ WPA_PTK_GROUP_KEYERROR
+ } wpa_ptk_group_state;
+
+ Boolean Init;
+ Boolean DeauthenticationRequest;
+ Boolean AuthenticationRequest;
+ Boolean ReAuthenticationRequest;
+ Boolean Disconnect;
+ int TimeoutCtr;
+ int GTimeoutCtr;
+ Boolean TimeoutEvt;
+ Boolean EAPOLKeyReceived;
+ Boolean EAPOLKeyPairwise;
+ Boolean EAPOLKeyRequest;
+ Boolean MICVerified;
+ Boolean GUpdateStationKeys;
+ u8 ANonce[WPA_NONCE_LEN];
+ u8 SNonce[WPA_NONCE_LEN];
+ u8 PMK[PMK_LEN];
+ struct wpa_ptk PTK;
+ Boolean PTK_valid;
+ Boolean pairwise_set;
+ int keycount;
+ Boolean Pair;
+ struct {
+ u8 counter[WPA_REPLAY_COUNTER_LEN];
+ Boolean valid;
+ } key_replay[RSNA_MAX_EAPOL_RETRIES];
+ Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */
+ Boolean PTKRequest; /* not in IEEE 802.11i state machine */
+ Boolean has_GTK;
+ Boolean PtkGroupInit; /* init request for PTK Group state machine */
+
+ u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */
+ size_t last_rx_eapol_key_len;
+
+ unsigned int changed:1;
+ unsigned int in_step_loop:1;
+ unsigned int pending_deinit:1;
+ unsigned int started:1;
+ unsigned int mgmt_frame_prot:1;
+#ifdef CONFIG_IEEE80211R
+ unsigned int ft_completed:1;
+ unsigned int pmk_r1_name_valid:1;
+#endif /* CONFIG_IEEE80211R */
+
+ u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
+ int req_replay_counter_used;
+
+ u8 *wpa_ie;
+ size_t wpa_ie_len;
+
+ enum {
+ WPA_VERSION_NO_WPA = 0 /* WPA not used */,
+ WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */,
+ WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */
+ } wpa;
+ int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+ int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ u32 dot11RSNAStatsTKIPLocalMICFailures;
+ u32 dot11RSNAStatsTKIPRemoteMICFailures;
+
+#ifdef CONFIG_IEEE80211R
+ u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+ size_t xxkey_len;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
+ * Request */
+ u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
+ size_t r0kh_id_len;
+ u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
+ * message 2/4 */
+ u8 *assoc_resp_ftie;
+#endif /* CONFIG_IEEE80211R */
+
+ 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/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
new file mode 100644
index 0000000..5e8d134
--- /dev/null
+++ b/src/ap/wpa_auth_ie.c
@@ -0,0 +1,824 @@
+/*
+ * hostapd - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wpa_auth_ie.h"
+#include "wpa_auth_i.h"
+
+
+#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;
+
+ hdr = (struct wpa_ie_hdr *) buf;
+ hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
+ RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
+ WPA_PUT_LE16(hdr->version, WPA_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ if (conf->wpa_group == WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+ } else if (conf->wpa_group == WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+ } else if (conf->wpa_group == WPA_CIPHER_WEP104) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104);
+ } else if (conf->wpa_group == WPA_CIPHER_WEP40) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40);
+ } else {
+ wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
+ conf->wpa_group);
+ return -1;
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ num_suites = 0;
+ count = pos;
+ pos += 2;
+
+ if (conf->wpa_pairwise & WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_pairwise & WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_pairwise & WPA_CIPHER_NONE) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+
+ if (num_suites == 0) {
+ wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
+ conf->wpa_pairwise);
+ return -1;
+ }
+ WPA_PUT_LE16(count, num_suites);
+
+ num_suites = 0;
+ count = pos;
+ pos += 2;
+
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+
+ if (num_suites == 0) {
+ wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
+ conf->wpa_key_mgmt);
+ return -1;
+ }
+ WPA_PUT_LE16(count, num_suites);
+
+ /* WPA Capabilities; use defaults, so no need to include it */
+
+ hdr->len = (pos - buf) - 2;
+
+ return pos - buf;
+}
+
+
+int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+ const u8 *pmkid)
+{
+ struct rsn_ie_hdr *hdr;
+ int num_suites;
+ u8 *pos, *count;
+ u16 capab;
+
+ hdr = (struct rsn_ie_hdr *) buf;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ if (conf->wpa_group == WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ } else if (conf->wpa_group == WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ } else if (conf->wpa_group == WPA_CIPHER_WEP104) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104);
+ } else if (conf->wpa_group == WPA_CIPHER_WEP40) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40);
+ } else {
+ wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
+ conf->wpa_group);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ num_suites = 0;
+ count = pos;
+ pos += 2;
+
+#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->rsn_pairwise & WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->rsn_pairwise & WPA_CIPHER_NONE) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+
+#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_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_auth->conf.wpa_key_mgmt &
+ (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) {
+ res = wpa_write_mdie(&wpa_auth->conf, pos,
+ buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+#endif /* CONFIG_IEEE80211R */
+ if (wpa_auth->conf.wpa & WPA_PROTO_WPA) {
+ res = wpa_write_wpa_ie(&wpa_auth->conf,
+ pos, buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+
+ os_free(wpa_auth->wpa_ie);
+ wpa_auth->wpa_ie = os_malloc(pos - buf);
+ if (wpa_auth->wpa_ie == NULL)
+ return -1;
+ os_memcpy(wpa_auth->wpa_ie, buf, pos - buf);
+ wpa_auth->wpa_ie_len = pos - buf;
+
+ return 0;
+}
+
+
+u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
+ const u8 *data2, size_t data2_len)
+{
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + data_len + data2_len;
+ RSN_SELECTOR_PUT(pos, kde);
+ pos += RSN_SELECTOR_LEN;
+ os_memcpy(pos, data, data_len);
+ pos += data_len;
+ if (data2) {
+ os_memcpy(pos, data2, data2_len);
+ pos += data2_len;
+ }
+ return pos;
+}
+
+
+struct wpa_auth_okc_iter_data {
+ struct rsn_pmksa_cache_entry *pmksa;
+ const u8 *aa;
+ const u8 *spa;
+ const u8 *pmkid;
+};
+
+
+static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx)
+{
+ struct wpa_auth_okc_iter_data *data = ctx;
+ data->pmksa = pmksa_cache_get_okc(a->pmksa, data->aa, data->spa,
+ data->pmkid);
+ if (data->pmksa)
+ return 1;
+ return 0;
+}
+
+
+int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ const u8 *mdie, size_t mdie_len)
+{
+ struct wpa_ie_data data;
+ int ciphers, key_mgmt, res, version;
+ u32 selector;
+ size_t i;
+ const u8 *pmkid = NULL;
+
+ if (wpa_auth == NULL || sm == NULL)
+ return WPA_NOT_ENABLED;
+
+ if (wpa_ie == NULL || wpa_ie_len < 1)
+ return WPA_INVALID_IE;
+
+ if (wpa_ie[0] == WLAN_EID_RSN)
+ version = WPA_PROTO_RSN;
+ else
+ version = WPA_PROTO_WPA;
+
+ if (!(wpa_auth->conf.wpa & version)) {
+ wpa_printf(MSG_DEBUG, "Invalid WPA proto (%d) from " MACSTR,
+ version, MAC2STR(sm->addr));
+ return WPA_INVALID_PROTO;
+ }
+
+ if (version == WPA_PROTO_RSN) {
+ res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data);
+
+ selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ if (0) {
+ }
+#ifdef CONFIG_IEEE80211R
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+ selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
+ selector = RSN_AUTH_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+ selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+ else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+ selector = RSN_AUTH_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+ selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+ wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
+
+ selector = RSN_CIPHER_SUITE_CCMP;
+ if (data.pairwise_cipher & WPA_CIPHER_CCMP)
+ selector = RSN_CIPHER_SUITE_CCMP;
+ else if (data.pairwise_cipher & WPA_CIPHER_TKIP)
+ selector = RSN_CIPHER_SUITE_TKIP;
+ else if (data.pairwise_cipher & WPA_CIPHER_WEP104)
+ selector = RSN_CIPHER_SUITE_WEP104;
+ else if (data.pairwise_cipher & WPA_CIPHER_WEP40)
+ selector = RSN_CIPHER_SUITE_WEP40;
+ else if (data.pairwise_cipher & WPA_CIPHER_NONE)
+ selector = RSN_CIPHER_SUITE_NONE;
+ wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
+
+ selector = RSN_CIPHER_SUITE_CCMP;
+ if (data.group_cipher & WPA_CIPHER_CCMP)
+ selector = RSN_CIPHER_SUITE_CCMP;
+ else if (data.group_cipher & WPA_CIPHER_TKIP)
+ selector = RSN_CIPHER_SUITE_TKIP;
+ else if (data.group_cipher & WPA_CIPHER_WEP104)
+ selector = RSN_CIPHER_SUITE_WEP104;
+ else if (data.group_cipher & WPA_CIPHER_WEP40)
+ selector = RSN_CIPHER_SUITE_WEP40;
+ else if (data.group_cipher & WPA_CIPHER_NONE)
+ selector = RSN_CIPHER_SUITE_NONE;
+ wpa_auth->dot11RSNAGroupCipherSelected = selector;
+ } else {
+ res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data);
+
+ selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+ selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+ wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
+
+ selector = WPA_CIPHER_SUITE_TKIP;
+ if (data.pairwise_cipher & WPA_CIPHER_CCMP)
+ selector = WPA_CIPHER_SUITE_CCMP;
+ else if (data.pairwise_cipher & WPA_CIPHER_TKIP)
+ selector = WPA_CIPHER_SUITE_TKIP;
+ else if (data.pairwise_cipher & WPA_CIPHER_WEP104)
+ selector = WPA_CIPHER_SUITE_WEP104;
+ else if (data.pairwise_cipher & WPA_CIPHER_WEP40)
+ selector = WPA_CIPHER_SUITE_WEP40;
+ else if (data.pairwise_cipher & WPA_CIPHER_NONE)
+ selector = WPA_CIPHER_SUITE_NONE;
+ wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
+
+ selector = WPA_CIPHER_SUITE_TKIP;
+ if (data.group_cipher & WPA_CIPHER_CCMP)
+ selector = WPA_CIPHER_SUITE_CCMP;
+ else if (data.group_cipher & WPA_CIPHER_TKIP)
+ selector = WPA_CIPHER_SUITE_TKIP;
+ else if (data.group_cipher & WPA_CIPHER_WEP104)
+ selector = WPA_CIPHER_SUITE_WEP104;
+ else if (data.group_cipher & WPA_CIPHER_WEP40)
+ selector = WPA_CIPHER_SUITE_WEP40;
+ else if (data.group_cipher & WPA_CIPHER_NONE)
+ selector = WPA_CIPHER_SUITE_NONE;
+ wpa_auth->dot11RSNAGroupCipherSelected = selector;
+ }
+ if (res) {
+ wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from "
+ MACSTR " (res=%d)", MAC2STR(sm->addr), res);
+ wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len);
+ return WPA_INVALID_IE;
+ }
+
+ if (data.group_cipher != wpa_auth->conf.wpa_group) {
+ wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from "
+ MACSTR, data.group_cipher, MAC2STR(sm->addr));
+ return WPA_INVALID_GROUP;
+ }
+
+ key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt;
+ if (!key_mgmt) {
+ wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from "
+ MACSTR, data.key_mgmt, MAC2STR(sm->addr));
+ return WPA_INVALID_AKMP;
+ }
+ if (0) {
+ }
+#ifdef CONFIG_IEEE80211R
+ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+ else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
+ else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ else
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+
+ if (version == WPA_PROTO_RSN)
+ ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise;
+ else
+ ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise;
+ if (!ciphers) {
+ wpa_printf(MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) "
+ "from " MACSTR,
+ version == WPA_PROTO_RSN ? "RSN" : "WPA",
+ data.pairwise_cipher, MAC2STR(sm->addr));
+ return WPA_INVALID_PAIRWISE;
+ }
+
+#ifdef CONFIG_IEEE80211W
+ if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
+ if (!(data.capabilities & WPA_CAPABILITY_MFPC)) {
+ wpa_printf(MSG_DEBUG, "Management frame protection "
+ "required, but client did not enable it");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+
+ if (ciphers & WPA_CIPHER_TKIP) {
+ wpa_printf(MSG_DEBUG, "Management frame protection "
+ "cannot use TKIP");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+
+ if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) {
+ wpa_printf(MSG_DEBUG, "Unsupported management group "
+ "cipher %d", data.mgmt_group_cipher);
+ return WPA_INVALID_MGMT_GROUP_CIPHER;
+ }
+ }
+
+ if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
+ !(data.capabilities & WPA_CAPABILITY_MFPC))
+ sm->mgmt_frame_prot = 0;
+ else
+ sm->mgmt_frame_prot = 1;
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
+ wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but "
+ "MDIE not included");
+ return WPA_INVALID_MDIE;
+ }
+ if (os_memcmp(mdie, wpa_auth->conf.mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG, "RSN: Attempted to use unknown "
+ "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN);
+ return WPA_INVALID_MDIE;
+ }
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ if (ciphers & WPA_CIPHER_CCMP)
+ sm->pairwise = WPA_CIPHER_CCMP;
+ else
+ sm->pairwise = WPA_CIPHER_TKIP;
+
+ /* TODO: clear WPA/WPA2 state if STA changes from one to another */
+ if (wpa_ie[0] == WLAN_EID_RSN)
+ sm->wpa = WPA_VERSION_WPA2;
+ else
+ sm->wpa = WPA_VERSION_WPA;
+
+ sm->pmksa = NULL;
+ for (i = 0; i < data.num_pmkid; i++) {
+ wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
+ &data.pmkid[i * PMKID_LEN], PMKID_LEN);
+ sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr,
+ &data.pmkid[i * PMKID_LEN]);
+ if (sm->pmksa) {
+ pmkid = sm->pmksa->pmkid;
+ break;
+ }
+ }
+ for (i = 0; sm->pmksa == NULL && wpa_auth->conf.okc &&
+ i < data.num_pmkid; i++) {
+ struct wpa_auth_okc_iter_data idata;
+ idata.pmksa = NULL;
+ idata.aa = wpa_auth->addr;
+ idata.spa = sm->addr;
+ idata.pmkid = &data.pmkid[i * PMKID_LEN];
+ wpa_auth_for_each_auth(wpa_auth, wpa_auth_okc_iter, &idata);
+ if (idata.pmksa) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "OKC match for PMKID");
+ sm->pmksa = pmksa_cache_add_okc(wpa_auth->pmksa,
+ idata.pmksa,
+ wpa_auth->addr,
+ idata.pmkid);
+ pmkid = idata.pmkid;
+ break;
+ }
+ }
+ if (sm->pmksa) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PMKID found from PMKSA cache "
+ "eap_type=%d vlan_id=%d",
+ sm->pmksa->eap_type_authsrv,
+ sm->pmksa->vlan_id);
+ os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
+ }
+
+ if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
+ os_free(sm->wpa_ie);
+ sm->wpa_ie = os_malloc(wpa_ie_len);
+ if (sm->wpa_ie == NULL)
+ return WPA_ALLOC_FAIL;
+ }
+ os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len);
+ sm->wpa_ie_len = wpa_ie_len;
+
+ return WPA_IE_OK;
+}
+
+
+/**
+ * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
+ * @pos: Pointer to the IE header
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_parse_generic(const u8 *pos, const u8 *end,
+ struct wpa_eapol_ie_parse *ie)
+{
+ if (pos[1] == 0)
+ return 1;
+
+ if (pos[1] >= 6 &&
+ RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
+ pos[2 + WPA_SELECTOR_LEN] == 1 &&
+ pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
+ ie->wpa_ie = pos;
+ ie->wpa_ie_len = pos[1] + 2;
+ return 0;
+ }
+
+ if (pos + 1 + RSN_SELECTOR_LEN < end &&
+ pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
+ ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
+ ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
+ ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
+ ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+
+#ifdef CONFIG_PEERKEY
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
+ ie->smk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
+ ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
+ ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
+ ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
+ ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
+ ie->error = pos + 2 + RSN_SELECTOR_LEN;
+ ie->error_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211W
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
+ ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ return 0;
+}
+
+
+/**
+ * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs
+ * @buf: Pointer to the Key Data buffer
+ * @len: Key Data Length
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
+{
+ const u8 *pos, *end;
+ int ret = 0;
+
+ os_memset(ie, 0, sizeof(*ie));
+ for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+ if (pos[0] == 0xdd &&
+ ((pos == buf + len - 1) || pos[1] == 0)) {
+ /* Ignore padding */
+ break;
+ }
+ if (pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
+ "underflow (ie=%d len=%d pos=%d)",
+ pos[0], pos[1], (int) (pos - buf));
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
+ buf, len);
+ ret = -1;
+ break;
+ }
+ if (*pos == WLAN_EID_RSN) {
+ ie->rsn_ie = pos;
+ ie->rsn_ie_len = pos[1] + 2;
+#ifdef CONFIG_IEEE80211R
+ } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
+ ie->mdie = pos;
+ ie->mdie_len = pos[1] + 2;
+ } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
+ ie->ftie = pos;
+ ie->ftie_len = pos[1] + 2;
+#endif /* CONFIG_IEEE80211R */
+ } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
+ ret = wpa_parse_generic(pos, end, ie);
+ if (ret < 0)
+ break;
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ } else {
+ wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
+ "Key Data IE", pos, 2 + pos[1]);
+ }
+ }
+
+ return ret;
+}
+
+
+int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
+{
+ return sm ? sm->mgmt_frame_prot : 0;
+}
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
new file mode 100644
index 0000000..61d4cb4
--- /dev/null
+++ b/src/ap/wpa_auth_ie.h
@@ -0,0 +1,56 @@
+/*
+ * hostapd - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_AUTH_IE_H
+#define WPA_AUTH_IE_H
+
+struct wpa_eapol_ie_parse {
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+ const u8 *rsn_ie;
+ size_t rsn_ie_len;
+ const u8 *pmkid;
+ const u8 *gtk;
+ size_t gtk_len;
+ const u8 *mac_addr;
+ size_t mac_addr_len;
+#ifdef CONFIG_PEERKEY
+ const u8 *smk;
+ size_t smk_len;
+ const u8 *nonce;
+ size_t nonce_len;
+ const u8 *lifetime;
+ size_t lifetime_len;
+ const u8 *error;
+ size_t error_len;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211W
+ const u8 *igtk;
+ size_t igtk_len;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+ const u8 *mdie;
+ size_t mdie_len;
+ const u8 *ftie;
+ size_t ftie_len;
+#endif /* CONFIG_IEEE80211R */
+};
+
+int wpa_parse_kde_ies(const u8 *buf, size_t len,
+ struct wpa_eapol_ie_parse *ie);
+u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
+ const u8 *data2, size_t data2_len);
+int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth);
+
+#endif /* WPA_AUTH_IE_H */
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
new file mode 100644
index 0000000..fcbd89b
--- /dev/null
+++ b/src/ap/wps_hostapd.c
@@ -0,0 +1,1380 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "crypto/dh_groups.h"
+#include "common/wpa_ctrl.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+#include "wps/wps_dev_attr.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "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 *ie, size_t ie_len);
+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;
+};
+
+
+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 = 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;
+ if (iface->for_each_interface == NULL)
+ return wps_for_each(iface, &data);
+ return iface->for_each_interface(iface->interfaces, wps_for_each,
+ &data);
+}
+
+
+static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
+ size_t psk_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct hostapd_wpa_psk *p;
+ struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+ wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA "
+ MACSTR, MAC2STR(mac_addr));
+ wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
+
+ if (psk_len != PMK_LEN) {
+ wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu",
+ (unsigned long) psk_len);
+ return -1;
+ }
+
+ /* Add the new PSK to runtime PSK list */
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ return -1;
+ os_memcpy(p->addr, mac_addr, ETH_ALEN);
+ os_memcpy(p->psk, psk, PMK_LEN);
+
+ p->next = ssid->wpa_psk;
+ ssid->wpa_psk = p;
+
+ if (ssid->wpa_psk_file) {
+ FILE *f;
+ char hex[PMK_LEN * 2 + 1];
+ /* Add the new PSK to PSK list file */
+ f = fopen(ssid->wpa_psk_file, "a");
+ if (f == NULL) {
+ wpa_printf(MSG_DEBUG, "Failed to add the PSK to "
+ "'%s'", ssid->wpa_psk_file);
+ return -1;
+ }
+
+ wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len);
+ fprintf(f, MACSTR " %s\n", MAC2STR(mac_addr), hex);
+ fclose(f);
+ }
+
+ return 0;
+}
+
+
+static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
+ struct wpabuf *probe_resp_ie)
+{
+ struct hostapd_data *hapd = ctx;
+ wpabuf_free(hapd->wps_beacon_ie);
+ hapd->wps_beacon_ie = beacon_ie;
+ wpabuf_free(hapd->wps_probe_resp_ie);
+ hapd->wps_probe_resp_ie = probe_resp_ie;
+ ieee802_11_set_beacon(hapd);
+ return 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);
+ }
+}
+
+
+static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
+ const u8 *uuid_e)
+{
+ struct hostapd_data *hapd = ctx;
+ char uuid[40];
+ if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+ return;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
+ MAC2STR(mac_addr), uuid);
+ if (hapd->wps_reg_success_cb)
+ hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
+ mac_addr, uuid_e);
+}
+
+
+static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr,
+ const u8 *uuid_e,
+ const u8 *pri_dev_type,
+ u16 config_methods,
+ u16 dev_password_id, u8 request_type,
+ const char *dev_name)
+{
+ struct hostapd_data *hapd = ctx;
+ char uuid[40];
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+ return;
+ if (dev_name == NULL)
+ dev_name = "";
+ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR
+ " %s %s 0x%x %u %u [%s]",
+ MAC2STR(addr), uuid,
+ wps_dev_type_bin2str(pri_dev_type, devtype,
+ sizeof(devtype)),
+ config_methods, dev_password_id, request_type, dev_name);
+}
+
+
+static int str_starts(const char *str, const char *start)
+{
+ return os_strncmp(str, start, os_strlen(start)) == 0;
+}
+
+
+static void wps_reload_config(void *eloop_data, void *user_ctx)
+{
+ struct hostapd_iface *iface = eloop_data;
+
+ wpa_printf(MSG_DEBUG, "WPS: Reload configuration data");
+ if (iface->reload_config(iface) < 0) {
+ wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated "
+ "configuration");
+ }
+}
+
+
+static int 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) {
+ size_t blen = cred->cred_attr_len * 2 + 1;
+ char *_buf = os_malloc(blen);
+ if (_buf) {
+ wpa_snprintf_hex(_buf, blen,
+ cred->cred_attr, cred->cred_attr_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "%s%s",
+ WPS_EVENT_NEW_AP_SETTINGS, _buf);
+ os_free(_buf);
+ }
+ } else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS);
+
+ if (hapd->conf->wps_cred_processing == 1)
+ return 0;
+
+ os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len);
+ hapd->wps->ssid_len = cred->ssid_len;
+ hapd->wps->encr_types = cred->encr_type;
+ hapd->wps->auth_types = cred->auth_type;
+ if (cred->key_len == 0) {
+ os_free(hapd->wps->network_key);
+ hapd->wps->network_key = NULL;
+ hapd->wps->network_key_len = 0;
+ } else {
+ if (hapd->wps->network_key == NULL ||
+ hapd->wps->network_key_len < cred->key_len) {
+ hapd->wps->network_key_len = 0;
+ os_free(hapd->wps->network_key);
+ hapd->wps->network_key = os_malloc(cred->key_len);
+ if (hapd->wps->network_key == NULL)
+ return -1;
+ }
+ hapd->wps->network_key_len = cred->key_len;
+ os_memcpy(hapd->wps->network_key, cred->key, cred->key_len);
+ }
+ hapd->wps->wps_state = WPS_STATE_CONFIGURED;
+
+ len = os_strlen(hapd->iface->config_fname) + 5;
+ tmp_fname = os_malloc(len);
+ if (tmp_fname == NULL)
+ return -1;
+ os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname);
+
+ oconf = fopen(hapd->iface->config_fname, "r");
+ if (oconf == NULL) {
+ wpa_printf(MSG_WARNING, "WPS: Could not open current "
+ "configuration file");
+ os_free(tmp_fname);
+ return -1;
+ }
+
+ nconf = fopen(tmp_fname, "w");
+ if (nconf == NULL) {
+ wpa_printf(MSG_WARNING, "WPS: Could not write updated "
+ "configuration file");
+ os_free(tmp_fname);
+ fclose(oconf);
+ return -1;
+ }
+
+ fprintf(nconf, "# WPS configuration - START\n");
+
+ fprintf(nconf, "wps_state=2\n");
+
+ fprintf(nconf, "ssid=");
+ for (i = 0; i < cred->ssid_len; i++)
+ fputc(cred->ssid[i], nconf);
+ fprintf(nconf, "\n");
+
+ if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
+ (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
+ wpa = 3;
+ else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
+ wpa = 2;
+ else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+ wpa = 1;
+ else
+ wpa = 0;
+
+ if (wpa) {
+ char *prefix;
+ fprintf(nconf, "wpa=%d\n", wpa);
+
+ fprintf(nconf, "wpa_key_mgmt=");
+ prefix = "";
+ if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) {
+ fprintf(nconf, "WPA-EAP");
+ prefix = " ";
+ }
+ if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
+ fprintf(nconf, "%sWPA-PSK", prefix);
+ fprintf(nconf, "\n");
+
+ fprintf(nconf, "wpa_pairwise=");
+ prefix = "";
+ if (cred->encr_type & WPS_ENCR_AES) {
+ fprintf(nconf, "CCMP");
+ prefix = " ";
+ }
+ if (cred->encr_type & WPS_ENCR_TKIP) {
+ fprintf(nconf, "%sTKIP", prefix);
+ }
+ fprintf(nconf, "\n");
+
+ if (cred->key_len >= 8 && cred->key_len < 64) {
+ fprintf(nconf, "wpa_passphrase=");
+ for (i = 0; i < cred->key_len; i++)
+ fputc(cred->key[i], nconf);
+ fprintf(nconf, "\n");
+ } else if (cred->key_len == 64) {
+ fprintf(nconf, "wpa_psk=");
+ for (i = 0; i < cred->key_len; i++)
+ fputc(cred->key[i], nconf);
+ fprintf(nconf, "\n");
+ } else {
+ wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu "
+ "for WPA/WPA2",
+ (unsigned long) cred->key_len);
+ }
+
+ fprintf(nconf, "auth_algs=1\n");
+ } else {
+ if ((cred->auth_type & WPS_AUTH_OPEN) &&
+ (cred->auth_type & WPS_AUTH_SHARED))
+ fprintf(nconf, "auth_algs=3\n");
+ else if (cred->auth_type & WPS_AUTH_SHARED)
+ fprintf(nconf, "auth_algs=2\n");
+ else
+ fprintf(nconf, "auth_algs=1\n");
+
+ if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx <= 4) {
+ int key_idx = cred->key_idx;
+ if (key_idx)
+ key_idx--;
+ fprintf(nconf, "wep_default_key=%d\n", key_idx);
+ fprintf(nconf, "wep_key%d=", key_idx);
+ if (cred->key_len == 10 || cred->key_len == 26) {
+ /* WEP key as a hex string */
+ for (i = 0; i < cred->key_len; i++)
+ fputc(cred->key[i], nconf);
+ } else {
+ /* Raw WEP key; convert to hex */
+ for (i = 0; i < cred->key_len; i++)
+ fprintf(nconf, "%02x", cred->key[i]);
+ }
+ fprintf(nconf, "\n");
+ }
+ }
+
+ fprintf(nconf, "# WPS configuration - END\n");
+
+ multi_bss = 0;
+ while (fgets(buf, sizeof(buf), oconf)) {
+ if (os_strncmp(buf, "bss=", 4) == 0)
+ multi_bss = 1;
+ if (!multi_bss &&
+ (str_starts(buf, "ssid=") ||
+ str_starts(buf, "auth_algs=") ||
+ str_starts(buf, "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;
+
+ 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++;
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
+ hapd->ap_pin_failures);
+ 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) {
+ 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)
+{
+ hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data);
+}
+
+
+static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
+ "No Error", /* WPS_EI_NO_ERROR */
+ "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
+ "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
+};
+
+static void hostapd_wps_event_fail(struct hostapd_data *hapd,
+ struct wps_event_fail *fail)
+{
+ 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_event_fail_reason[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:
+ break;
+ case WPS_EV_FAIL:
+ hostapd_wps_event_fail(hapd, &data->fail);
+ break;
+ case WPS_EV_SUCCESS:
+ break;
+ case WPS_EV_PWD_AUTH_FAIL:
+ hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
+ break;
+ case WPS_EV_PBC_OVERLAP:
+ break;
+ case WPS_EV_PBC_TIMEOUT:
+ 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;
+ }
+ if (hapd->wps_event_cb)
+ hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data);
+}
+
+
+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 && !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->for_each_interface == NULL)
+ return NULL;
+ uuid = NULL;
+ iface->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->for_each_interface == NULL)
+ return 0;
+ iface->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->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) {
+ 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);
+ 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 (cfg.dualband)
+ wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
+
+ 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;
+
+ if (hostapd_wps_upnp_init(hapd, wps) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
+ wps_registrar_deinit(wps->registrar);
+ os_free(wps->network_key);
+ os_free(wps);
+ return -1;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
+
+ hapd->wps = wps;
+
+ return 0;
+}
+
+
+void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+ eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+ eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+ if (hapd->wps == NULL)
+ return;
+#ifdef CONFIG_WPS_UPNP
+ hostapd_wps_upnp_deinit(hapd);
+#endif /* CONFIG_WPS_UPNP */
+ wps_registrar_deinit(hapd->wps->registrar);
+ os_free(hapd->wps->network_key);
+ wps_device_data_free(&hapd->wps->dev);
+ wpabuf_free(hapd->wps->dh_pubkey);
+ wpabuf_free(hapd->wps->dh_privkey);
+ wpabuf_free(hapd->wps->oob_conf.pubkey_hash);
+ wpabuf_free(hapd->wps->oob_conf.dev_password);
+ wps_free_pending_msgs(hapd->wps->upnp_msgs);
+ os_free(hapd->wps);
+ hapd->wps = NULL;
+ hostapd_wps_clear_ies(hapd);
+}
+
+
+void hostapd_update_wps(struct hostapd_data *hapd)
+{
+ if (hapd->wps == NULL)
+ return;
+
+#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);
+}
+
+
+#ifdef CONFIG_WPS_OOB
+int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
+ char *path, char *method, char *name)
+{
+ struct wps_context *wps = hapd->wps;
+ struct oob_device_data *oob_dev;
+
+ oob_dev = wps_get_oob_device(device_type);
+ if (oob_dev == NULL)
+ return -1;
+ oob_dev->device_path = path;
+ oob_dev->device_name = name;
+ wps->oob_conf.oob_method = wps_get_oob_method(method);
+
+ if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) {
+ /*
+ * Use pre-configured DH keys in order to be able to write the
+ * key hash into the OOB file.
+ */
+ wpabuf_free(wps->dh_pubkey);
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = NULL;
+ wps->dh_pubkey = dh_init(dh_groups_get(WPS_DH_GROUP),
+ &wps->dh_privkey);
+ wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192);
+ if (wps->dh_pubkey == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to initialize "
+ "Diffie-Hellman handshake");
+ return -1;
+ }
+ }
+
+ if (wps_process_oob(wps, oob_dev, 1) < 0)
+ goto error;
+
+ if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
+ wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
+ hostapd_wps_add_pin(hapd, NULL, "any",
+ wpabuf_head(wps->oob_conf.dev_password), 0) <
+ 0)
+ goto error;
+
+ return 0;
+
+error:
+ wpabuf_free(wps->dh_pubkey);
+ wps->dh_pubkey = NULL;
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = NULL;
+ return -1;
+}
+#endif /* CONFIG_WPS_OOB */
+
+
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
+ const u8 *ie, size_t ie_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct wpabuf *wps_ie;
+ struct ieee802_11_elems elems;
+
+ if (hapd->wps == NULL)
+ return 0;
+
+ if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from "
+ MACSTR, MAC2STR(addr));
+ return 0;
+ }
+
+ if (elems.ssid && elems.ssid_len > 0 &&
+ (elems.ssid_len != hapd->conf->ssid.ssid_len ||
+ os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) !=
+ 0))
+ return 0; /* Not for us */
+
+ wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+ if (wps_ie == NULL)
+ return 0;
+ if (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) {
+ 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);
+}
+
+
+static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
+ hapd->ap_pin_failures = 0;
+ hapd->conf->ap_setup_locked = 0;
+ if (hapd->wps->ap_setup_locked) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+ hapd->wps->ap_setup_locked = 0;
+ wps_registrar_update_ie(hapd->wps->registrar);
+ }
+ eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0,
+ hostapd_wps_ap_pin_timeout, hapd, NULL);
+}
+
+
+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), "%u", 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);
+}
diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h
new file mode 100644
index 0000000..338a220
--- /dev/null
+++ b/src/ap/wps_hostapd.h
@@ -0,0 +1,72 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPS_HOSTAPD_H
+#define WPS_HOSTAPD_H
+
+#ifdef CONFIG_WPS
+
+int hostapd_init_wps(struct hostapd_data *hapd,
+ struct hostapd_bss_config *conf);
+void hostapd_deinit_wps(struct hostapd_data *hapd);
+void hostapd_update_wps(struct hostapd_data *hapd);
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const 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_start_oob(struct hostapd_data *hapd, char *device_type,
+ char *path, char *method, char *name);
+int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
+ char *buf, size_t buflen);
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd);
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout);
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd);
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+ int timeout);
+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);
+
+#else /* CONFIG_WPS */
+
+static inline int hostapd_init_wps(struct hostapd_data *hapd,
+ struct hostapd_bss_config *conf)
+{
+ return 0;
+}
+
+static inline void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_update_wps(struct hostapd_data *hapd)
+{
+}
+
+static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd,
+ const u8 *addr,
+ char *buf, size_t buflen)
+{
+ return 0;
+}
+
+static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+ const u8 *p2p_dev_addr)
+{
+ return 0;
+}
+
+#endif /* CONFIG_WPS */
+
+#endif /* WPS_HOSTAPD_H */
diff --git a/src/common/Makefile b/src/common/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/common/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/common/defs.h b/src/common/defs.h
new file mode 100644
index 0000000..8abec07
--- /dev/null
+++ b/src/common/defs.h
@@ -0,0 +1,270 @@
+/*
+ * WPA Supplicant - Common definitions
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef DEFS_H
+#define DEFS_H
+
+#ifdef FALSE
+#undef FALSE
+#endif
+#ifdef TRUE
+#undef TRUE
+#endif
+typedef enum { FALSE = 0, TRUE = 1 } Boolean;
+
+
+#define WPA_CIPHER_NONE BIT(0)
+#define WPA_CIPHER_WEP40 BIT(1)
+#define WPA_CIPHER_WEP104 BIT(2)
+#define WPA_CIPHER_TKIP BIT(3)
+#define WPA_CIPHER_CCMP BIT(4)
+#ifdef CONFIG_IEEE80211W
+#define WPA_CIPHER_AES_128_CMAC BIT(5)
+#endif /* CONFIG_IEEE80211W */
+
+#define WPA_KEY_MGMT_IEEE8021X BIT(0)
+#define WPA_KEY_MGMT_PSK BIT(1)
+#define WPA_KEY_MGMT_NONE BIT(2)
+#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3)
+#define WPA_KEY_MGMT_WPA_NONE BIT(4)
+#define WPA_KEY_MGMT_FT_IEEE8021X BIT(5)
+#define WPA_KEY_MGMT_FT_PSK BIT(6)
+#define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7)
+#define WPA_KEY_MGMT_PSK_SHA256 BIT(8)
+#define WPA_KEY_MGMT_WPS BIT(9)
+
+static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
+ WPA_KEY_MGMT_FT_IEEE8021X |
+ 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));
+}
+
+static inline int wpa_key_mgmt_ft(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_FT_PSK |
+ WPA_KEY_MGMT_FT_IEEE8021X));
+}
+
+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);
+}
+
+
+#define WPA_PROTO_WPA BIT(0)
+#define WPA_PROTO_RSN BIT(1)
+
+#define WPA_AUTH_ALG_OPEN BIT(0)
+#define WPA_AUTH_ALG_SHARED BIT(1)
+#define WPA_AUTH_ALG_LEAP BIT(2)
+#define WPA_AUTH_ALG_FT BIT(3)
+
+
+enum wpa_alg {
+ WPA_ALG_NONE,
+ WPA_ALG_WEP,
+ WPA_ALG_TKIP,
+ WPA_ALG_CCMP,
+ WPA_ALG_IGTK,
+ WPA_ALG_PMK
+};
+
+/**
+ * enum wpa_cipher - Cipher suites
+ */
+enum wpa_cipher {
+ CIPHER_NONE,
+ CIPHER_WEP40,
+ CIPHER_TKIP,
+ CIPHER_CCMP,
+ CIPHER_WEP104
+};
+
+/**
+ * enum wpa_key_mgmt - Key management suites
+ */
+enum wpa_key_mgmt {
+ KEY_MGMT_802_1X,
+ KEY_MGMT_PSK,
+ KEY_MGMT_NONE,
+ KEY_MGMT_802_1X_NO_WPA,
+ KEY_MGMT_WPA_NONE,
+ KEY_MGMT_FT_802_1X,
+ KEY_MGMT_FT_PSK,
+ KEY_MGMT_802_1X_SHA256,
+ KEY_MGMT_PSK_SHA256,
+ KEY_MGMT_WPS
+};
+
+/**
+ * enum wpa_states - wpa_supplicant state
+ *
+ * These enumeration values are used to indicate the current wpa_supplicant
+ * state (wpa_s->wpa_state). The current state can be retrieved with
+ * wpa_supplicant_get_state() function and the state can be changed by calling
+ * wpa_supplicant_set_state(). In WPA state machine (wpa.c and preauth.c), the
+ * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used
+ * to access the state variable.
+ */
+enum wpa_states {
+ /**
+ * WPA_DISCONNECTED - Disconnected state
+ *
+ * This state indicates that client is not associated, but is likely to
+ * start looking for an access point. This state is entered when a
+ * connection is lost.
+ */
+ WPA_DISCONNECTED,
+
+ /**
+ * WPA_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
+};
+
+/**
+ * enum hostapd_hw_mode - Hardware mode
+ */
+enum hostapd_hw_mode {
+ HOSTAPD_MODE_IEEE80211B,
+ HOSTAPD_MODE_IEEE80211G,
+ HOSTAPD_MODE_IEEE80211A,
+ NUM_HOSTAPD_MODES
+};
+
+#endif /* DEFS_H */
diff --git a/src/common/eapol_common.h b/src/common/eapol_common.h
new file mode 100644
index 0000000..d70e62d
--- /dev/null
+++ b/src/common/eapol_common.h
@@ -0,0 +1,47 @@
+/*
+ * EAPOL definitions shared between hostapd and wpa_supplicant
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAPOL_COMMON_H
+#define EAPOL_COMMON_H
+
+/* IEEE Std 802.1X-2004 */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee802_1x_hdr {
+ u8 version;
+ u8 type;
+ be16 length;
+ /* followed by length octets of data */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define EAPOL_VERSION 2
+
+enum { IEEE802_1X_TYPE_EAP_PACKET = 0,
+ IEEE802_1X_TYPE_EAPOL_START = 1,
+ IEEE802_1X_TYPE_EAPOL_LOGOFF = 2,
+ IEEE802_1X_TYPE_EAPOL_KEY = 3,
+ IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4
+};
+
+enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2,
+ EAPOL_KEY_TYPE_WPA = 254 };
+
+#endif /* EAPOL_COMMON_H */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
new file mode 100644
index 0000000..ee41b3a
--- /dev/null
+++ b/src/common/ieee802_11_common.c
@@ -0,0 +1,347 @@
+/*
+ * IEEE 802.11 Common routines
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ieee802_11_defs.h"
+#include "ieee802_11_common.h"
+
+
+static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
+ struct ieee802_11_elems *elems,
+ int show_errors)
+{
+ unsigned int oui;
+
+ /* first 3 bytes in vendor specific information element are the IEEE
+ * OUI of the vendor. The following byte is used a vendor specific
+ * sub-type. */
+ if (elen < 4) {
+ if (show_errors) {
+ wpa_printf(MSG_MSGDUMP, "short vendor specific "
+ "information element ignored (len=%lu)",
+ (unsigned long) elen);
+ }
+ return -1;
+ }
+
+ oui = WPA_GET_BE24(pos);
+ switch (oui) {
+ case OUI_MICROSOFT:
+ /* Microsoft/Wi-Fi information elements are further typed and
+ * subtyped */
+ switch (pos[3]) {
+ case 1:
+ /* Microsoft OUI (00:50:F2) with OUI Type 1:
+ * real WPA information element */
+ elems->wpa_ie = pos;
+ elems->wpa_ie_len = elen;
+ break;
+ case WMM_OUI_TYPE:
+ /* WMM information element */
+ if (elen < 5) {
+ wpa_printf(MSG_MSGDUMP, "short WMM "
+ "information element ignored "
+ "(len=%lu)",
+ (unsigned long) elen);
+ return -1;
+ }
+ switch (pos[4]) {
+ case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
+ case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
+ /*
+ * Share same pointer since only one of these
+ * is used and they start with same data.
+ * Length field can be used to distinguish the
+ * IEs.
+ */
+ elems->wmm = pos;
+ elems->wmm_len = elen;
+ break;
+ case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
+ elems->wmm_tspec = pos;
+ elems->wmm_tspec_len = elen;
+ break;
+ default:
+ wpa_printf(MSG_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;
+ 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_LINK_ID:
+ if (elen < 18)
+ break;
+ elems->link_id = pos;
+ break;
+ default:
+ unknown++;
+ if (!show_errors)
+ break;
+ wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
+ "ignored unknown element (id=%d elen=%d)",
+ id, elen);
+ break;
+ }
+
+ left -= elen;
+ pos += elen;
+ }
+
+ if (left)
+ return ParseFailed;
+
+ return unknown ? ParseUnknown : ParseOK;
+}
+
+
+int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
+{
+ int count = 0;
+ const u8 *pos, *end;
+
+ if (ies == NULL)
+ return 0;
+
+ pos = ies;
+ end = ies + ies_len;
+
+ while (pos + 2 <= end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ count++;
+ pos += 2 + pos[1];
+ }
+
+ return count;
+}
+
+
+struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
+ u32 oui_type)
+{
+ struct wpabuf *buf;
+ const u8 *end, *pos, *ie;
+
+ pos = ies;
+ end = ies + ies_len;
+ ie = NULL;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ return NULL;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ WPA_GET_BE32(&pos[2]) == oui_type) {
+ ie = pos;
+ break;
+ }
+ pos += 2 + pos[1];
+ }
+
+ if (ie == NULL)
+ return NULL; /* No specified vendor IE found */
+
+ buf = wpabuf_alloc(ies_len);
+ if (buf == NULL)
+ return NULL;
+
+ /*
+ * There may be multiple vendor IEs in the message, so need to
+ * concatenate their data fields.
+ */
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ WPA_GET_BE32(&pos[2]) == oui_type)
+ wpabuf_put_data(buf, pos + 6, pos[1] - 4);
+ pos += 2 + pos[1];
+ }
+
+ return buf;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
new file mode 100644
index 0000000..0c90fa4
--- /dev/null
+++ b/src/common/ieee802_11_common.h
@@ -0,0 +1,81 @@
+/*
+ * IEEE 802.11 Common routines
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef IEEE802_11_COMMON_H
+#define IEEE802_11_COMMON_H
+
+/* Parsed Information Elements */
+struct ieee802_11_elems {
+ const u8 *ssid;
+ const u8 *supp_rates;
+ const u8 *fh_params;
+ const u8 *ds_params;
+ const u8 *cf_params;
+ const u8 *tim;
+ const u8 *ibss_params;
+ const u8 *challenge;
+ const u8 *erp_info;
+ const u8 *ext_supp_rates;
+ const u8 *wpa_ie;
+ const u8 *rsn_ie;
+ const u8 *wmm; /* WMM Information or Parameter Element */
+ const u8 *wmm_tspec;
+ const u8 *wps_ie;
+ const u8 *power_cap;
+ const u8 *supp_channels;
+ const u8 *mdie;
+ const u8 *ftie;
+ const u8 *timeout_int;
+ const u8 *ht_capabilities;
+ const u8 *ht_operation;
+ const u8 *vendor_ht_cap;
+ const u8 *p2p;
+ const u8 *link_id;
+
+ u8 ssid_len;
+ u8 supp_rates_len;
+ u8 fh_params_len;
+ u8 ds_params_len;
+ u8 cf_params_len;
+ u8 tim_len;
+ u8 ibss_params_len;
+ u8 challenge_len;
+ u8 erp_info_len;
+ u8 ext_supp_rates_len;
+ u8 wpa_ie_len;
+ u8 rsn_ie_len;
+ u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */
+ u8 wmm_tspec_len;
+ u8 wps_ie_len;
+ u8 power_cap_len;
+ u8 supp_channels_len;
+ u8 mdie_len;
+ u8 ftie_len;
+ u8 timeout_int_len;
+ u8 ht_capabilities_len;
+ u8 ht_operation_len;
+ u8 vendor_ht_cap_len;
+ u8 p2p_len;
+};
+
+typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
+
+ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
+ struct ieee802_11_elems *elems,
+ int show_errors);
+int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
+struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
+ u32 oui_type);
+
+#endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
new file mode 100644
index 0000000..86868c0
--- /dev/null
+++ b/src/common/ieee802_11_defs.h
@@ -0,0 +1,800 @@
+/*
+ * IEEE 802.11 Frame type definitions
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2008 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef IEEE802_11_DEFS_H
+#define IEEE802_11_DEFS_H
+
+/* IEEE 802.11 defines */
+
+#define WLAN_FC_PVER 0x0003
+#define WLAN_FC_TODS 0x0100
+#define WLAN_FC_FROMDS 0x0200
+#define WLAN_FC_MOREFRAG 0x0400
+#define WLAN_FC_RETRY 0x0800
+#define WLAN_FC_PWRMGT 0x1000
+#define WLAN_FC_MOREDATA 0x2000
+#define WLAN_FC_ISWEP 0x4000
+#define WLAN_FC_ORDER 0x8000
+
+#define WLAN_FC_GET_TYPE(fc) (((fc) & 0x000c) >> 2)
+#define WLAN_FC_GET_STYPE(fc) (((fc) & 0x00f0) >> 4)
+
+#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
+#define WLAN_GET_SEQ_SEQ(seq) \
+ (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4)
+
+#define WLAN_FC_TYPE_MGMT 0
+#define WLAN_FC_TYPE_CTRL 1
+#define WLAN_FC_TYPE_DATA 2
+
+/* management */
+#define WLAN_FC_STYPE_ASSOC_REQ 0
+#define WLAN_FC_STYPE_ASSOC_RESP 1
+#define WLAN_FC_STYPE_REASSOC_REQ 2
+#define WLAN_FC_STYPE_REASSOC_RESP 3
+#define WLAN_FC_STYPE_PROBE_REQ 4
+#define WLAN_FC_STYPE_PROBE_RESP 5
+#define WLAN_FC_STYPE_BEACON 8
+#define WLAN_FC_STYPE_ATIM 9
+#define WLAN_FC_STYPE_DISASSOC 10
+#define WLAN_FC_STYPE_AUTH 11
+#define WLAN_FC_STYPE_DEAUTH 12
+#define WLAN_FC_STYPE_ACTION 13
+
+/* control */
+#define WLAN_FC_STYPE_PSPOLL 10
+#define WLAN_FC_STYPE_RTS 11
+#define WLAN_FC_STYPE_CTS 12
+#define WLAN_FC_STYPE_ACK 13
+#define WLAN_FC_STYPE_CFEND 14
+#define WLAN_FC_STYPE_CFENDACK 15
+
+/* data */
+#define WLAN_FC_STYPE_DATA 0
+#define WLAN_FC_STYPE_DATA_CFACK 1
+#define WLAN_FC_STYPE_DATA_CFPOLL 2
+#define WLAN_FC_STYPE_DATA_CFACKPOLL 3
+#define WLAN_FC_STYPE_NULLFUNC 4
+#define WLAN_FC_STYPE_CFACK 5
+#define WLAN_FC_STYPE_CFPOLL 6
+#define WLAN_FC_STYPE_CFACKPOLL 7
+#define WLAN_FC_STYPE_QOS_DATA 8
+#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_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_INVALID_RSNIE 72
+
+/* 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_CHALLENGE 16
+/* EIDs defined by IEEE 802.11h - START */
+#define WLAN_EID_PWR_CONSTRAINT 32
+#define WLAN_EID_PWR_CAPABILITY 33
+#define WLAN_EID_TPC_REQUEST 34
+#define WLAN_EID_TPC_REPORT 35
+#define WLAN_EID_SUPPORTED_CHANNELS 36
+#define WLAN_EID_CHANNEL_SWITCH 37
+#define WLAN_EID_MEASURE_REQUEST 38
+#define WLAN_EID_MEASURE_REPORT 39
+#define WLAN_EID_QUITE 40
+#define WLAN_EID_IBSS_DFS 41
+/* EIDs defined by IEEE 802.11h - END */
+#define WLAN_EID_ERP_INFO 42
+#define WLAN_EID_HT_CAP 45
+#define WLAN_EID_RSN 48
+#define WLAN_EID_EXT_SUPP_RATES 50
+#define WLAN_EID_MOBILITY_DOMAIN 54
+#define WLAN_EID_FAST_BSS_TRANSITION 55
+#define WLAN_EID_TIMEOUT_INTERVAL 56
+#define WLAN_EID_RIC_DATA 57
+#define WLAN_EID_HT_OPERATION 61
+#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
+#define WLAN_EID_20_40_BSS_COEXISTENCE 72
+#define WLAN_EID_20_40_BSS_INTOLERANT 73
+#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
+#define WLAN_EID_MMIE 76
+#define WLAN_EID_LINK_ID 101
+#define WLAN_EID_ADV_PROTO 108
+#define WLAN_EID_EXT_CAPAB 127
+#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_TDLS 12
+#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
+#define WLAN_ACTION_VENDOR_SPECIFIC 127
+
+/* Public action codes */
+#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
+
+/* 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
+
+/* Advertisement Protocol ID definitions (IEEE 802.11u) */
+enum adv_proto_id {
+ NATIVE_QUERY_PROTOCOL = 0,
+ MIH_INFO_SERVICE = 1,
+ MIH_CMD_AND_EVENT_DISCOVERY = 2,
+ EMERGENCY_ALERT_SYSTEM = 3,
+ LOCATION_TO_SERVICE = 4,
+ ADV_PROTO_VENDOR_SPECIFIC = 221
+};
+
+/* Native Query Protocol info ID definitions (IEEE 802.11u) */
+enum nqp_info_id {
+ NQP_CAPABILITY_LIST = 256,
+ NQP_VENUE_NAME = 257,
+ NQP_EMERGENCY_CALL_NUMBER = 258,
+ NQP_NETWORK_AUTH_TYPE = 259,
+ NQP_ROAMING_CONSORTIUM = 260,
+ NQP_IP_ADDR_TYPE_AVAILABILITY = 261,
+ NQP_NAI_REALM = 262,
+ NQP_3GPP_CELLULAR_NETWORK = 263,
+ NQP_AP_GEOSPATIAL_LOCATION = 264,
+ NQP_AP_CIVIC_LOCATION = 265,
+ NQP_DOMAIN_NAME = 266,
+ NQP_EMERGENCY_ALERT_URI = 267,
+ NQP_VENDOR_SPECIFIC = 56797
+};
+
+
+#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 variable[0];
+ } STRUCT_PACKED public_action;
+ struct {
+ u8 action; /* 9 */
+ u8 oui[3];
+ /* Vendor-specific content */
+ u8 variable[0];
+ } STRUCT_PACKED vs_public_action;
+ } u;
+ } STRUCT_PACKED action;
+ } u;
+} STRUCT_PACKED;
+
+
+struct ieee80211_ht_capabilities {
+ le16 ht_capabilities_info;
+ u8 a_mpdu_params;
+ u8 supported_mcs_set[16];
+ le16 ht_extended_capabilities;
+ le32 tx_bf_capability_info;
+ u8 asel_capabilities;
+} STRUCT_PACKED;
+
+
+struct ieee80211_ht_operation {
+ u8 control_chan;
+ u8 ht_param;
+ le16 operation_mode;
+ le16 stbc_param;
+ u8 basic_set[16];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define ERP_INFO_NON_ERP_PRESENT BIT(0)
+#define ERP_INFO_USE_PROTECTION BIT(1)
+#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2)
+
+
+#define HT_CAP_INFO_LDPC_CODING_CAP ((u16) BIT(0))
+#define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET ((u16) BIT(1))
+#define HT_CAP_INFO_SMPS_MASK ((u16) (BIT(2) | BIT(3)))
+#define HT_CAP_INFO_SMPS_STATIC ((u16) 0)
+#define HT_CAP_INFO_SMPS_DYNAMIC ((u16) BIT(2))
+#define HT_CAP_INFO_SMPS_DISABLED ((u16) (BIT(2) | BIT(3)))
+#define HT_CAP_INFO_GREEN_FIELD ((u16) BIT(4))
+#define HT_CAP_INFO_SHORT_GI20MHZ ((u16) BIT(5))
+#define HT_CAP_INFO_SHORT_GI40MHZ ((u16) BIT(6))
+#define HT_CAP_INFO_TX_STBC ((u16) BIT(7))
+#define HT_CAP_INFO_RX_STBC_MASK ((u16) (BIT(8) | BIT(9)))
+#define HT_CAP_INFO_RX_STBC_1 ((u16) BIT(8))
+#define HT_CAP_INFO_RX_STBC_12 ((u16) BIT(9))
+#define HT_CAP_INFO_RX_STBC_123 ((u16) (BIT(8) | BIT(9)))
+#define HT_CAP_INFO_DELAYED_BA ((u16) BIT(10))
+#define HT_CAP_INFO_MAX_AMSDU_SIZE ((u16) BIT(11))
+#define HT_CAP_INFO_DSSS_CCK40MHZ ((u16) BIT(12))
+#define HT_CAP_INFO_PSMP_SUPP ((u16) BIT(13))
+#define HT_CAP_INFO_40MHZ_INTOLERANT ((u16) BIT(14))
+#define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15))
+
+
+#define EXT_HT_CAP_INFO_PCO ((u16) BIT(0))
+#define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1
+#define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8
+#define EXT_HT_CAP_INFO_HTC_SUPPORTED ((u16) BIT(10))
+#define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11))
+
+
+#define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0))
+#define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
+#define TX_BEAMFORM_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2))
+#define TX_BEAMFORM_CAP_RX_ZLF_CAP ((u32) BIT(3))
+#define TX_BEAMFORM_CAP_TX_ZLF_CAP ((u32) BIT(4))
+#define TX_BEAMFORM_CAP_IMPLICIT_ZLF_CAP ((u32) BIT(5))
+#define TX_BEAMFORM_CAP_CALIB_OFFSET 6
+#define TX_BEAMFORM_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8))
+#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_CAP ((u32) BIT(9))
+#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_CAP ((u32) BIT(10))
+#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11
+#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13
+#define TX_BEAMFORM_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15
+#define TX_BEAMFORM_CAP_MINIMAL_GROUPING_OFFSET 17
+#define TX_BEAMFORM_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19
+#define TX_BEAMFORM_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21
+#define TX_BEAMFORM_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23
+#define TX_BEAMFORM_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25
+
+
+#define ASEL_CAPABILITY_ASEL_CAPABLE ((u8) BIT(0))
+#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1))
+#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2))
+#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3))
+#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4))
+#define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5))
+#define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6))
+
+#define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK ((u8) BIT(0) | BIT(1))
+#define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE ((u8) BIT(0))
+#define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW ((u8) BIT(0) | BIT(1))
+#define HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH ((u8) BIT(2))
+#define HT_INFO_HT_PARAM_RIFS_MODE ((u8) BIT(3))
+#define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY ((u8) BIT(4))
+#define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY ((u8) BIT(5))
+
+
+#define OP_MODE_PURE 0
+#define OP_MODE_MAY_BE_LEGACY_STAS 1
+#define OP_MODE_20MHZ_HT_STA_ASSOCED 2
+#define OP_MODE_MIXED 3
+
+#define HT_INFO_OPERATION_MODE_OP_MODE_MASK \
+ ((le16) (0x0001 | 0x0002))
+#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0
+#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT ((u8) BIT(2))
+#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT ((u8) BIT(3))
+#define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT ((u8) BIT(4))
+
+#define HT_INFO_STBC_PARAM_DUAL_BEACON ((u16) BIT(6))
+#define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT ((u16) BIT(7))
+#define HT_INFO_STBC_PARAM_SECONDARY_BCN ((u16) BIT(8))
+#define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED ((u16) BIT(9))
+#define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10))
+#define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11))
+
+#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
+
+#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 WMM_OUI_TYPE 2
+#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
+#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1
+#define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2
+#define WMM_VERSION 1
+
+#define WMM_ACTION_CODE_ADDTS_REQ 0
+#define WMM_ACTION_CODE_ADDTS_RESP 1
+#define WMM_ACTION_CODE_DELTS 2
+
+#define WMM_ADDTS_STATUS_ADMISSION_ACCEPTED 0
+#define WMM_ADDTS_STATUS_INVALID_PARAMETERS 1
+/* 2 - Reserved */
+#define WMM_ADDTS_STATUS_REFUSED 3
+/* 4-255 - Reserved */
+
+/* WMM TSPEC Direction Field Values */
+#define WMM_TSPEC_DIRECTION_UPLINK 0
+#define WMM_TSPEC_DIRECTION_DOWNLINK 1
+/* 2 - Reserved */
+#define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3
+
+/*
+ * WMM Information Element (used in (Re)Association Request frames; may also be
+ * used in Beacon frames)
+ */
+struct wmm_information_element {
+ /* Element ID: 221 (0xdd); Length: 7 */
+ /* required fields for WMM version 1 */
+ u8 oui[3]; /* 00:50:f2 */
+ u8 oui_type; /* 2 */
+ u8 oui_subtype; /* 0 */
+ u8 version; /* 1 for WMM version 1.0 */
+ u8 qos_info; /* AP/STA specific QoS info */
+
+} STRUCT_PACKED;
+
+#define WMM_AC_AIFSN_MASK 0x0f
+#define WMM_AC_AIFNS_SHIFT 0
+#define WMM_AC_ACM 0x10
+#define WMM_AC_ACI_MASK 0x60
+#define WMM_AC_ACI_SHIFT 5
+
+#define WMM_AC_ECWMIN_MASK 0x0f
+#define WMM_AC_ECWMIN_SHIFT 0
+#define WMM_AC_ECWMAX_MASK 0xf0
+#define WMM_AC_ECWMAX_SHIFT 4
+
+struct wmm_ac_parameter {
+ u8 aci_aifsn; /* AIFSN, ACM, ACI */
+ u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+ le16 txop_limit;
+} STRUCT_PACKED;
+
+/*
+ * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association
+ * Response frmaes)
+ */
+struct wmm_parameter_element {
+ /* Element ID: 221 (0xdd); Length: 24 */
+ /* required fields for WMM version 1 */
+ u8 oui[3]; /* 00:50:f2 */
+ u8 oui_type; /* 2 */
+ u8 oui_subtype; /* 1 */
+ u8 version; /* 1 for WMM version 1.0 */
+ u8 qos_info; /* AP/STA specif QoS info */
+ u8 reserved; /* 0 */
+ struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */
+
+} STRUCT_PACKED;
+
+/* WMM TSPEC Element */
+struct wmm_tspec_element {
+ u8 eid; /* 221 = 0xdd */
+ u8 length; /* 6 + 55 = 61 */
+ u8 oui[3]; /* 00:50:f2 */
+ u8 oui_type; /* 2 */
+ u8 oui_subtype; /* 2 */
+ u8 version; /* 1 */
+ /* WMM TSPEC body (55 octets): */
+ u8 ts_info[3];
+ le16 nominal_msdu_size;
+ le16 maximum_msdu_size;
+ le32 minimum_service_interval;
+ le32 maximum_service_interval;
+ le32 inactivity_interval;
+ le32 suspension_interval;
+ le32 service_start_time;
+ le32 minimum_data_rate;
+ le32 mean_data_rate;
+ le32 peak_data_rate;
+ le32 maximum_burst_size;
+ le32 delay_bound;
+ le32 minimum_phy_rate;
+ le16 surplus_bandwidth_allowance;
+ le16 medium_time;
+} STRUCT_PACKED;
+
+
+/* Access Categories / ACI to AC coding */
+enum {
+ WMM_AC_BE = 0 /* Best Effort */,
+ WMM_AC_BK = 1 /* Background */,
+ WMM_AC_VI = 2 /* Video */,
+ WMM_AC_VO = 3 /* Voice */
+};
+
+
+/* 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_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
+};
+
+
+#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
+
+#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
+
+/* cipher suite selectors */
+#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00
+#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01
+#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02
+/* reserved: 0x000FAC03 */
+#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04
+#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05
+#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06
+
+/* AKM suite selectors */
+#define WLAN_AKM_SUITE_8021X 0x000FAC01
+#define WLAN_AKM_SUITE_PSK 0x000FAC02
+
+#endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h
new file mode 100644
index 0000000..cc900be
--- /dev/null
+++ b/src/common/privsep_commands.h
@@ -0,0 +1,75 @@
+/*
+ * WPA Supplicant - privilege separation commands
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PRIVSEP_COMMANDS_H
+#define PRIVSEP_COMMANDS_H
+
+enum privsep_cmd {
+ PRIVSEP_CMD_REGISTER,
+ PRIVSEP_CMD_UNREGISTER,
+ PRIVSEP_CMD_SCAN,
+ PRIVSEP_CMD_GET_SCAN_RESULTS,
+ PRIVSEP_CMD_ASSOCIATE,
+ PRIVSEP_CMD_GET_BSSID,
+ PRIVSEP_CMD_GET_SSID,
+ PRIVSEP_CMD_SET_KEY,
+ PRIVSEP_CMD_GET_CAPA,
+ PRIVSEP_CMD_L2_REGISTER,
+ PRIVSEP_CMD_L2_UNREGISTER,
+ PRIVSEP_CMD_L2_NOTIFY_AUTH_START,
+ PRIVSEP_CMD_L2_SEND,
+ PRIVSEP_CMD_SET_COUNTRY,
+};
+
+struct privsep_cmd_associate
+{
+ u8 bssid[ETH_ALEN];
+ u8 ssid[32];
+ size_t ssid_len;
+ int freq;
+ int pairwise_suite;
+ int group_suite;
+ int key_mgmt_suite;
+ int auth_alg;
+ int mode;
+ size_t wpa_ie_len;
+ /* followed by wpa_ie_len bytes of wpa_ie */
+};
+
+struct privsep_cmd_set_key
+{
+ int alg;
+ u8 addr[ETH_ALEN];
+ int key_idx;
+ int set_tx;
+ u8 seq[8];
+ size_t seq_len;
+ u8 key[32];
+ size_t key_len;
+};
+
+enum privsep_event {
+ PRIVSEP_EVENT_SCAN_RESULTS,
+ PRIVSEP_EVENT_ASSOC,
+ PRIVSEP_EVENT_DISASSOC,
+ PRIVSEP_EVENT_ASSOCINFO,
+ PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
+ PRIVSEP_EVENT_INTERFACE_STATUS,
+ PRIVSEP_EVENT_PMKID_CANDIDATE,
+ PRIVSEP_EVENT_STKSTART,
+ PRIVSEP_EVENT_FT_RESPONSE,
+ PRIVSEP_EVENT_RX_EAPOL,
+};
+
+#endif /* PRIVSEP_COMMANDS_H */
diff --git a/src/common/version.h b/src/common/version.h
new file mode 100644
index 0000000..ba2d2c0
--- /dev/null
+++ b/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 "0.8.x" VERSION_STR_POSTFIX
+
+#endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
new file mode 100644
index 0000000..eb2745e
--- /dev/null
+++ b/src/common/wpa_common.c
@@ -0,0 +1,927 @@
+/*
+ * WPA/RSN - Shared functions for supplicant and authenticator
+ * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "ieee802_11_defs.h"
+#include "defs.h"
+#include "wpa_common.h"
+
+
+/**
+ * wpa_eapol_key_mic - Calculate EAPOL-Key MIC
+ * @key: EAPOL-Key Key Confirmation Key (KCK)
+ * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*)
+ * @buf: Pointer to the beginning of the EAPOL header (version field)
+ * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame)
+ * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written
+ * Returns: 0 on success, -1 on failure
+ *
+ * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has
+ * to be cleared (all zeroes) when calling this function.
+ *
+ * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the
+ * description of the Key MIC calculation. It includes packet data from the
+ * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change
+ * happened during final editing of the standard and the correct behavior is
+ * defined in the last draft (IEEE 802.11i/D10).
+ */
+int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
+ u8 *mic)
+{
+ u8 hash[SHA1_MAC_LEN];
+
+ switch (ver) {
+ case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
+ return hmac_md5(key, 16, buf, len, mic);
+ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
+ if (hmac_sha1(key, 16, buf, len, hash))
+ return -1;
+ os_memcpy(mic, hash, MD5_MAC_LEN);
+ break;
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+ case WPA_KEY_INFO_TYPE_AES_128_CMAC:
+ return omac1_aes_128(key, buf, len, mic);
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces
+ * @pmk: Pairwise master key
+ * @pmk_len: Length of PMK
+ * @label: Label to use in derivation
+ * @addr1: AA or SA
+ * @addr2: SA or AA
+ * @nonce1: ANonce or SNonce
+ * @nonce2: SNonce or ANonce
+ * @ptk: Buffer for pairwise transient key
+ * @ptk_len: Length of PTK
+ * @use_sha256: Whether to use SHA256-based KDF
+ *
+ * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+ * PTK = PRF-X(PMK, "Pairwise key expansion",
+ * Min(AA, SA) || Max(AA, SA) ||
+ * Min(ANonce, SNonce) || Max(ANonce, SNonce))
+ *
+ * STK = PRF-X(SMK, "Peer key expansion",
+ * Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) ||
+ * Min(INonce, PNonce) || Max(INonce, PNonce))
+ */
+void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+ const u8 *addr1, const u8 *addr2,
+ const u8 *nonce1, const u8 *nonce2,
+ u8 *ptk, size_t ptk_len, int use_sha256)
+{
+ u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
+
+ if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
+ os_memcpy(data, addr1, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
+ } else {
+ os_memcpy(data, addr2, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, addr1, ETH_ALEN);
+ }
+
+ if (os_memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) {
+ os_memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN);
+ os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2,
+ WPA_NONCE_LEN);
+ } else {
+ os_memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN);
+ os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1,
+ WPA_NONCE_LEN);
+ }
+
+#ifdef CONFIG_IEEE80211W
+ if (use_sha256)
+ sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+ ptk, ptk_len);
+ else
+#endif /* CONFIG_IEEE80211W */
+ sha1_prf(pmk, pmk_len, label, data, sizeof(data), ptk,
+ ptk_len);
+
+ wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
+ MAC2STR(addr1), MAC2STR(addr2));
+ wpa_hexdump(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;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifndef CONFIG_NO_WPA2
+static int rsn_selector_to_bitfield(const u8 *s)
+{
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE)
+ return WPA_CIPHER_NONE;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP40)
+ return WPA_CIPHER_WEP40;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP)
+ return WPA_CIPHER_TKIP;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP)
+ return WPA_CIPHER_CCMP;
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP104)
+ return WPA_CIPHER_WEP104;
+#ifdef CONFIG_IEEE80211W
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC)
+ return WPA_CIPHER_AES_128_CMAC;
+#endif /* CONFIG_IEEE80211W */
+ return 0;
+}
+
+
+static int rsn_key_mgmt_to_bitfield(const u8 *s)
+{
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X)
+ return WPA_KEY_MGMT_IEEE8021X;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X)
+ return WPA_KEY_MGMT_PSK;
+#ifdef CONFIG_IEEE80211R
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X)
+ return WPA_KEY_MGMT_FT_IEEE8021X;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK)
+ return WPA_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
+ return WPA_KEY_MGMT_IEEE8021X_SHA256;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256)
+ return WPA_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+ return 0;
+}
+#endif /* CONFIG_NO_WPA2 */
+
+
+/**
+ * wpa_parse_wpa_ie_rsn - Parse RSN IE
+ * @rsn_ie: Buffer containing RSN IE
+ * @rsn_ie_len: RSN IE buffer length (including IE number and length octets)
+ * @data: Pointer to structure that will be filled in with parsed data
+ * Returns: 0 on success, <0 on failure
+ */
+int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
+ struct wpa_ie_data *data)
+{
+#ifndef CONFIG_NO_WPA2
+ const struct rsn_ie_hdr *hdr;
+ const u8 *pos;
+ int left;
+ int i, count;
+
+ os_memset(data, 0, sizeof(*data));
+ data->proto = WPA_PROTO_RSN;
+ data->pairwise_cipher = WPA_CIPHER_CCMP;
+ data->group_cipher = WPA_CIPHER_CCMP;
+ data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ data->capabilities = 0;
+ data->pmkid = NULL;
+ data->num_pmkid = 0;
+#ifdef CONFIG_IEEE80211W
+ data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
+#else /* CONFIG_IEEE80211W */
+ data->mgmt_group_cipher = 0;
+#endif /* CONFIG_IEEE80211W */
+
+ if (rsn_ie_len == 0) {
+ /* No RSN IE - fail silently */
+ return -1;
+ }
+
+ if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) {
+ wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+ __func__, (unsigned long) rsn_ie_len);
+ return -1;
+ }
+
+ hdr = (const struct rsn_ie_hdr *) rsn_ie;
+
+ if (hdr->elem_id != WLAN_EID_RSN ||
+ hdr->len != rsn_ie_len - 2 ||
+ WPA_GET_LE16(hdr->version) != RSN_VERSION) {
+ wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+ __func__);
+ return -2;
+ }
+
+ pos = (const u8 *) (hdr + 1);
+ left = rsn_ie_len - sizeof(*hdr);
+
+ if (left >= RSN_SELECTOR_LEN) {
+ data->group_cipher = rsn_selector_to_bitfield(pos);
+#ifdef CONFIG_IEEE80211W
+ if (data->group_cipher == WPA_CIPHER_AES_128_CMAC) {
+ wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as group "
+ "cipher", __func__);
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211W */
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ } else if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+ __func__, left);
+ return -3;
+ }
+
+ if (left >= 2) {
+ data->pairwise_cipher = 0;
+ count = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+ "count %u left %u", __func__, count, left);
+ return -4;
+ }
+ for (i = 0; i < count; i++) {
+ data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+#ifdef CONFIG_IEEE80211W
+ if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) {
+ wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as "
+ "pairwise cipher", __func__);
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211W */
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+ __func__);
+ return -5;
+ }
+
+ if (left >= 2) {
+ data->key_mgmt = 0;
+ count = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+ "count %u left %u", __func__, count, left);
+ return -6;
+ }
+ for (i = 0; i < count; i++) {
+ data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos);
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+ __func__);
+ return -7;
+ }
+
+ if (left >= 2) {
+ data->capabilities = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ }
+
+ if (left >= 2) {
+ data->num_pmkid = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (left < (int) data->num_pmkid * PMKID_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: PMKID underflow "
+ "(num_pmkid=%lu left=%d)",
+ __func__, (unsigned long) data->num_pmkid,
+ left);
+ data->num_pmkid = 0;
+ return -9;
+ } else {
+ data->pmkid = pos;
+ pos += data->num_pmkid * PMKID_LEN;
+ left -= data->num_pmkid * PMKID_LEN;
+ }
+ }
+
+#ifdef CONFIG_IEEE80211W
+ if (left >= 4) {
+ data->mgmt_group_cipher = rsn_selector_to_bitfield(pos);
+ if (data->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) {
+ wpa_printf(MSG_DEBUG, "%s: Unsupported management "
+ "group cipher 0x%x", __func__,
+ data->mgmt_group_cipher);
+ return -10;
+ }
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
+ __func__, left);
+ }
+
+ return 0;
+#else /* CONFIG_NO_WPA2 */
+ return -1;
+#endif /* CONFIG_NO_WPA2 */
+}
+
+
+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";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+/**
+ * wpa_key_mgmt_txt - Convert key management suite to a text string
+ * @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum)
+ * @proto: WPA/WPA2 version (WPA_PROTO_*)
+ * Returns: Pointer to a text string of the key management suite name
+ */
+const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
+{
+ switch (key_mgmt) {
+ case WPA_KEY_MGMT_IEEE8021X:
+ if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
+ return "WPA2+WPA/IEEE 802.1X/EAP";
+ return proto == WPA_PROTO_RSN ?
+ "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP";
+ case WPA_KEY_MGMT_PSK:
+ if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
+ return "WPA2-PSK+WPA-PSK";
+ return proto == WPA_PROTO_RSN ?
+ "WPA2-PSK" : "WPA-PSK";
+ case WPA_KEY_MGMT_NONE:
+ return "NONE";
+ case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
+ return "IEEE 802.1X (no WPA)";
+#ifdef CONFIG_IEEE80211R
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ return "FT-EAP";
+ case WPA_KEY_MGMT_FT_PSK:
+ return "FT-PSK";
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ case WPA_KEY_MGMT_IEEE8021X_SHA256:
+ return "WPA2-EAP-SHA256";
+ case WPA_KEY_MGMT_PSK_SHA256:
+ return "WPA2-PSK-SHA256";
+#endif /* CONFIG_IEEE80211W */
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+int wpa_compare_rsn_ie(int ft_initial_assoc,
+ const u8 *ie1, size_t ie1len,
+ const u8 *ie2, size_t ie2len)
+{
+ if (ie1 == NULL || ie2 == NULL)
+ return -1;
+
+ if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
+ return 0; /* identical IEs */
+
+#ifdef CONFIG_IEEE80211R
+ if (ft_initial_assoc) {
+ struct wpa_ie_data ie1d, ie2d;
+ /*
+ * The PMKID-List in RSN IE is different between Beacon/Probe
+ * Response/(Re)Association Request frames and EAPOL-Key
+ * messages in FT initial mobility domain association. Allow
+ * for this, but verify that other parts of the RSN IEs are
+ * identical.
+ */
+ if (wpa_parse_wpa_ie_rsn(ie1, ie1len, &ie1d) < 0 ||
+ wpa_parse_wpa_ie_rsn(ie2, ie2len, &ie2d) < 0)
+ return -1;
+ if (ie1d.proto == ie2d.proto &&
+ ie1d.pairwise_cipher == ie2d.pairwise_cipher &&
+ ie1d.group_cipher == ie2d.group_cipher &&
+ ie1d.key_mgmt == ie2d.key_mgmt &&
+ ie1d.capabilities == ie2d.capabilities &&
+ ie1d.mgmt_group_cipher == ie2d.mgmt_group_cipher)
+ return 0;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ return -1;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid)
+{
+ u8 *start, *end, *rpos, *rend;
+ int added = 0;
+
+ start = ies;
+ end = ies + ies_len;
+
+ while (start < end) {
+ if (*start == WLAN_EID_RSN)
+ break;
+ start += 2 + start[1];
+ }
+ if (start >= end) {
+ wpa_printf(MSG_ERROR, "FT: Could not find RSN IE in "
+ "IEs data");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "FT: RSN IE before modification",
+ start, 2 + start[1]);
+
+ /* Find start of PMKID-Count */
+ rpos = start + 2;
+ rend = rpos + start[1];
+
+ /* Skip Version and Group Data Cipher Suite */
+ rpos += 2 + 4;
+ /* Skip Pairwise Cipher Suite Count and List */
+ rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
+ /* Skip AKM Suite Count and List */
+ rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
+
+ if (rpos == rend) {
+ /* Add RSN Capabilities */
+ os_memmove(rpos + 2, rpos, end - rpos);
+ *rpos++ = 0;
+ *rpos++ = 0;
+ } else {
+ /* Skip RSN Capabilities */
+ rpos += 2;
+ if (rpos > rend) {
+ wpa_printf(MSG_ERROR, "FT: Could not parse RSN IE in "
+ "IEs data");
+ return -1;
+ }
+ }
+
+ if (rpos == rend) {
+ /* No PMKID-Count field included; add it */
+ os_memmove(rpos + 2 + PMKID_LEN, rpos, end - rpos);
+ WPA_PUT_LE16(rpos, 1);
+ rpos += 2;
+ os_memcpy(rpos, pmkid, PMKID_LEN);
+ added += 2 + PMKID_LEN;
+ start[1] += 2 + PMKID_LEN;
+ } else {
+ /* PMKID-Count was included; use it */
+ if (WPA_GET_LE16(rpos) != 0) {
+ wpa_printf(MSG_ERROR, "FT: Unexpected PMKID "
+ "in RSN IE in EAPOL-Key data");
+ return -1;
+ }
+ WPA_PUT_LE16(rpos, 1);
+ rpos += 2;
+ os_memmove(rpos + PMKID_LEN, rpos, end - rpos);
+ os_memcpy(rpos, pmkid, PMKID_LEN);
+ added += PMKID_LEN;
+ start[1] += PMKID_LEN;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification "
+ "(PMKID inserted)", start, 2 + start[1]);
+
+ return added;
+}
+#endif /* CONFIG_IEEE80211R */
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
new file mode 100644
index 0000000..fe79cee
--- /dev/null
+++ b/src/common/wpa_common.h
@@ -0,0 +1,361 @@
+/*
+ * WPA definitions shared between hostapd and wpa_supplicant
+ * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_COMMON_H
+#define WPA_COMMON_H
+
+#define WPA_MAX_SSID_LEN 32
+
+/* IEEE 802.11i */
+#define PMKID_LEN 16
+#define PMK_LEN 32
+#define WPA_REPLAY_COUNTER_LEN 8
+#define WPA_NONCE_LEN 32
+#define WPA_KEY_RSC_LEN 8
+#define WPA_GMK_LEN 32
+#define WPA_GTK_MAX_LEN 32
+
+#define WPA_SELECTOR_LEN 4
+#define WPA_VERSION 1
+#define RSN_SELECTOR_LEN 4
+#define RSN_VERSION 1
+
+#define RSN_SELECTOR(a, b, c, d) \
+ ((((u32) (a)) << 24) | (((u32) (b)) << 16) | (((u32) (c)) << 8) | \
+ (u32) (d))
+
+#define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
+#define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
+#define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
+#define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
+#define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
+#define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
+#if 0
+#define WPA_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x50, 0xf2, 3)
+#endif
+#define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4)
+#define WPA_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x50, 0xf2, 5)
+
+
+#define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
+#define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+#ifdef CONFIG_IEEE80211R
+#define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+#define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#endif /* CONFIG_IEEE80211R */
+#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
+#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+#define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+
+#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)
+
+/* EAPOL-Key Key Data Encapsulation
+ * GroupKey and PeerKey require encryption, otherwise, encryption is optional.
+ */
+#define RSN_KEY_DATA_GROUPKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
+#if 0
+#define RSN_KEY_DATA_STAKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+#endif
+#define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+#define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#ifdef CONFIG_PEERKEY
+#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
+#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211W
+#define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+#endif /* CONFIG_IEEE80211W */
+
+#define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
+
+#define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val))
+#define RSN_SELECTOR_GET(a) WPA_GET_BE32((const u8 *) (a))
+
+#define RSN_NUM_REPLAY_COUNTERS_1 0
+#define RSN_NUM_REPLAY_COUNTERS_2 1
+#define RSN_NUM_REPLAY_COUNTERS_4 2
+#define RSN_NUM_REPLAY_COUNTERS_16 3
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#ifdef CONFIG_IEEE80211W
+#define WPA_IGTK_LEN 16
+#endif /* CONFIG_IEEE80211W */
+
+
+/* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */
+#define WPA_CAPABILITY_PREAUTH BIT(0)
+#define WPA_CAPABILITY_NO_PAIRWISE BIT(1)
+/* B2-B3: PTKSA Replay Counter */
+/* B4-B5: GTKSA Replay Counter */
+#define WPA_CAPABILITY_MFPR BIT(6)
+#define WPA_CAPABILITY_MFPC BIT(7)
+/* 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);
+
+#endif /* WPA_COMMON_H */
diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
new file mode 100644
index 0000000..88d3a02
--- /dev/null
+++ b/src/common/wpa_ctrl.c
@@ -0,0 +1,500 @@
+/*
+ * wpa_supplicant/hostapd control interface library
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#ifdef CONFIG_CTRL_IFACE
+
+#ifdef CONFIG_CTRL_IFACE_UNIX
+#include <sys/un.h>
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+
+#ifdef ANDROID
+#include <cutils/sockets.h>
+#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;
+#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;
+
+ 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 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 != NULL && *ctrl_path != '/') {
+ char buf[21];
+ os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path);
+ if (socket_local_client_connect(
+ ctrl->s, buf,
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_DGRAM) < 0) {
+ close(ctrl->s);
+ unlink(ctrl->local.sun_path);
+ os_free(ctrl);
+ return NULL;
+ }
+ return ctrl;
+ }
+#endif /* ANDROID */
+
+ ctrl->dest.sun_family = AF_UNIX;
+ res = os_strlcpy(ctrl->dest.sun_path, ctrl_path,
+ sizeof(ctrl->dest.sun_path));
+ if (res >= sizeof(ctrl->dest.sun_path)) {
+ close(ctrl->s);
+ os_free(ctrl);
+ return NULL;
+ }
+ if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
+ sizeof(ctrl->dest)) < 0) {
+ close(ctrl->s);
+ unlink(ctrl->local.sun_path);
+ os_free(ctrl);
+ return NULL;
+ }
+
+ return ctrl;
+}
+
+
+void wpa_ctrl_close(struct wpa_ctrl *ctrl)
+{
+ if (ctrl == NULL)
+ return;
+ unlink(ctrl->local.sun_path);
+ if (ctrl->s >= 0)
+ close(ctrl->s);
+ os_free(ctrl);
+}
+
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
+{
+ struct wpa_ctrl *ctrl;
+ char buf[128];
+ size_t len;
+
+ ctrl = os_malloc(sizeof(*ctrl));
+ if (ctrl == NULL)
+ return NULL;
+ os_memset(ctrl, 0, sizeof(*ctrl));
+
+ ctrl->s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (ctrl->s < 0) {
+ perror("socket");
+ os_free(ctrl);
+ return NULL;
+ }
+
+ ctrl->local.sin_family = AF_INET;
+ ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1);
+ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+ sizeof(ctrl->local)) < 0) {
+ close(ctrl->s);
+ os_free(ctrl);
+ return NULL;
+ }
+
+ ctrl->dest.sin_family = AF_INET;
+ ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1);
+ ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT);
+ if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
+ sizeof(ctrl->dest)) < 0) {
+ perror("connect");
+ close(ctrl->s);
+ os_free(ctrl);
+ return NULL;
+ }
+
+ len = sizeof(buf) - 1;
+ if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) {
+ buf[len] = '\0';
+ ctrl->cookie = os_strdup(buf);
+ }
+
+ return ctrl;
+}
+
+
+void wpa_ctrl_close(struct wpa_ctrl *ctrl)
+{
+ close(ctrl->s);
+ os_free(ctrl->cookie);
+ os_free(ctrl);
+}
+
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+
+#ifdef CTRL_IFACE_SOCKET
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+ char *reply, size_t *reply_len,
+ void (*msg_cb)(char *msg, size_t len))
+{
+ struct timeval tv;
+ int res;
+ fd_set rfds;
+ const char *_cmd;
+ char *cmd_buf = NULL;
+ size_t _cmd_len;
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+ if (ctrl->cookie) {
+ char *pos;
+ _cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len;
+ cmd_buf = os_malloc(_cmd_len);
+ if (cmd_buf == NULL)
+ return -1;
+ _cmd = cmd_buf;
+ pos = cmd_buf;
+ os_strlcpy(pos, ctrl->cookie, _cmd_len);
+ pos += os_strlen(ctrl->cookie);
+ *pos++ = ' ';
+ os_memcpy(pos, cmd, cmd_len);
+ } else
+#endif /* CONFIG_CTRL_IFACE_UDP */
+ {
+ _cmd = cmd;
+ _cmd_len = cmd_len;
+ }
+
+ if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
+ os_free(cmd_buf);
+ return -1;
+ }
+ os_free(cmd_buf);
+
+ for (;;) {
+ tv.tv_sec = 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/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
new file mode 100644
index 0000000..528cc16
--- /dev/null
+++ b/src/common/wpa_ctrl.h
@@ -0,0 +1,274 @@
+/*
+ * wpa_supplicant/hostapd control interface library
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_CTRL_H
+#define WPA_CTRL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* wpa_supplicant control interface - fixed message prefixes */
+
+/** Interactive request for identity/password/pin */
+#define WPA_CTRL_REQ "CTRL-REQ-"
+
+/** Response to identity/password/pin request */
+#define WPA_CTRL_RSP "CTRL-RSP-"
+
+/* Event messages with fixed prefix */
+/** Authentication completed successfully and data connection enabled */
+#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED "
+/** Disconnected, data connection is not available */
+#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED "
+/** 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 authentication completed successfully */
+#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
+/** EAP authentication failed (EAP-Failure received) */
+#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
+/** New scan results available */
+#define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
+/** 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 "
+
+/** 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 "
+
+#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 "
+/** 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: <peer address> <PIN> */
+#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP "
+/* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */
+#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
+/* parameters: <src addr> <update indicator> <TLVs> */
+#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 "
+
+/* hostapd control interface - fixed message prefixes */
+#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
+#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
+#define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS "
+#define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED "
+#define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED "
+#define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED "
+#define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED "
+#define AP_STA_CONNECTED "AP-STA-CONNECTED "
+#define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
+
+
+/* wpa_supplicant/hostapd control interface access */
+
+/**
+ * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd
+ * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
+ * Returns: Pointer to abstract control interface data or %NULL on failure
+ *
+ * This function is used to open a control interface to wpa_supplicant/hostapd.
+ * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path
+ * is configured in wpa_supplicant/hostapd and other programs using the control
+ * interface need to use matching path configuration.
+ */
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
+
+
+/**
+ * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ *
+ * This function is used to close a control interface.
+ */
+void wpa_ctrl_close(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * @cmd: Command; usually, ASCII text, e.g., "PING"
+ * @cmd_len: Length of the cmd in bytes
+ * @reply: Buffer for the response
+ * @reply_len: Reply buffer length
+ * @msg_cb: Callback function for unsolicited messages or %NULL if not used
+ * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout
+ *
+ * This function is used to send commands to wpa_supplicant/hostapd. Received
+ * response will be written to reply and reply_len is set to the actual length
+ * of the reply. This function will block for up to two seconds while waiting
+ * for the reply. If unsolicited messages are received, the blocking time may
+ * be longer.
+ *
+ * msg_cb can be used to register a callback function that will be called for
+ * unsolicited messages received while waiting for the command response. These
+ * messages may be received if wpa_ctrl_request() is called at the same time as
+ * wpa_supplicant/hostapd is sending such a message. This can happen only if
+ * the program has used wpa_ctrl_attach() to register itself as a monitor for
+ * event messages. Alternatively to msg_cb, programs can register two control
+ * interface connections and use one of them for commands and the other one for
+ * receiving event messages, in other words, call wpa_ctrl_attach() only for
+ * the control interface connection that will be used for event messages.
+ */
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+ char *reply, size_t *reply_len,
+ void (*msg_cb)(char *msg, size_t len));
+
+
+/**
+ * wpa_ctrl_attach - Register as an event monitor for the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 0 on success, -1 on failure, -2 on timeout
+ *
+ * This function registers the control interface connection as a monitor for
+ * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the
+ * control interface connection starts receiving event messages that can be
+ * read with wpa_ctrl_recv().
+ */
+int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_detach - Unregister event monitor from the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 0 on success, -1 on failure, -2 on timeout
+ *
+ * This function unregisters the control interface connection as a monitor for
+ * wpa_supplicant/hostapd events, i.e., cancels the registration done with
+ * wpa_ctrl_attach().
+ */
+int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_recv - Receive a pending control interface message
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * @reply: Buffer for the message data
+ * @reply_len: Length of the reply buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function will receive a pending control interface message. This
+ * function will block if no messages are available. The received response will
+ * be written to reply and reply_len is set to the actual length of the reply.
+ * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach()
+ * must have been used to register the control interface as an event monitor.
+ */
+int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
+
+
+/**
+ * wpa_ctrl_pending - Check whether there are pending event messages
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 1 if there are pending messages, 0 if no, or -1 on error
+ *
+ * This function will check whether there are any pending control interface
+ * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is
+ * only used for event messages, i.e., wpa_ctrl_attach() must have been used to
+ * register the control interface as an event monitor.
+ */
+int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_get_fd - Get file descriptor used by the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: File descriptor used for the connection
+ *
+ * This function can be used to get the file descriptor that is used for the
+ * control interface connection. The returned value can be used, e.g., with
+ * select() while waiting for multiple events.
+ *
+ * The returned file descriptor must not be used directly for sending or
+ * receiving packets; instead, the library functions wpa_ctrl_request() and
+ * wpa_ctrl_recv() must be used for this.
+ */
+int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+#define WPA_CTRL_IFACE_PORT 9877
+#define WPA_GLOBAL_CTRL_IFACE_PORT 9878
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WPA_CTRL_H */
diff --git a/src/crypto/.gitignore b/src/crypto/.gitignore
new file mode 100644
index 0000000..ee60604
--- /dev/null
+++ b/src/crypto/.gitignore
@@ -0,0 +1 @@
+libcrypto.a
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
new file mode 100644
index 0000000..69aa16a
--- /dev/null
+++ b/src/crypto/Makefile
@@ -0,0 +1,56 @@
+all: libcrypto.a
+
+clean:
+ rm -f *~ *.o *.d libcrypto.a
+
+install:
+ @echo Nothing to be made.
+
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+#CFLAGS += -DALL_DH_GROUPS
+
+LIB_OBJS= \
+ aes-cbc.o \
+ aes-ctr.o \
+ aes-eax.o \
+ aes-encblock.o \
+ aes-internal.o \
+ aes-internal-dec.o \
+ aes-internal-enc.o \
+ aes-omac1.o \
+ aes-unwrap.o \
+ aes-wrap.o \
+ des-internal.o \
+ dh_group5.o \
+ dh_groups.o \
+ md4-internal.o \
+ md5.o \
+ md5-internal.o \
+ md5-non-fips.o \
+ milenage.o \
+ ms_funcs.o \
+ rc4.o \
+ sha1.o \
+ sha1-internal.o \
+ sha1-pbkdf2.o \
+ sha1-tlsprf.o \
+ sha1-tprf.o \
+ sha256.o \
+ sha256-internal.o
+
+LIB_OBJS += crypto_internal.o
+LIB_OBJS += crypto_internal-cipher.o
+LIB_OBJS += crypto_internal-modexp.o
+LIB_OBJS += crypto_internal-rsa.o
+LIB_OBJS += tls_internal.o
+LIB_OBJS += fips_prf_internal.o
+
+
+libcrypto.a: $(LIB_OBJS)
+ $(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/crypto/aes-cbc.c b/src/crypto/aes-cbc.c
new file mode 100644
index 0000000..bd74769
--- /dev/null
+++ b/src/crypto/aes-cbc.c
@@ -0,0 +1,86 @@
+/*
+ * AES-128 CBC
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_cbc_encrypt - AES-128 CBC encryption
+ * @key: Encryption key
+ * @iv: Encryption IV for CBC mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes (must be divisible by 16)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+ void *ctx;
+ u8 cbc[AES_BLOCK_SIZE];
+ u8 *pos = data;
+ int i, j, blocks;
+
+ ctx = aes_encrypt_init(key, 16);
+ if (ctx == NULL)
+ return -1;
+ os_memcpy(cbc, iv, AES_BLOCK_SIZE);
+
+ blocks = data_len / AES_BLOCK_SIZE;
+ for (i = 0; i < blocks; i++) {
+ for (j = 0; j < AES_BLOCK_SIZE; j++)
+ cbc[j] ^= pos[j];
+ aes_encrypt(ctx, cbc, cbc);
+ os_memcpy(pos, cbc, AES_BLOCK_SIZE);
+ pos += AES_BLOCK_SIZE;
+ }
+ aes_encrypt_deinit(ctx);
+ return 0;
+}
+
+
+/**
+ * aes_128_cbc_decrypt - AES-128 CBC decryption
+ * @key: Decryption key
+ * @iv: Decryption IV for CBC mode (16 bytes)
+ * @data: Data to decrypt in-place
+ * @data_len: Length of data in bytes (must be divisible by 16)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+ void *ctx;
+ u8 cbc[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE];
+ u8 *pos = data;
+ int i, j, blocks;
+
+ ctx = aes_decrypt_init(key, 16);
+ if (ctx == NULL)
+ return -1;
+ os_memcpy(cbc, iv, AES_BLOCK_SIZE);
+
+ blocks = data_len / AES_BLOCK_SIZE;
+ for (i = 0; i < blocks; i++) {
+ os_memcpy(tmp, pos, AES_BLOCK_SIZE);
+ aes_decrypt(ctx, pos, pos);
+ for (j = 0; j < AES_BLOCK_SIZE; j++)
+ pos[j] ^= cbc[j];
+ os_memcpy(cbc, tmp, AES_BLOCK_SIZE);
+ pos += AES_BLOCK_SIZE;
+ }
+ aes_decrypt_deinit(ctx);
+ return 0;
+}
diff --git a/src/crypto/aes-ctr.c b/src/crypto/aes-ctr.c
new file mode 100644
index 0000000..468f877
--- /dev/null
+++ b/src/crypto/aes-ctr.c
@@ -0,0 +1,61 @@
+/*
+ * AES-128 CTR
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_ctr_encrypt - AES-128 CTR mode encryption
+ * @key: Key for encryption (16 bytes)
+ * @nonce: Nonce for counter mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+ u8 *data, size_t data_len)
+{
+ void *ctx;
+ size_t j, len, left = data_len;
+ int i;
+ u8 *pos = data;
+ u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE];
+
+ ctx = aes_encrypt_init(key, 16);
+ if (ctx == NULL)
+ return -1;
+ os_memcpy(counter, nonce, AES_BLOCK_SIZE);
+
+ while (left > 0) {
+ aes_encrypt(ctx, counter, buf);
+
+ len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE;
+ for (j = 0; j < len; j++)
+ pos[j] ^= buf[j];
+ pos += len;
+ left -= len;
+
+ for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) {
+ counter[i]++;
+ if (counter[i])
+ break;
+ }
+ }
+ aes_encrypt_deinit(ctx);
+ return 0;
+}
diff --git a/src/crypto/aes-eax.c b/src/crypto/aes-eax.c
new file mode 100644
index 0000000..d5c3971
--- /dev/null
+++ b/src/crypto/aes-eax.c
@@ -0,0 +1,151 @@
+/*
+ * AES-128 EAX
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_eax_encrypt - AES-128 EAX mode encryption
+ * @key: Key for encryption (16 bytes)
+ * @nonce: Nonce for counter mode
+ * @nonce_len: Nonce length in bytes
+ * @hdr: Header data to be authenticity protected
+ * @hdr_len: Length of the header data bytes
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * @tag: 16-byte tag value
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, u8 *tag)
+{
+ u8 *buf;
+ size_t buf_len;
+ u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE],
+ data_mac[AES_BLOCK_SIZE];
+ int i, ret = -1;
+
+ if (nonce_len > data_len)
+ buf_len = nonce_len;
+ else
+ buf_len = data_len;
+ if (hdr_len > buf_len)
+ buf_len = hdr_len;
+ buf_len += 16;
+
+ buf = os_malloc(buf_len);
+ if (buf == NULL)
+ return -1;
+
+ os_memset(buf, 0, 15);
+
+ buf[15] = 0;
+ os_memcpy(buf + 16, nonce, nonce_len);
+ if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac))
+ goto fail;
+
+ buf[15] = 1;
+ os_memcpy(buf + 16, hdr, hdr_len);
+ if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac))
+ goto fail;
+
+ if (aes_128_ctr_encrypt(key, nonce_mac, data, data_len))
+ goto fail;
+ buf[15] = 2;
+ os_memcpy(buf + 16, data, data_len);
+ if (omac1_aes_128(key, buf, 16 + data_len, data_mac))
+ goto fail;
+
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
+ tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i];
+
+ ret = 0;
+fail:
+ os_free(buf);
+
+ return ret;
+}
+
+
+/**
+ * aes_128_eax_decrypt - AES-128 EAX mode decryption
+ * @key: Key for decryption (16 bytes)
+ * @nonce: Nonce for counter mode
+ * @nonce_len: Nonce length in bytes
+ * @hdr: Header data to be authenticity protected
+ * @hdr_len: Length of the header data bytes
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * @tag: 16-byte tag value
+ * Returns: 0 on success, -1 on failure, -2 if tag does not match
+ */
+int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, const u8 *tag)
+{
+ u8 *buf;
+ size_t buf_len;
+ u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE],
+ data_mac[AES_BLOCK_SIZE];
+ int i;
+
+ if (nonce_len > data_len)
+ buf_len = nonce_len;
+ else
+ buf_len = data_len;
+ if (hdr_len > buf_len)
+ buf_len = hdr_len;
+ buf_len += 16;
+
+ buf = os_malloc(buf_len);
+ if (buf == NULL)
+ return -1;
+
+ os_memset(buf, 0, 15);
+
+ buf[15] = 0;
+ os_memcpy(buf + 16, nonce, nonce_len);
+ if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) {
+ os_free(buf);
+ return -1;
+ }
+
+ buf[15] = 1;
+ os_memcpy(buf + 16, hdr, hdr_len);
+ if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) {
+ os_free(buf);
+ return -1;
+ }
+
+ buf[15] = 2;
+ os_memcpy(buf + 16, data, data_len);
+ if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) {
+ os_free(buf);
+ return -1;
+ }
+
+ os_free(buf);
+
+ for (i = 0; i < AES_BLOCK_SIZE; i++) {
+ if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]))
+ return -2;
+ }
+
+ return aes_128_ctr_encrypt(key, nonce_mac, data, data_len);
+}
diff --git a/src/crypto/aes-encblock.c b/src/crypto/aes-encblock.c
new file mode 100644
index 0000000..8f35caa
--- /dev/null
+++ b/src/crypto/aes-encblock.c
@@ -0,0 +1,38 @@
+/*
+ * AES encrypt_block
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_encrypt_block - Perform one AES 128-bit block operation
+ * @key: Key for AES
+ * @in: Input data (16 bytes)
+ * @out: Output of the AES block operation (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
+{
+ void *ctx;
+ ctx = aes_encrypt_init(key, 16);
+ if (ctx == NULL)
+ return -1;
+ aes_encrypt(ctx, in, out);
+ aes_encrypt_deinit(ctx);
+ return 0;
+}
diff --git a/src/crypto/aes-internal-dec.c b/src/crypto/aes-internal-dec.c
new file mode 100644
index 0000000..2d32c03
--- /dev/null
+++ b/src/crypto/aes-internal-dec.c
@@ -0,0 +1,151 @@
+/*
+ * AES (Rijndael) cipher - decrypt
+ *
+ * Modifications to public domain implementation:
+ * - support only 128-bit keys
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ * cost of reduced throughput (quite small difference on Pentium 4,
+ * 10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "aes_i.h"
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ *
+ * @return the number of rounds for the given cipher key size.
+ */
+void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[])
+{
+ int Nr = 10, i, j;
+ u32 temp;
+
+ /* expand the cipher key: */
+ rijndaelKeySetupEnc(rk, cipherKey);
+ /* invert the order of the round keys: */
+ for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) {
+ temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp;
+ temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+ temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+ temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+ }
+ /* apply the inverse MixColumn transform to all round keys but the
+ * first and the last: */
+ for (i = 1; i < Nr; i++) {
+ rk += 4;
+ for (j = 0; j < 4; j++) {
+ rk[j] = TD0_(TE4((rk[j] >> 24) )) ^
+ TD1_(TE4((rk[j] >> 16) & 0xff)) ^
+ TD2_(TE4((rk[j] >> 8) & 0xff)) ^
+ TD3_(TE4((rk[j] ) & 0xff));
+ }
+ }
+}
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+ u32 *rk;
+ if (len != 16)
+ return NULL;
+ rk = os_malloc(AES_PRIV_SIZE);
+ if (rk == NULL)
+ return NULL;
+ rijndaelKeySetupDec(rk, key);
+ return rk;
+}
+
+static void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16])
+{
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+ const int Nr = 10;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(ct ) ^ rk[0];
+ s1 = GETU32(ct + 4) ^ rk[1];
+ s2 = GETU32(ct + 8) ^ rk[2];
+ s3 = GETU32(ct + 12) ^ rk[3];
+
+#define ROUND(i,d,s) \
+d##0 = TD0(s##0) ^ TD1(s##3) ^ TD2(s##2) ^ TD3(s##1) ^ rk[4 * i]; \
+d##1 = TD0(s##1) ^ TD1(s##0) ^ TD2(s##3) ^ TD3(s##2) ^ rk[4 * i + 1]; \
+d##2 = TD0(s##2) ^ TD1(s##1) ^ TD2(s##0) ^ TD3(s##3) ^ rk[4 * i + 2]; \
+d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3]
+
+#ifdef FULL_UNROLL
+
+ ROUND(1,t,s);
+ ROUND(2,s,t);
+ ROUND(3,t,s);
+ ROUND(4,s,t);
+ ROUND(5,t,s);
+ ROUND(6,s,t);
+ ROUND(7,t,s);
+ ROUND(8,s,t);
+ ROUND(9,t,s);
+
+ rk += Nr << 2;
+
+#else /* !FULL_UNROLL */
+
+ /* Nr - 1 full rounds: */
+ r = Nr >> 1;
+ for (;;) {
+ ROUND(1,t,s);
+ rk += 8;
+ if (--r == 0)
+ break;
+ ROUND(0,s,t);
+ }
+
+#endif /* ?FULL_UNROLL */
+
+#undef ROUND
+
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0];
+ PUTU32(pt , s0);
+ s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1];
+ PUTU32(pt + 4, s1);
+ s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2];
+ PUTU32(pt + 8, s2);
+ s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3];
+ PUTU32(pt + 12, s3);
+}
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+ rijndaelDecrypt(ctx, crypt, plain);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+ os_memset(ctx, 0, AES_PRIV_SIZE);
+ os_free(ctx);
+}
diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c
new file mode 100644
index 0000000..2f19826
--- /dev/null
+++ b/src/crypto/aes-internal-enc.c
@@ -0,0 +1,121 @@
+/*
+ * AES (Rijndael) cipher - encrypt
+ *
+ * Modifications to public domain implementation:
+ * - support only 128-bit keys
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ * cost of reduced throughput (quite small difference on Pentium 4,
+ * 10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "aes_i.h"
+
+void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16])
+{
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+ const int Nr = 10;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(pt ) ^ rk[0];
+ s1 = GETU32(pt + 4) ^ rk[1];
+ s2 = GETU32(pt + 8) ^ rk[2];
+ s3 = GETU32(pt + 12) ^ rk[3];
+
+#define ROUND(i,d,s) \
+d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \
+d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \
+d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \
+d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3]
+
+#ifdef FULL_UNROLL
+
+ ROUND(1,t,s);
+ ROUND(2,s,t);
+ ROUND(3,t,s);
+ ROUND(4,s,t);
+ ROUND(5,t,s);
+ ROUND(6,s,t);
+ ROUND(7,t,s);
+ ROUND(8,s,t);
+ ROUND(9,t,s);
+
+ rk += Nr << 2;
+
+#else /* !FULL_UNROLL */
+
+ /* Nr - 1 full rounds: */
+ r = Nr >> 1;
+ for (;;) {
+ ROUND(1,t,s);
+ rk += 8;
+ if (--r == 0)
+ break;
+ ROUND(0,s,t);
+ }
+
+#endif /* ?FULL_UNROLL */
+
+#undef ROUND
+
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0];
+ PUTU32(ct , s0);
+ s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1];
+ PUTU32(ct + 4, s1);
+ s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2];
+ PUTU32(ct + 8, s2);
+ s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3];
+ PUTU32(ct + 12, s3);
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ u32 *rk;
+ if (len != 16)
+ return NULL;
+ rk = os_malloc(AES_PRIV_SIZE);
+ if (rk == NULL)
+ return NULL;
+ rijndaelKeySetupEnc(rk, key);
+ return rk;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+ rijndaelEncrypt(ctx, plain, crypt);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+ os_memset(ctx, 0, AES_PRIV_SIZE);
+ os_free(ctx);
+}
diff --git a/src/crypto/aes-internal.c b/src/crypto/aes-internal.c
new file mode 100644
index 0000000..4161220
--- /dev/null
+++ b/src/crypto/aes-internal.c
@@ -0,0 +1,805 @@
+/*
+ * AES (Rijndael) cipher
+ *
+ * Modifications to public domain implementation:
+ * - support only 128-bit keys
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ * cost of reduced throughput (quite small difference on Pentium 4,
+ * 10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "aes_i.h"
+
+/*
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+const u32 Te0[256] = {
+ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+ 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+ 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+ 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+ 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+ 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+ 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+ 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+ 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+ 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+ 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+ 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+ 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+ 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+ 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+ 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+ 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+ 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+ 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+ 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+ 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+ 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+ 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+ 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+ 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+ 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+ 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+ 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+ 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+ 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+ 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+ 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+ 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+ 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+ 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+ 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+ 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+ 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+ 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+ 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+ 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+ 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+ 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+ 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+ 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+ 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+ 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+ 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+ 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+ 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+ 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+ 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+#ifndef AES_SMALL_TABLES
+const u32 Te1[256] = {
+ 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+ 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+ 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+ 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+ 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+ 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+ 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+ 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+ 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+ 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+ 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+ 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+ 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+ 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+ 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+ 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+ 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+ 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+ 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+ 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+ 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+ 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+ 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+ 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+ 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+ 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+ 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+ 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+ 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+ 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+ 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+ 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+ 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+ 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+ 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+ 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+ 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+ 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+ 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+ 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+ 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+ 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+ 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+ 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+ 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+ 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+ 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+ 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+ 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+ 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+ 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+ 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+ 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+ 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+ 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+ 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+ 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+ 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+ 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+ 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+ 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+ 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+ 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+ 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+const u32 Te2[256] = {
+ 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+ 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+ 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+ 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+ 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+ 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+ 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+ 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+ 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+ 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+ 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+ 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+ 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+ 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+ 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+ 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+ 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+ 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+ 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+ 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+ 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+ 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+ 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+ 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+ 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+ 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+ 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+ 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+ 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+ 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+ 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+ 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+ 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+ 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+ 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+ 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+ 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+ 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+ 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+ 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+ 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+ 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+ 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+ 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+ 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+ 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+ 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+ 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+ 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+ 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+ 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+ 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+ 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+ 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+ 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+ 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+ 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+ 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+ 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+ 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+ 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+ 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+ 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+ 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+const u32 Te3[256] = {
+
+ 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+ 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+ 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+ 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+ 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+ 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+ 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+ 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+ 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+ 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+ 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+ 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+ 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+ 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+ 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+ 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+ 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+ 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+ 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+ 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+ 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+ 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+ 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+ 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+ 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+ 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+ 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+ 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+ 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+ 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+ 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+ 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+ 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+ 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+ 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+ 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+ 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+ 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+ 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+ 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+ 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+ 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+ 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+ 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+ 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+ 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+ 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+ 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+ 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+ 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+ 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+ 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+ 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+ 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+ 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+ 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+ 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+ 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+ 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+ 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+ 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+ 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+ 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+ 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+const u32 Te4[256] = {
+ 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+ 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+ 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+ 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+ 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+ 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+ 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+ 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+ 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+ 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+ 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+ 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+ 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+ 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+ 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+ 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+ 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+ 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+ 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+ 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+ 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+ 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+ 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+ 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+ 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+ 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+ 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+ 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+ 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+ 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+ 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+ 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+ 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+ 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+ 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+ 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+ 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+ 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+ 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+ 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+ 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+ 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+ 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+ 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+ 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+ 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+ 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+ 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+ 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+ 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+ 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+ 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+ 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+ 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+ 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+ 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+ 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+ 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+ 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+ 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+ 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+ 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+ 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+ 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+#endif /* AES_SMALL_TABLES */
+const u32 Td0[256] = {
+ 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+ 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+ 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+ 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+ 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+ 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+ 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+ 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+ 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+ 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+ 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+ 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+ 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+ 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+ 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+ 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+ 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+ 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+ 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+ 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+ 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+ 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+ 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+ 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+ 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+ 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+ 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+ 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+ 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+ 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+ 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+ 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+ 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+ 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+ 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+ 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+ 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+ 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+ 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+ 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+ 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+ 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+ 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+ 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+ 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+ 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+ 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+ 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+ 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+ 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+ 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+ 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+ 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+ 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+ 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+ 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+ 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+ 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+ 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+ 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+ 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+ 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+ 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+ 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+#ifndef AES_SMALL_TABLES
+const u32 Td1[256] = {
+ 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+ 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+ 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+ 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+ 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+ 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+ 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+ 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+ 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+ 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+ 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+ 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+ 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+ 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+ 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+ 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+ 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+ 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+ 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+ 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+ 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+ 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+ 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+ 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+ 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+ 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+ 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+ 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+ 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+ 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+ 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+ 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+ 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+ 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+ 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+ 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+ 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+ 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+ 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+ 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+ 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+ 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+ 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+ 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+ 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+ 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+ 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+ 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+ 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+ 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+ 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+ 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+ 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+ 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+ 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+ 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+ 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+ 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+ 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+ 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+ 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+ 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+ 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+ 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+const u32 Td2[256] = {
+ 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+ 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+ 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+ 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+ 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+ 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+ 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+ 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+ 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+ 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+ 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+ 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+ 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+ 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+ 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+ 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+ 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+ 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+ 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+ 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+ 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+ 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+ 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+ 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+ 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+ 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+ 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+ 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+ 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+ 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+ 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+ 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+ 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+ 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+ 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+ 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+ 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+ 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+ 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+ 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+ 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+ 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+ 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+ 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+ 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+ 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+ 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+ 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+ 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+ 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+ 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+ 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+ 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+ 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+ 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+ 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+ 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+ 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+ 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+ 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+ 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+ 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+ 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+ 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+const u32 Td3[256] = {
+ 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+ 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+ 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+ 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+ 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+ 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+ 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+ 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+ 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+ 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+ 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+ 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+ 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+ 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+ 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+ 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+ 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+ 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+ 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+ 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+ 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+ 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+ 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+ 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+ 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+ 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+ 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+ 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+ 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+ 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+ 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+ 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+ 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+ 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+ 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+ 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+ 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+ 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+ 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+ 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+ 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+ 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+ 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+ 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+ 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+ 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+ 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+ 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+ 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+ 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+ 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+ 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+ 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+ 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+ 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+ 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+ 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+ 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+ 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+ 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+ 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+ 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+ 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+ 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+const u32 Td4[256] = {
+ 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+ 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+ 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+ 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+ 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+ 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+ 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+ 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+ 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+ 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+ 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+ 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+ 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+ 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+ 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+ 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+ 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+ 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+ 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+ 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+ 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+ 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+ 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+ 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+ 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+ 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+ 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+ 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+ 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+ 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+ 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+ 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+ 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+ 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+ 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+ 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+ 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+ 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+ 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+ 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+ 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+ 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+ 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+ 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+ 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+ 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+ 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+ 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+ 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+ 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+ 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+ 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+ 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+ 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+ 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+ 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+ 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+ 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+ 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+ 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+ 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+ 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+ 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+ 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+const u32 rcon[] = {
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000,
+ 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+#else /* AES_SMALL_TABLES */
+const u8 Td4s[256] = {
+ 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U,
+ 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU,
+ 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U,
+ 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU,
+ 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU,
+ 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU,
+ 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U,
+ 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U,
+ 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U,
+ 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U,
+ 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,
+ 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U,
+ 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU,
+ 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U,
+ 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U,
+ 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU,
+ 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU,
+ 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U,
+ 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U,
+ 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU,
+ 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U,
+ 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,
+ 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U,
+ 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U,
+ 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U,
+ 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU,
+ 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU,
+ 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU,
+ 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U,
+ 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U,
+ 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U,
+ 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU,
+};
+const u8 rcons[] = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
+ /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+#endif /* AES_SMALL_TABLES */
+/**
+ * Expand the cipher key into the encryption key schedule.
+ *
+ * @return the number of rounds for the given cipher key size.
+ */
+void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[])
+{
+ int i;
+ u32 temp;
+
+ rk[0] = GETU32(cipherKey );
+ rk[1] = GETU32(cipherKey + 4);
+ rk[2] = GETU32(cipherKey + 8);
+ rk[3] = GETU32(cipherKey + 12);
+ for (i = 0; i < 10; i++) {
+ temp = rk[3];
+ rk[4] = rk[0] ^
+ TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^
+ RCON(i);
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ rk += 4;
+ }
+}
diff --git a/src/crypto/aes-omac1.c b/src/crypto/aes-omac1.c
new file mode 100644
index 0000000..f775296
--- /dev/null
+++ b/src/crypto/aes-omac1.c
@@ -0,0 +1,124 @@
+/*
+ * One-key CBC MAC (OMAC1) hash with AES-128
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+static void gf_mulx(u8 *pad)
+{
+ int i, carry;
+
+ carry = pad[0] & 0x80;
+ for (i = 0; i < AES_BLOCK_SIZE - 1; i++)
+ pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+ pad[AES_BLOCK_SIZE - 1] <<= 1;
+ if (carry)
+ pad[AES_BLOCK_SIZE - 1] ^= 0x87;
+}
+
+
+/**
+ * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128
+ * @key: 128-bit key for the hash operation
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ void *ctx;
+ u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];
+ const u8 *pos, *end;
+ size_t i, e, left, total_len;
+
+ ctx = aes_encrypt_init(key, 16);
+ if (ctx == NULL)
+ return -1;
+ os_memset(cbc, 0, AES_BLOCK_SIZE);
+
+ total_len = 0;
+ for (e = 0; e < num_elem; e++)
+ total_len += len[e];
+ left = total_len;
+
+ e = 0;
+ pos = addr[0];
+ end = pos + len[0];
+
+ while (left >= AES_BLOCK_SIZE) {
+ for (i = 0; i < AES_BLOCK_SIZE; i++) {
+ cbc[i] ^= *pos++;
+ if (pos >= end) {
+ e++;
+ pos = addr[e];
+ end = pos + len[e];
+ }
+ }
+ if (left > AES_BLOCK_SIZE)
+ aes_encrypt(ctx, cbc, cbc);
+ left -= AES_BLOCK_SIZE;
+ }
+
+ os_memset(pad, 0, AES_BLOCK_SIZE);
+ aes_encrypt(ctx, pad, pad);
+ gf_mulx(pad);
+
+ if (left || total_len == 0) {
+ for (i = 0; i < left; i++) {
+ cbc[i] ^= *pos++;
+ if (pos >= end) {
+ e++;
+ pos = addr[e];
+ end = pos + len[e];
+ }
+ }
+ cbc[left] ^= 0x80;
+ gf_mulx(pad);
+ }
+
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
+ pad[i] ^= cbc[i];
+ aes_encrypt(ctx, pad, mac);
+ aes_encrypt_deinit(ctx);
+ return 0;
+}
+
+
+/**
+ * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 (aka AES-CMAC)
+ * @key: 128-bit key for the hash operation
+ * @data: Data buffer for which a MAC is determined
+ * @data_len: Length of data buffer in bytes
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
diff --git a/src/crypto/aes-unwrap.c b/src/crypto/aes-unwrap.c
new file mode 100644
index 0000000..f233ffa
--- /dev/null
+++ b/src/crypto/aes-unwrap.c
@@ -0,0 +1,79 @@
+/*
+ * AES key unwrap (128-bit KEK, RFC3394)
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
+ * @kek: Key encryption key (KEK)
+ * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16
+ * bytes
+ * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits
+ * @plain: Plaintext key, n * 64 bits
+ * Returns: 0 on success, -1 on failure (e.g., integrity verification failed)
+ */
+int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain)
+{
+ u8 a[8], *r, b[16];
+ int i, j;
+ void *ctx;
+
+ /* 1) Initialize variables. */
+ os_memcpy(a, cipher, 8);
+ r = plain;
+ os_memcpy(r, cipher + 8, 8 * n);
+
+ ctx = aes_decrypt_init(kek, 16);
+ if (ctx == NULL)
+ return -1;
+
+ /* 2) Compute intermediate values.
+ * For j = 5 to 0
+ * For i = n to 1
+ * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
+ * A = MSB(64, B)
+ * R[i] = LSB(64, B)
+ */
+ for (j = 5; j >= 0; j--) {
+ r = plain + (n - 1) * 8;
+ for (i = n; i >= 1; i--) {
+ os_memcpy(b, a, 8);
+ b[7] ^= n * j + i;
+
+ os_memcpy(b + 8, r, 8);
+ aes_decrypt(ctx, b, b);
+ os_memcpy(a, b, 8);
+ os_memcpy(r, b + 8, 8);
+ r -= 8;
+ }
+ }
+ aes_decrypt_deinit(ctx);
+
+ /* 3) Output results.
+ *
+ * These are already in @plain due to the location of temporary
+ * variables. Just verify that the IV matches with the expected value.
+ */
+ for (i = 0; i < 8; i++) {
+ if (a[i] != 0xa6)
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/crypto/aes-wrap.c b/src/crypto/aes-wrap.c
new file mode 100644
index 0000000..28d0c89
--- /dev/null
+++ b/src/crypto/aes-wrap.c
@@ -0,0 +1,76 @@
+/*
+ * AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
+ * @kek: 16-octet Key encryption key (KEK)
+ * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16
+ * bytes
+ * @plain: Plaintext key to be wrapped, n * 64 bits
+ * @cipher: Wrapped key, (n + 1) * 64 bits
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher)
+{
+ u8 *a, *r, b[16];
+ int i, j;
+ void *ctx;
+
+ a = cipher;
+ r = cipher + 8;
+
+ /* 1) Initialize variables. */
+ os_memset(a, 0xa6, 8);
+ os_memcpy(r, plain, 8 * n);
+
+ ctx = aes_encrypt_init(kek, 16);
+ if (ctx == NULL)
+ return -1;
+
+ /* 2) Calculate intermediate values.
+ * For j = 0 to 5
+ * For i=1 to n
+ * B = AES(K, A | R[i])
+ * A = MSB(64, B) ^ t where t = (n*j)+i
+ * R[i] = LSB(64, B)
+ */
+ for (j = 0; j <= 5; j++) {
+ r = cipher + 8;
+ for (i = 1; i <= n; i++) {
+ os_memcpy(b, a, 8);
+ os_memcpy(b + 8, r, 8);
+ aes_encrypt(ctx, b, b);
+ os_memcpy(a, b, 8);
+ a[7] ^= n * j + i;
+ os_memcpy(r, b + 8, 8);
+ r += 8;
+ }
+ }
+ aes_encrypt_deinit(ctx);
+
+ /* 3) Output the results.
+ *
+ * These are already in @cipher due to the location of temporary
+ * variables.
+ */
+
+ return 0;
+}
diff --git a/src/crypto/aes.h b/src/crypto/aes.h
new file mode 100644
index 0000000..ba384a9
--- /dev/null
+++ b/src/crypto/aes.h
@@ -0,0 +1,27 @@
+/*
+ * AES functions
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef AES_H
+#define AES_H
+
+#define AES_BLOCK_SIZE 16
+
+void * aes_encrypt_init(const u8 *key, size_t len);
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+void aes_encrypt_deinit(void *ctx);
+void * aes_decrypt_init(const u8 *key, size_t len);
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+void aes_decrypt_deinit(void *ctx);
+
+#endif /* AES_H */
diff --git a/src/crypto/aes_i.h b/src/crypto/aes_i.h
new file mode 100644
index 0000000..6b40bc7
--- /dev/null
+++ b/src/crypto/aes_i.h
@@ -0,0 +1,122 @@
+/*
+ * AES (Rijndael) cipher
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef AES_I_H
+#define AES_I_H
+
+#include "aes.h"
+
+/* #define FULL_UNROLL */
+#define AES_SMALL_TABLES
+
+extern const u32 Te0[256];
+extern const u32 Te1[256];
+extern const u32 Te2[256];
+extern const u32 Te3[256];
+extern const u32 Te4[256];
+extern const u32 Td0[256];
+extern const u32 Td1[256];
+extern const u32 Td2[256];
+extern const u32 Td3[256];
+extern const u32 Td4[256];
+extern const u32 rcon[10];
+extern const u8 Td4s[256];
+extern const u8 rcons[10];
+
+#ifndef AES_SMALL_TABLES
+
+#define RCON(i) rcon[(i)]
+
+#define TE0(i) Te0[((i) >> 24) & 0xff]
+#define TE1(i) Te1[((i) >> 16) & 0xff]
+#define TE2(i) Te2[((i) >> 8) & 0xff]
+#define TE3(i) Te3[(i) & 0xff]
+#define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000)
+#define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE44(i) (Te4[(i) & 0xff] & 0x000000ff)
+#define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000)
+#define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000)
+#define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00)
+#define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff)
+#define TE4(i) (Te4[(i)] & 0x000000ff)
+
+#define TD0(i) Td0[((i) >> 24) & 0xff]
+#define TD1(i) Td1[((i) >> 16) & 0xff]
+#define TD2(i) Td2[((i) >> 8) & 0xff]
+#define TD3(i) Td3[(i) & 0xff]
+#define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000)
+#define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TD44(i) (Td4[(i) & 0xff] & 0x000000ff)
+#define TD0_(i) Td0[(i) & 0xff]
+#define TD1_(i) Td1[(i) & 0xff]
+#define TD2_(i) Td2[(i) & 0xff]
+#define TD3_(i) Td3[(i) & 0xff]
+
+#else /* AES_SMALL_TABLES */
+
+#define RCON(i) (rcons[(i)] << 24)
+
+static inline u32 rotr(u32 val, int bits)
+{
+ return (val >> bits) | (val << (32 - bits));
+}
+
+#define TE0(i) Te0[((i) >> 24) & 0xff]
+#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8)
+#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16)
+#define TE3(i) rotr(Te0[(i) & 0xff], 24)
+#define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000)
+#define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff)
+#define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000)
+#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000)
+#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00)
+#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff)
+#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff)
+
+#define TD0(i) Td0[((i) >> 24) & 0xff]
+#define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8)
+#define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16)
+#define TD3(i) rotr(Td0[(i) & 0xff], 24)
+#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24)
+#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16)
+#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8)
+#define TD44(i) (Td4s[(i) & 0xff])
+#define TD0_(i) Td0[(i) & 0xff]
+#define TD1_(i) rotr(Td0[(i) & 0xff], 8)
+#define TD2_(i) rotr(Td0[(i) & 0xff], 16)
+#define TD3_(i) rotr(Td0[(i) & 0xff], 24)
+
+#endif /* AES_SMALL_TABLES */
+
+#ifdef _MSC_VER
+#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+#define GETU32(p) SWAP(*((u32 *)(p)))
+#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }
+#else
+#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \
+((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+#define PUTU32(ct, st) { \
+(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \
+(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
+#endif
+
+#define AES_PRIV_SIZE (4 * 44)
+
+void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]);
+
+#endif /* AES_I_H */
diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h
new file mode 100644
index 0000000..4b1c7b0
--- /dev/null
+++ b/src/crypto/aes_wrap.h
@@ -0,0 +1,48 @@
+/*
+ * AES-based functions
+ *
+ * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
+ * - One-Key CBC MAC (OMAC1) hash with AES-128
+ * - AES-128 CTR mode encryption
+ * - AES-128 EAX mode encryption/decryption
+ * - AES-128 CBC
+ *
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef AES_WRAP_H
+#define AES_WRAP_H
+
+int __must_check aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher);
+int __must_check aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain);
+int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem,
+ const u8 *addr[], const size_t *len,
+ u8 *mac);
+int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len,
+ u8 *mac);
+int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
+int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+ u8 *data, size_t data_len);
+int __must_check aes_128_eax_encrypt(const u8 *key,
+ const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, u8 *tag);
+int __must_check aes_128_eax_decrypt(const u8 *key,
+ const u8 *nonce, size_t nonce_len,
+ const u8 *hdr, size_t hdr_len,
+ u8 *data, size_t data_len, const u8 *tag);
+int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data,
+ size_t data_len);
+int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data,
+ size_t data_len);
+
+#endif /* AES_WRAP_H */
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
new file mode 100644
index 0000000..587b5a9
--- /dev/null
+++ b/src/crypto/crypto.h
@@ -0,0 +1,469 @@
+/*
+ * WPA Supplicant / wrapper functions for crypto libraries
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file defines the cryptographic functions that need to be implemented
+ * for wpa_supplicant and hostapd. When TLS is not used, internal
+ * implementation of MD5, SHA1, and AES is used and no external libraries are
+ * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the
+ * crypto library used by the TLS implementation is expected to be used for
+ * non-TLS needs, too, in order to save space by not implementing these
+ * functions twice.
+ *
+ * Wrapper code for using each crypto library is in its own file (crypto*.c)
+ * and one of these files is build and linked in to provide the functions
+ * defined here.
+ */
+
+#ifndef CRYPTO_H
+#define CRYPTO_H
+
+/**
+ * md4_vector - MD4 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+#ifdef CONFIG_FIPS
+/**
+ * md5_vector_non_fips_allow - MD5 hash for data vector (non-FIPS use allowed)
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[],
+ const size_t *len, u8 *mac);
+#else /* CONFIG_FIPS */
+#define md5_vector_non_fips_allow md5_vector
+#endif /* CONFIG_FIPS */
+
+
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+/**
+ * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF
+ * @seed: Seed/key for the PRF
+ * @seed_len: Seed length in bytes
+ * @x: Buffer for PRF output
+ * @xlen: Output length in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function implements random number generation specified in NIST FIPS
+ * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to
+ * SHA-1, but has different message padding.
+ */
+int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x,
+ size_t xlen);
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+/**
+ * des_encrypt - Encrypt one block with DES
+ * @clear: 8 octets (in)
+ * @key: 7 octets (in) (no parity bits included)
+ * @cypher: 8 octets (out)
+ */
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+
+/**
+ * aes_encrypt_init - Initialize AES for encryption
+ * @key: Encryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_encrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_encrypt - Encrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @plain: Plaintext data to be encrypted (16 bytes)
+ * @crypt: Buffer for the encrypted data (16 bytes)
+ */
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+
+/**
+ * aes_encrypt_deinit - Deinitialize AES encryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_encrypt_deinit(void *ctx);
+
+/**
+ * aes_decrypt_init - Initialize AES for decryption
+ * @key: Decryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_decrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_decrypt - Decrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @crypt: Encrypted data (16 bytes)
+ * @plain: Buffer for the decrypted data (16 bytes)
+ */
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+
+/**
+ * aes_decrypt_deinit - Deinitialize AES decryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_decrypt_deinit(void *ctx);
+
+
+enum crypto_hash_alg {
+ CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
+ CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1
+};
+
+struct crypto_hash;
+
+/**
+ * crypto_hash_init - Initialize hash/HMAC function
+ * @alg: Hash algorithm
+ * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed
+ * @key_len: Length of the key in bytes
+ * Returns: Pointer to hash context to use with other hash functions or %NULL
+ * on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len);
+
+/**
+ * crypto_hash_update - Add data to hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @data: Data buffer to add
+ * @len: Length of the buffer
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len);
+
+/**
+ * crypto_hash_finish - Complete hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @hash: Buffer for hash value or %NULL if caller is just freeing the hash
+ * context
+ * @len: Pointer to length of the buffer or %NULL if caller is just freeing the
+ * hash context; on return, this is set to the actual length of the hash value
+ * Returns: 0 on success, -1 if buffer is too small (len set to needed length),
+ * or -2 on other failures (including failed crypto_hash_update() operations)
+ *
+ * This function calculates the hash value and frees the context buffer that
+ * was used for hash calculation.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len);
+
+
+enum crypto_cipher_alg {
+ CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES,
+ CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4
+};
+
+struct crypto_cipher;
+
+/**
+ * crypto_cipher_init - Initialize block/stream cipher function
+ * @alg: Cipher algorithm
+ * @iv: Initialization vector for block ciphers or %NULL for stream ciphers
+ * @key: Cipher key
+ * @key_len: Length of key in bytes
+ * Returns: Pointer to cipher context to use with other cipher functions or
+ * %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len);
+
+/**
+ * crypto_cipher_encrypt - Cipher encrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @plain: Plaintext to cipher
+ * @crypt: Resulting ciphertext
+ * @len: Length of the plaintext
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx,
+ const u8 *plain, u8 *crypt, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Cipher decrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @crypt: Ciphertext to decrypt
+ * @plain: Resulting plaintext
+ * @len: Length of the cipher text
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx,
+ const u8 *crypt, u8 *plain, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Free cipher context
+ * @ctx: Context pointer from crypto_cipher_init()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_cipher_deinit(struct crypto_cipher *ctx);
+
+
+struct crypto_public_key;
+struct crypto_private_key;
+
+/**
+ * crypto_public_key_import - Import an RSA public key
+ * @key: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library supports X.509
+ * parsing. In that case, crypto_public_key_from_cert() is used to import the
+ * public key from a certificate.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len);
+
+/**
+ * crypto_private_key_import - Import an RSA private key
+ * @key: Key buffer (DER encoded RSA private key)
+ * @len: Key buffer length in bytes
+ * @passwd: Key encryption password or %NULL if key is not encrypted
+ * Returns: Pointer to the private key or %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+ size_t len,
+ const char *passwd);
+
+/**
+ * crypto_public_key_from_cert - Import an RSA public key from a certificate
+ * @buf: DER encoded X.509 certificate
+ * @len: Certificate buffer length in bytes
+ * Returns: Pointer to public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library does not support
+ * X.509 parsing. In that case, internal code will be used to parse the
+ * certificate and public key is imported using crypto_public_key_import().
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+ size_t len);
+
+/**
+ * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5)
+ * @key: Public key
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_public_key_encrypt_pkcs1_v15(
+ struct crypto_public_key *key, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5)
+ * @key: Private key
+ * @in: Encrypted buffer
+ * @inlen: Length of encrypted buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_decrypt_pkcs1_v15(
+ struct crypto_private_key *key, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1)
+ * @key: Private key from crypto_private_key_import()
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted (signed) data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_public_key_free - Free public key
+ * @key: Public key
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_public_key_free(struct crypto_public_key *key);
+
+/**
+ * crypto_private_key_free - Free private key
+ * @key: Private key from crypto_private_key_import()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_private_key_free(struct crypto_private_key *key);
+
+/**
+ * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature
+ * @key: Public key
+ * @crypt: Encrypted signature data (using the private key)
+ * @crypt_len: Encrypted signature data length
+ * @plain: Buffer for plaintext (at least crypt_len bytes)
+ * @plain_len: Plaintext length (max buffer size on input, real len on output);
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check crypto_public_key_decrypt_pkcs1(
+ struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
+ u8 *plain, size_t *plain_len);
+
+/**
+ * crypto_global_init - Initialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_global_init(void);
+
+/**
+ * crypto_global_deinit - Deinitialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_global_deinit(void);
+
+/**
+ * crypto_mod_exp - Modular exponentiation of large integers
+ * @base: Base integer (big endian byte array)
+ * @base_len: Length of base integer in bytes
+ * @power: Power integer (big endian byte array)
+ * @power_len: Length of power integer in bytes
+ * @modulus: Modulus integer (big endian byte array)
+ * @modulus_len: Length of modulus integer in bytes
+ * @result: Buffer for the result
+ * @result_len: Result length (max buffer size on input, real len on output)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function calculates result = base ^ power mod modulus. modules_len is
+ * used as the maximum size of modulus buffer. It is set to the used size on
+ * success.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len);
+
+/**
+ * rc4_skip - XOR RC4 stream to given data with skip-stream-start
+ * @key: RC4 key
+ * @keylen: RC4 key length
+ * @skip: number of bytes to skip from the beginning of the RC4 stream
+ * @data: data to be XOR'ed with RC4 stream
+ * @data_len: buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Generate RC4 pseudo random stream for the given key, skip beginning of the
+ * stream, and XOR the end result with the data buffer to perform RC4
+ * encryption/decryption.
+ */
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+ u8 *data, size_t data_len);
+
+#endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_cryptoapi.c b/src/crypto/crypto_cryptoapi.c
new file mode 100644
index 0000000..2a8d200
--- /dev/null
+++ b/src/crypto/crypto_cryptoapi.c
@@ -0,0 +1,789 @@
+/*
+ * Crypto wrapper for Microsoft CryptoAPI
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <windows.h>
+#include <wincrypt.h>
+
+#include "common.h"
+#include "crypto.h"
+
+#ifndef MS_ENH_RSA_AES_PROV
+#ifdef UNICODE
+#define MS_ENH_RSA_AES_PROV \
+L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
+#else
+#define MS_ENH_RSA_AES_PROV \
+"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
+#endif
+#endif /* MS_ENH_RSA_AES_PROV */
+
+#ifndef CALG_HMAC
+#define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC)
+#endif
+
+#ifdef __MINGW32_VERSION
+/*
+ * MinGW does not yet include all the needed definitions for CryptoAPI, so
+ * define here whatever extra is needed.
+ */
+
+static BOOL WINAPI
+(*CryptImportPublicKeyInfo)(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType,
+ PCERT_PUBLIC_KEY_INFO pInfo, HCRYPTKEY *phKey)
+= NULL; /* to be loaded from crypt32.dll */
+
+
+static int mingw_load_crypto_func(void)
+{
+ HINSTANCE dll;
+
+ /* MinGW does not yet have full CryptoAPI support, so load the needed
+ * function here. */
+
+ if (CryptImportPublicKeyInfo)
+ return 0;
+
+ dll = LoadLibrary("crypt32");
+ if (dll == NULL) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 "
+ "library");
+ return -1;
+ }
+
+ CryptImportPublicKeyInfo = GetProcAddress(
+ dll, "CryptImportPublicKeyInfo");
+ if (CryptImportPublicKeyInfo == NULL) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get "
+ "CryptImportPublicKeyInfo() address from "
+ "crypt32 library");
+ return -1;
+ }
+
+ return 0;
+}
+
+#else /* __MINGW32_VERSION */
+
+static int mingw_load_crypto_func(void)
+{
+ return 0;
+}
+
+#endif /* __MINGW32_VERSION */
+
+
+static void cryptoapi_report_error(const char *msg)
+{
+ char *s, *pos;
+ DWORD err = GetLastError();
+
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, err, 0, (LPTSTR) &s, 0, NULL) == 0) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d", msg, (int) err);
+ }
+
+ pos = s;
+ while (*pos) {
+ if (*pos == '\n' || *pos == '\r') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+
+ wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d: (%s)", msg, (int) err, s);
+ LocalFree(s);
+}
+
+
+int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ HCRYPTPROV prov;
+ HCRYPTHASH hash;
+ size_t i;
+ DWORD hlen;
+ int ret = 0;
+
+ if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, 0)) {
+ cryptoapi_report_error("CryptAcquireContext");
+ return -1;
+ }
+
+ if (!CryptCreateHash(prov, alg, 0, 0, &hash)) {
+ cryptoapi_report_error("CryptCreateHash");
+ CryptReleaseContext(prov, 0);
+ return -1;
+ }
+
+ for (i = 0; i < num_elem; i++) {
+ if (!CryptHashData(hash, (BYTE *) addr[i], len[i], 0)) {
+ cryptoapi_report_error("CryptHashData");
+ CryptDestroyHash(hash);
+ CryptReleaseContext(prov, 0);
+ }
+ }
+
+ hlen = hash_len;
+ if (!CryptGetHashParam(hash, HP_HASHVAL, mac, &hlen, 0)) {
+ cryptoapi_report_error("CryptGetHashParam");
+ ret = -1;
+ }
+
+ CryptDestroyHash(hash);
+ CryptReleaseContext(prov, 0);
+
+ return ret;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac);
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ u8 next, tmp;
+ int i;
+ HCRYPTPROV prov;
+ HCRYPTKEY ckey;
+ DWORD dlen;
+ struct {
+ BLOBHEADER hdr;
+ DWORD len;
+ BYTE key[8];
+ } key_blob;
+ DWORD mode = CRYPT_MODE_ECB;
+
+ key_blob.hdr.bType = PLAINTEXTKEYBLOB;
+ key_blob.hdr.bVersion = CUR_BLOB_VERSION;
+ key_blob.hdr.reserved = 0;
+ key_blob.hdr.aiKeyAlg = CALG_DES;
+ key_blob.len = 8;
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ key_blob.key[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ key_blob.key[i] = next | 1;
+
+ if (!CryptAcquireContext(&prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT)) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: "
+ "%d", (int) GetLastError());
+ return;
+ }
+
+ if (!CryptImportKey(prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0,
+ &ckey)) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d",
+ (int) GetLastError());
+ CryptReleaseContext(prov, 0);
+ return;
+ }
+
+ if (!CryptSetKeyParam(ckey, KP_MODE, (BYTE *) &mode, 0)) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) "
+ "failed: %d", (int) GetLastError());
+ CryptDestroyKey(ckey);
+ CryptReleaseContext(prov, 0);
+ return;
+ }
+
+ os_memcpy(cypher, clear, 8);
+ dlen = 8;
+ if (!CryptEncrypt(ckey, 0, FALSE, 0, cypher, &dlen, 8)) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d",
+ (int) GetLastError());
+ os_memset(cypher, 0, 8);
+ }
+
+ CryptDestroyKey(ckey);
+ CryptReleaseContext(prov, 0);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac);
+}
+
+
+struct aes_context {
+ HCRYPTPROV prov;
+ HCRYPTKEY ckey;
+};
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ struct aes_context *akey;
+ struct {
+ BLOBHEADER hdr;
+ DWORD len;
+ BYTE key[16];
+ } key_blob;
+ DWORD mode = CRYPT_MODE_ECB;
+
+ if (len != 16)
+ return NULL;
+
+ key_blob.hdr.bType = PLAINTEXTKEYBLOB;
+ key_blob.hdr.bVersion = CUR_BLOB_VERSION;
+ key_blob.hdr.reserved = 0;
+ key_blob.hdr.aiKeyAlg = CALG_AES_128;
+ key_blob.len = len;
+ os_memcpy(key_blob.key, key, len);
+
+ akey = os_zalloc(sizeof(*akey));
+ if (akey == NULL)
+ return NULL;
+
+ if (!CryptAcquireContext(&akey->prov, NULL,
+ MS_ENH_RSA_AES_PROV, PROV_RSA_AES,
+ CRYPT_VERIFYCONTEXT)) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: "
+ "%d", (int) GetLastError());
+ os_free(akey);
+ return NULL;
+ }
+
+ if (!CryptImportKey(akey->prov, (BYTE *) &key_blob, sizeof(key_blob),
+ 0, 0, &akey->ckey)) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d",
+ (int) GetLastError());
+ CryptReleaseContext(akey->prov, 0);
+ os_free(akey);
+ return NULL;
+ }
+
+ if (!CryptSetKeyParam(akey->ckey, KP_MODE, (BYTE *) &mode, 0)) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) "
+ "failed: %d", (int) GetLastError());
+ CryptDestroyKey(akey->ckey);
+ CryptReleaseContext(akey->prov, 0);
+ os_free(akey);
+ return NULL;
+ }
+
+ return akey;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+ struct aes_context *akey = ctx;
+ DWORD dlen;
+
+ os_memcpy(crypt, plain, 16);
+ dlen = 16;
+ if (!CryptEncrypt(akey->ckey, 0, FALSE, 0, crypt, &dlen, 16)) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d",
+ (int) GetLastError());
+ os_memset(crypt, 0, 16);
+ }
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+ struct aes_context *akey = ctx;
+ if (akey) {
+ CryptDestroyKey(akey->ckey);
+ CryptReleaseContext(akey->prov, 0);
+ os_free(akey);
+ }
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+ return aes_encrypt_init(key, len);
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+ struct aes_context *akey = ctx;
+ DWORD dlen;
+
+ os_memcpy(plain, crypt, 16);
+ dlen = 16;
+
+ if (!CryptDecrypt(akey->ckey, 0, FALSE, 0, plain, &dlen)) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: CryptDecrypt failed: %d",
+ (int) GetLastError());
+ }
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+ aes_encrypt_deinit(ctx);
+}
+
+
+struct crypto_hash {
+ enum crypto_hash_alg alg;
+ int error;
+ HCRYPTPROV prov;
+ HCRYPTHASH hash;
+ HCRYPTKEY key;
+};
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_hash *ctx;
+ ALG_ID calg;
+ struct {
+ BLOBHEADER hdr;
+ DWORD len;
+ BYTE key[32];
+ } key_blob;
+
+ os_memset(&key_blob, 0, sizeof(key_blob));
+ switch (alg) {
+ case CRYPTO_HASH_ALG_MD5:
+ calg = CALG_MD5;
+ break;
+ case CRYPTO_HASH_ALG_SHA1:
+ calg = CALG_SHA;
+ break;
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ calg = CALG_HMAC;
+ key_blob.hdr.bType = PLAINTEXTKEYBLOB;
+ key_blob.hdr.bVersion = CUR_BLOB_VERSION;
+ key_blob.hdr.reserved = 0;
+ /*
+ * Note: RC2 is not really used, but that can be used to
+ * import HMAC keys of up to 16 byte long.
+ * CRYPT_IPSEC_HMAC_KEY flag for CryptImportKey() is needed to
+ * be able to import longer keys (HMAC-SHA1 uses 20-byte key).
+ */
+ key_blob.hdr.aiKeyAlg = CALG_RC2;
+ key_blob.len = key_len;
+ if (key_len > sizeof(key_blob.key))
+ return NULL;
+ os_memcpy(key_blob.key, key, key_len);
+ break;
+ default:
+ return NULL;
+ }
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->alg = alg;
+
+ if (!CryptAcquireContext(&ctx->prov, NULL, NULL, PROV_RSA_FULL, 0)) {
+ cryptoapi_report_error("CryptAcquireContext");
+ os_free(ctx);
+ return NULL;
+ }
+
+ if (calg == CALG_HMAC) {
+#ifndef CRYPT_IPSEC_HMAC_KEY
+#define CRYPT_IPSEC_HMAC_KEY 0x00000100
+#endif
+ if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob,
+ sizeof(key_blob), 0, CRYPT_IPSEC_HMAC_KEY,
+ &ctx->key)) {
+ cryptoapi_report_error("CryptImportKey");
+ CryptReleaseContext(ctx->prov, 0);
+ os_free(ctx);
+ return NULL;
+ }
+ }
+
+ if (!CryptCreateHash(ctx->prov, calg, ctx->key, 0, &ctx->hash)) {
+ cryptoapi_report_error("CryptCreateHash");
+ CryptReleaseContext(ctx->prov, 0);
+ os_free(ctx);
+ return NULL;
+ }
+
+ if (calg == CALG_HMAC) {
+ HMAC_INFO info;
+ os_memset(&info, 0, sizeof(info));
+ switch (alg) {
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ info.HashAlgid = CALG_MD5;
+ break;
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ info.HashAlgid = CALG_SHA;
+ break;
+ default:
+ /* unreachable */
+ break;
+ }
+
+ if (!CryptSetHashParam(ctx->hash, HP_HMAC_INFO, (BYTE *) &info,
+ 0)) {
+ cryptoapi_report_error("CryptSetHashParam");
+ CryptDestroyHash(ctx->hash);
+ CryptReleaseContext(ctx->prov, 0);
+ os_free(ctx);
+ return NULL;
+ }
+ }
+
+ return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+ if (ctx == NULL || ctx->error)
+ return;
+
+ if (!CryptHashData(ctx->hash, (BYTE *) data, len, 0)) {
+ cryptoapi_report_error("CryptHashData");
+ ctx->error = 1;
+ }
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+ int ret = 0;
+ DWORD hlen;
+
+ if (ctx == NULL)
+ return -2;
+
+ if (mac == NULL || len == NULL)
+ goto done;
+
+ if (ctx->error) {
+ ret = -2;
+ goto done;
+ }
+
+ hlen = *len;
+ if (!CryptGetHashParam(ctx->hash, HP_HASHVAL, mac, &hlen, 0)) {
+ cryptoapi_report_error("CryptGetHashParam");
+ ret = -2;
+ }
+ *len = hlen;
+
+done:
+ if (ctx->alg == CRYPTO_HASH_ALG_HMAC_SHA1 ||
+ ctx->alg == CRYPTO_HASH_ALG_HMAC_MD5)
+ CryptDestroyKey(ctx->key);
+
+ os_free(ctx);
+
+ return ret;
+}
+
+
+struct crypto_cipher {
+ HCRYPTPROV prov;
+ HCRYPTKEY key;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_cipher *ctx;
+ struct {
+ BLOBHEADER hdr;
+ DWORD len;
+ BYTE key[32];
+ } key_blob;
+ DWORD mode = CRYPT_MODE_CBC;
+
+ key_blob.hdr.bType = PLAINTEXTKEYBLOB;
+ key_blob.hdr.bVersion = CUR_BLOB_VERSION;
+ key_blob.hdr.reserved = 0;
+ key_blob.len = key_len;
+ if (key_len > sizeof(key_blob.key))
+ return NULL;
+ os_memcpy(key_blob.key, key, key_len);
+
+ switch (alg) {
+ case CRYPTO_CIPHER_ALG_AES:
+ if (key_len == 32)
+ key_blob.hdr.aiKeyAlg = CALG_AES_256;
+ else if (key_len == 24)
+ key_blob.hdr.aiKeyAlg = CALG_AES_192;
+ else
+ key_blob.hdr.aiKeyAlg = CALG_AES_128;
+ break;
+ case CRYPTO_CIPHER_ALG_3DES:
+ key_blob.hdr.aiKeyAlg = CALG_3DES;
+ break;
+ case CRYPTO_CIPHER_ALG_DES:
+ key_blob.hdr.aiKeyAlg = CALG_DES;
+ break;
+ case CRYPTO_CIPHER_ALG_RC2:
+ key_blob.hdr.aiKeyAlg = CALG_RC2;
+ break;
+ case CRYPTO_CIPHER_ALG_RC4:
+ key_blob.hdr.aiKeyAlg = CALG_RC4;
+ break;
+ default:
+ return NULL;
+ }
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ if (!CryptAcquireContext(&ctx->prov, NULL, MS_ENH_RSA_AES_PROV,
+ PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
+ cryptoapi_report_error("CryptAcquireContext");
+ goto fail1;
+ }
+
+ if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob,
+ sizeof(key_blob), 0, 0, &ctx->key)) {
+ cryptoapi_report_error("CryptImportKey");
+ goto fail2;
+ }
+
+ if (!CryptSetKeyParam(ctx->key, KP_MODE, (BYTE *) &mode, 0)) {
+ cryptoapi_report_error("CryptSetKeyParam(KP_MODE)");
+ goto fail3;
+ }
+
+ if (iv && !CryptSetKeyParam(ctx->key, KP_IV, (BYTE *) iv, 0)) {
+ cryptoapi_report_error("CryptSetKeyParam(KP_IV)");
+ goto fail3;
+ }
+
+ return ctx;
+
+fail3:
+ CryptDestroyKey(ctx->key);
+fail2:
+ CryptReleaseContext(ctx->prov, 0);
+fail1:
+ os_free(ctx);
+ return NULL;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+ u8 *crypt, size_t len)
+{
+ DWORD dlen;
+
+ os_memcpy(crypt, plain, len);
+ dlen = len;
+ if (!CryptEncrypt(ctx->key, 0, FALSE, 0, crypt, &dlen, len)) {
+ cryptoapi_report_error("CryptEncrypt");
+ os_memset(crypt, 0, len);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+ u8 *plain, size_t len)
+{
+ DWORD dlen;
+
+ os_memcpy(plain, crypt, len);
+ dlen = len;
+ if (!CryptDecrypt(ctx->key, 0, FALSE, 0, plain, &dlen)) {
+ cryptoapi_report_error("CryptDecrypt");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+ CryptDestroyKey(ctx->key);
+ CryptReleaseContext(ctx->prov, 0);
+ os_free(ctx);
+}
+
+
+struct crypto_public_key {
+ HCRYPTPROV prov;
+ HCRYPTKEY rsa;
+};
+
+struct crypto_private_key {
+ HCRYPTPROV prov;
+ HCRYPTKEY rsa;
+};
+
+
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
+{
+ /* Use crypto_public_key_from_cert() instead. */
+ return NULL;
+}
+
+
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+ size_t len,
+ const char *passwd)
+{
+ /* TODO */
+ return NULL;
+}
+
+
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+ size_t len)
+{
+ struct crypto_public_key *pk;
+ PCCERT_CONTEXT cc;
+
+ pk = os_zalloc(sizeof(*pk));
+ if (pk == NULL)
+ return NULL;
+
+ cc = CertCreateCertificateContext(X509_ASN_ENCODING |
+ PKCS_7_ASN_ENCODING, buf, len);
+ if (!cc) {
+ cryptoapi_report_error("CryptCreateCertificateContext");
+ os_free(pk);
+ return NULL;
+ }
+
+ if (!CryptAcquireContext(&pk->prov, NULL, MS_DEF_PROV, PROV_RSA_FULL,
+ 0)) {
+ cryptoapi_report_error("CryptAcquireContext");
+ os_free(pk);
+ CertFreeCertificateContext(cc);
+ return NULL;
+ }
+
+ if (!CryptImportPublicKeyInfo(pk->prov, X509_ASN_ENCODING |
+ PKCS_7_ASN_ENCODING,
+ &cc->pCertInfo->SubjectPublicKeyInfo,
+ &pk->rsa)) {
+ cryptoapi_report_error("CryptImportPublicKeyInfo");
+ CryptReleaseContext(pk->prov, 0);
+ os_free(pk);
+ CertFreeCertificateContext(cc);
+ return NULL;
+ }
+
+ CertFreeCertificateContext(cc);
+
+ return pk;
+}
+
+
+int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ DWORD clen;
+ u8 *tmp;
+ size_t i;
+
+ if (*outlen < inlen)
+ return -1;
+ tmp = malloc(*outlen);
+ if (tmp == NULL)
+ return -1;
+
+ os_memcpy(tmp, in, inlen);
+ clen = inlen;
+ if (!CryptEncrypt(key->rsa, 0, TRUE, 0, tmp, &clen, *outlen)) {
+ wpa_printf(MSG_DEBUG, "CryptoAPI: Failed to encrypt using "
+ "public key: %d", (int) GetLastError());
+ os_free(tmp);
+ return -1;
+ }
+
+ *outlen = clen;
+
+ /* Reverse the output */
+ for (i = 0; i < *outlen; i++)
+ out[i] = tmp[*outlen - 1 - i];
+
+ os_free(tmp);
+
+ return 0;
+}
+
+
+int crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ /* TODO */
+ return -1;
+}
+
+
+void crypto_public_key_free(struct crypto_public_key *key)
+{
+ if (key) {
+ CryptDestroyKey(key->rsa);
+ CryptReleaseContext(key->prov, 0);
+ os_free(key);
+ }
+}
+
+
+void crypto_private_key_free(struct crypto_private_key *key)
+{
+ if (key) {
+ CryptDestroyKey(key->rsa);
+ CryptReleaseContext(key->prov, 0);
+ os_free(key);
+ }
+}
+
+
+int crypto_global_init(void)
+{
+ return mingw_load_crypto_func();
+}
+
+
+void crypto_global_deinit(void)
+{
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len)
+{
+ /* TODO */
+ return -1;
+}
diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c
new file mode 100644
index 0000000..0998cca
--- /dev/null
+++ b/src/crypto/crypto_gnutls.c
@@ -0,0 +1,305 @@
+/*
+ * WPA Supplicant / wrapper functions for libgcrypt
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <gcrypt.h>
+
+#include "common.h"
+#include "crypto.h"
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ gcry_md_hd_t hd;
+ unsigned char *p;
+ size_t i;
+
+ if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR)
+ return -1;
+ for (i = 0; i < num_elem; i++)
+ gcry_md_write(hd, addr[i], len[i]);
+ p = gcry_md_read(hd, GCRY_MD_MD4);
+ if (p)
+ memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4));
+ gcry_md_close(hd);
+ return 0;
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ gcry_cipher_hd_t hd;
+ u8 pkey[8], next, tmp;
+ int i;
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ pkey[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ pkey[i] = next | 1;
+
+ gcry_cipher_open(&hd, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
+ gcry_err_code(gcry_cipher_setkey(hd, pkey, 8));
+ gcry_cipher_encrypt(hd, cypher, 8, clear, 8);
+ gcry_cipher_close(hd);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ gcry_md_hd_t hd;
+ unsigned char *p;
+ size_t i;
+
+ if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR)
+ return -1;
+ for (i = 0; i < num_elem; i++)
+ gcry_md_write(hd, addr[i], len[i]);
+ p = gcry_md_read(hd, GCRY_MD_MD5);
+ if (p)
+ memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5));
+ gcry_md_close(hd);
+ return 0;
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ gcry_md_hd_t hd;
+ unsigned char *p;
+ size_t i;
+
+ if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR)
+ return -1;
+ for (i = 0; i < num_elem; i++)
+ gcry_md_write(hd, addr[i], len[i]);
+ p = gcry_md_read(hd, GCRY_MD_SHA1);
+ if (p)
+ memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1));
+ gcry_md_close(hd);
+ return 0;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ gcry_cipher_hd_t hd;
+
+ if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
+ GPG_ERR_NO_ERROR) {
+ printf("cipher open failed\n");
+ return NULL;
+ }
+ if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) {
+ printf("setkey failed\n");
+ gcry_cipher_close(hd);
+ return NULL;
+ }
+
+ return hd;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+ gcry_cipher_hd_t hd = ctx;
+ gcry_cipher_encrypt(hd, crypt, 16, plain, 16);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+ gcry_cipher_hd_t hd = ctx;
+ gcry_cipher_close(hd);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+ gcry_cipher_hd_t hd;
+
+ if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
+ GPG_ERR_NO_ERROR)
+ return NULL;
+ if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) {
+ gcry_cipher_close(hd);
+ return NULL;
+ }
+
+ return hd;
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+ gcry_cipher_hd_t hd = ctx;
+ gcry_cipher_decrypt(hd, plain, 16, crypt, 16);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+ gcry_cipher_hd_t hd = ctx;
+ gcry_cipher_close(hd);
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len)
+{
+ gcry_mpi_t bn_base = NULL, bn_exp = NULL, bn_modulus = NULL,
+ bn_result = NULL;
+ int ret = -1;
+
+ if (gcry_mpi_scan(&bn_base, GCRYMPI_FMT_USG, base, base_len, NULL) !=
+ GPG_ERR_NO_ERROR ||
+ gcry_mpi_scan(&bn_exp, GCRYMPI_FMT_USG, power, power_len, NULL) !=
+ GPG_ERR_NO_ERROR ||
+ gcry_mpi_scan(&bn_modulus, GCRYMPI_FMT_USG, modulus, modulus_len,
+ NULL) != GPG_ERR_NO_ERROR)
+ goto error;
+ bn_result = gcry_mpi_new(modulus_len * 8);
+
+ gcry_mpi_powm(bn_result, bn_base, bn_exp, bn_modulus);
+
+ if (gcry_mpi_print(GCRYMPI_FMT_USG, result, *result_len, result_len,
+ bn_result) != GPG_ERR_NO_ERROR)
+ goto error;
+
+ ret = 0;
+
+error:
+ gcry_mpi_release(bn_base);
+ gcry_mpi_release(bn_exp);
+ gcry_mpi_release(bn_modulus);
+ gcry_mpi_release(bn_result);
+ return ret;
+}
+
+
+struct crypto_cipher {
+ gcry_cipher_hd_t enc;
+ gcry_cipher_hd_t dec;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_cipher *ctx;
+ gcry_error_t res;
+ enum gcry_cipher_algos a;
+ int ivlen;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ switch (alg) {
+ case CRYPTO_CIPHER_ALG_RC4:
+ a = GCRY_CIPHER_ARCFOUR;
+ res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_STREAM,
+ 0);
+ gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_STREAM, 0);
+ break;
+ case CRYPTO_CIPHER_ALG_AES:
+ if (key_len == 24)
+ a = GCRY_CIPHER_AES192;
+ else if (key_len == 32)
+ a = GCRY_CIPHER_AES256;
+ else
+ a = GCRY_CIPHER_AES;
+ res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+ gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+ break;
+ case CRYPTO_CIPHER_ALG_3DES:
+ a = GCRY_CIPHER_3DES;
+ res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+ gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+ break;
+ case CRYPTO_CIPHER_ALG_DES:
+ a = GCRY_CIPHER_DES;
+ res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+ gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+ break;
+ case CRYPTO_CIPHER_ALG_RC2:
+ if (key_len == 5)
+ a = GCRY_CIPHER_RFC2268_40;
+ else
+ a = GCRY_CIPHER_RFC2268_128;
+ res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+ gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+ break;
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+
+ if (res != GPG_ERR_NO_ERROR) {
+ os_free(ctx);
+ return NULL;
+ }
+
+ if (gcry_cipher_setkey(ctx->enc, key, key_len) != GPG_ERR_NO_ERROR ||
+ gcry_cipher_setkey(ctx->dec, key, key_len) != GPG_ERR_NO_ERROR) {
+ gcry_cipher_close(ctx->enc);
+ gcry_cipher_close(ctx->dec);
+ os_free(ctx);
+ return NULL;
+ }
+
+ ivlen = gcry_cipher_get_algo_blklen(a);
+ if (gcry_cipher_setiv(ctx->enc, iv, ivlen) != GPG_ERR_NO_ERROR ||
+ gcry_cipher_setiv(ctx->dec, iv, ivlen) != GPG_ERR_NO_ERROR) {
+ gcry_cipher_close(ctx->enc);
+ gcry_cipher_close(ctx->dec);
+ os_free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+ u8 *crypt, size_t len)
+{
+ if (gcry_cipher_encrypt(ctx->enc, crypt, len, plain, len) !=
+ GPG_ERR_NO_ERROR)
+ return -1;
+ return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+ u8 *plain, size_t len)
+{
+ if (gcry_cipher_decrypt(ctx->dec, plain, len, crypt, len) !=
+ GPG_ERR_NO_ERROR)
+ return -1;
+ return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+ gcry_cipher_close(ctx->enc);
+ gcry_cipher_close(ctx->dec);
+ os_free(ctx);
+}
diff --git a/src/crypto/crypto_internal-cipher.c b/src/crypto/crypto_internal-cipher.c
new file mode 100644
index 0000000..75134f0
--- /dev/null
+++ b/src/crypto/crypto_internal-cipher.c
@@ -0,0 +1,256 @@
+/*
+ * Crypto wrapper for internal crypto implementation - Cipher wrappers
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "aes.h"
+#include "des_i.h"
+
+
+struct crypto_cipher {
+ enum crypto_cipher_alg alg;
+ union {
+ struct {
+ size_t used_bytes;
+ u8 key[16];
+ size_t keylen;
+ } rc4;
+ struct {
+ u8 cbc[32];
+ size_t block_size;
+ void *ctx_enc;
+ void *ctx_dec;
+ } aes;
+ struct {
+ struct des3_key_s key;
+ u8 cbc[8];
+ } des3;
+ struct {
+ u32 ek[32];
+ u32 dk[32];
+ u8 cbc[8];
+ } des;
+ } u;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_cipher *ctx;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->alg = alg;
+
+ switch (alg) {
+ case CRYPTO_CIPHER_ALG_RC4:
+ if (key_len > sizeof(ctx->u.rc4.key)) {
+ os_free(ctx);
+ return NULL;
+ }
+ ctx->u.rc4.keylen = key_len;
+ os_memcpy(ctx->u.rc4.key, key, key_len);
+ break;
+ case CRYPTO_CIPHER_ALG_AES:
+ if (key_len > sizeof(ctx->u.aes.cbc)) {
+ os_free(ctx);
+ return NULL;
+ }
+ ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len);
+ if (ctx->u.aes.ctx_enc == NULL) {
+ os_free(ctx);
+ return NULL;
+ }
+ ctx->u.aes.ctx_dec = aes_decrypt_init(key, key_len);
+ if (ctx->u.aes.ctx_dec == NULL) {
+ aes_encrypt_deinit(ctx->u.aes.ctx_enc);
+ os_free(ctx);
+ return NULL;
+ }
+ ctx->u.aes.block_size = key_len;
+ os_memcpy(ctx->u.aes.cbc, iv, ctx->u.aes.block_size);
+ break;
+ case CRYPTO_CIPHER_ALG_3DES:
+ if (key_len != 24) {
+ os_free(ctx);
+ return NULL;
+ }
+ des3_key_setup(key, &ctx->u.des3.key);
+ os_memcpy(ctx->u.des3.cbc, iv, 8);
+ break;
+ case CRYPTO_CIPHER_ALG_DES:
+ if (key_len != 8) {
+ os_free(ctx);
+ return NULL;
+ }
+ des_key_setup(key, ctx->u.des.ek, ctx->u.des.dk);
+ os_memcpy(ctx->u.des.cbc, iv, 8);
+ break;
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+ u8 *crypt, size_t len)
+{
+ size_t i, j, blocks;
+
+ switch (ctx->alg) {
+ case CRYPTO_CIPHER_ALG_RC4:
+ if (plain != crypt)
+ os_memcpy(crypt, plain, len);
+ rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+ ctx->u.rc4.used_bytes, crypt, len);
+ ctx->u.rc4.used_bytes += len;
+ break;
+ case CRYPTO_CIPHER_ALG_AES:
+ if (len % ctx->u.aes.block_size)
+ return -1;
+ blocks = len / ctx->u.aes.block_size;
+ for (i = 0; i < blocks; i++) {
+ for (j = 0; j < ctx->u.aes.block_size; j++)
+ ctx->u.aes.cbc[j] ^= plain[j];
+ aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc,
+ ctx->u.aes.cbc);
+ os_memcpy(crypt, ctx->u.aes.cbc,
+ ctx->u.aes.block_size);
+ plain += ctx->u.aes.block_size;
+ crypt += ctx->u.aes.block_size;
+ }
+ break;
+ case CRYPTO_CIPHER_ALG_3DES:
+ if (len % 8)
+ return -1;
+ blocks = len / 8;
+ for (i = 0; i < blocks; i++) {
+ for (j = 0; j < 8; j++)
+ ctx->u.des3.cbc[j] ^= plain[j];
+ des3_encrypt(ctx->u.des3.cbc, &ctx->u.des3.key,
+ ctx->u.des3.cbc);
+ os_memcpy(crypt, ctx->u.des3.cbc, 8);
+ plain += 8;
+ crypt += 8;
+ }
+ break;
+ case CRYPTO_CIPHER_ALG_DES:
+ if (len % 8)
+ return -1;
+ blocks = len / 8;
+ for (i = 0; i < blocks; i++) {
+ for (j = 0; j < 8; j++)
+ ctx->u.des3.cbc[j] ^= plain[j];
+ des_block_encrypt(ctx->u.des.cbc, ctx->u.des.ek,
+ ctx->u.des.cbc);
+ os_memcpy(crypt, ctx->u.des.cbc, 8);
+ plain += 8;
+ crypt += 8;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+ u8 *plain, size_t len)
+{
+ size_t i, j, blocks;
+ u8 tmp[32];
+
+ switch (ctx->alg) {
+ case CRYPTO_CIPHER_ALG_RC4:
+ if (plain != crypt)
+ os_memcpy(plain, crypt, len);
+ rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+ ctx->u.rc4.used_bytes, plain, len);
+ ctx->u.rc4.used_bytes += len;
+ break;
+ case CRYPTO_CIPHER_ALG_AES:
+ if (len % ctx->u.aes.block_size)
+ return -1;
+ blocks = len / ctx->u.aes.block_size;
+ for (i = 0; i < blocks; i++) {
+ os_memcpy(tmp, crypt, ctx->u.aes.block_size);
+ aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain);
+ for (j = 0; j < ctx->u.aes.block_size; j++)
+ plain[j] ^= ctx->u.aes.cbc[j];
+ os_memcpy(ctx->u.aes.cbc, tmp, ctx->u.aes.block_size);
+ plain += ctx->u.aes.block_size;
+ crypt += ctx->u.aes.block_size;
+ }
+ break;
+ case CRYPTO_CIPHER_ALG_3DES:
+ if (len % 8)
+ return -1;
+ blocks = len / 8;
+ for (i = 0; i < blocks; i++) {
+ os_memcpy(tmp, crypt, 8);
+ des3_decrypt(crypt, &ctx->u.des3.key, plain);
+ for (j = 0; j < 8; j++)
+ plain[j] ^= ctx->u.des3.cbc[j];
+ os_memcpy(ctx->u.des3.cbc, tmp, 8);
+ plain += 8;
+ crypt += 8;
+ }
+ break;
+ case CRYPTO_CIPHER_ALG_DES:
+ if (len % 8)
+ return -1;
+ blocks = len / 8;
+ for (i = 0; i < blocks; i++) {
+ os_memcpy(tmp, crypt, 8);
+ des_block_decrypt(crypt, ctx->u.des.dk, plain);
+ for (j = 0; j < 8; j++)
+ plain[j] ^= ctx->u.des.cbc[j];
+ os_memcpy(ctx->u.des.cbc, tmp, 8);
+ plain += 8;
+ crypt += 8;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+ switch (ctx->alg) {
+ case CRYPTO_CIPHER_ALG_AES:
+ aes_encrypt_deinit(ctx->u.aes.ctx_enc);
+ aes_decrypt_deinit(ctx->u.aes.ctx_dec);
+ break;
+ case CRYPTO_CIPHER_ALG_3DES:
+ break;
+ default:
+ break;
+ }
+ os_free(ctx);
+}
diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c
new file mode 100644
index 0000000..3124742
--- /dev/null
+++ b/src/crypto/crypto_internal-modexp.c
@@ -0,0 +1,55 @@
+/*
+ * Crypto wrapper for internal crypto implementation - modexp
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "tls/bignum.h"
+#include "crypto.h"
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len)
+{
+ struct bignum *bn_base, *bn_exp, *bn_modulus, *bn_result;
+ int ret = -1;
+
+ bn_base = bignum_init();
+ bn_exp = bignum_init();
+ bn_modulus = bignum_init();
+ bn_result = bignum_init();
+
+ if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL ||
+ bn_result == NULL)
+ goto error;
+
+ if (bignum_set_unsigned_bin(bn_base, base, base_len) < 0 ||
+ bignum_set_unsigned_bin(bn_exp, power, power_len) < 0 ||
+ bignum_set_unsigned_bin(bn_modulus, modulus, modulus_len) < 0)
+ goto error;
+
+ if (bignum_exptmod(bn_base, bn_exp, bn_modulus, bn_result) < 0)
+ goto error;
+
+ ret = bignum_get_unsigned_bin(bn_result, result, result_len);
+
+error:
+ bignum_deinit(bn_base);
+ bignum_deinit(bn_exp);
+ bignum_deinit(bn_modulus);
+ bignum_deinit(bn_result);
+ return ret;
+}
diff --git a/src/crypto/crypto_internal-rsa.c b/src/crypto/crypto_internal-rsa.c
new file mode 100644
index 0000000..205042c
--- /dev/null
+++ b/src/crypto/crypto_internal-rsa.c
@@ -0,0 +1,115 @@
+/*
+ * Crypto wrapper for internal crypto implementation - RSA parts
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "tls/rsa.h"
+#include "tls/bignum.h"
+#include "tls/pkcs1.h"
+#include "tls/pkcs8.h"
+
+/* Dummy structures; these are just typecast to struct crypto_rsa_key */
+struct crypto_public_key;
+struct crypto_private_key;
+
+
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
+{
+ return (struct crypto_public_key *)
+ crypto_rsa_import_public_key(key, len);
+}
+
+
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+ size_t len,
+ const char *passwd)
+{
+ struct crypto_private_key *res;
+
+ /* First, check for possible PKCS #8 encoding */
+ res = pkcs8_key_import(key, len);
+ if (res)
+ return res;
+
+ if (passwd) {
+ /* Try to parse as encrypted PKCS #8 */
+ res = pkcs8_enc_key_import(key, len, passwd);
+ if (res)
+ return res;
+ }
+
+ /* Not PKCS#8, so try to import PKCS #1 encoded RSA private key */
+ wpa_printf(MSG_DEBUG, "Trying to parse PKCS #1 encoded RSA private "
+ "key");
+ return (struct crypto_private_key *)
+ crypto_rsa_import_private_key(key, len);
+}
+
+
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+ size_t len)
+{
+ /* No X.509 support in crypto_internal.c */
+ return NULL;
+}
+
+
+int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ return pkcs1_encrypt(2, (struct crypto_rsa_key *) key,
+ 0, in, inlen, out, outlen);
+}
+
+
+int crypto_private_key_decrypt_pkcs1_v15(struct crypto_private_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ return pkcs1_v15_private_key_decrypt((struct crypto_rsa_key *) key,
+ in, inlen, out, outlen);
+}
+
+
+int crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ return pkcs1_encrypt(1, (struct crypto_rsa_key *) key,
+ 1, in, inlen, out, outlen);
+}
+
+
+void crypto_public_key_free(struct crypto_public_key *key)
+{
+ crypto_rsa_free((struct crypto_rsa_key *) key);
+}
+
+
+void crypto_private_key_free(struct crypto_private_key *key)
+{
+ crypto_rsa_free((struct crypto_rsa_key *) key);
+}
+
+
+int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key,
+ const u8 *crypt, size_t crypt_len,
+ u8 *plain, size_t *plain_len)
+{
+ return pkcs1_decrypt_public_key((struct crypto_rsa_key *) key,
+ crypt, crypt_len, plain, plain_len);
+}
diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c
new file mode 100644
index 0000000..8fdba65
--- /dev/null
+++ b/src/crypto/crypto_internal.c
@@ -0,0 +1,205 @@
+/*
+ * Crypto wrapper for internal crypto implementation
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "sha1_i.h"
+#include "md5_i.h"
+
+struct crypto_hash {
+ enum crypto_hash_alg alg;
+ union {
+ struct MD5Context md5;
+ struct SHA1Context sha1;
+ } u;
+ u8 key[64];
+ size_t key_len;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_hash *ctx;
+ u8 k_pad[64];
+ u8 tk[20];
+ size_t i;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->alg = alg;
+
+ switch (alg) {
+ case CRYPTO_HASH_ALG_MD5:
+ MD5Init(&ctx->u.md5);
+ break;
+ case CRYPTO_HASH_ALG_SHA1:
+ SHA1Init(&ctx->u.sha1);
+ break;
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ if (key_len > sizeof(k_pad)) {
+ MD5Init(&ctx->u.md5);
+ MD5Update(&ctx->u.md5, key, key_len);
+ MD5Final(tk, &ctx->u.md5);
+ key = tk;
+ key_len = 16;
+ }
+ os_memcpy(ctx->key, key, key_len);
+ ctx->key_len = key_len;
+
+ os_memcpy(k_pad, key, key_len);
+ os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+ for (i = 0; i < sizeof(k_pad); i++)
+ k_pad[i] ^= 0x36;
+ MD5Init(&ctx->u.md5);
+ MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad));
+ break;
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ if (key_len > sizeof(k_pad)) {
+ SHA1Init(&ctx->u.sha1);
+ SHA1Update(&ctx->u.sha1, key, key_len);
+ SHA1Final(tk, &ctx->u.sha1);
+ key = tk;
+ key_len = 20;
+ }
+ os_memcpy(ctx->key, key, key_len);
+ ctx->key_len = key_len;
+
+ os_memcpy(k_pad, key, key_len);
+ os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+ for (i = 0; i < sizeof(k_pad); i++)
+ k_pad[i] ^= 0x36;
+ SHA1Init(&ctx->u.sha1);
+ SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad));
+ break;
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+ if (ctx == NULL)
+ return;
+
+ switch (ctx->alg) {
+ case CRYPTO_HASH_ALG_MD5:
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ MD5Update(&ctx->u.md5, data, len);
+ break;
+ case CRYPTO_HASH_ALG_SHA1:
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ SHA1Update(&ctx->u.sha1, data, len);
+ break;
+ }
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+ u8 k_pad[64];
+ size_t i;
+
+ if (ctx == NULL)
+ return -2;
+
+ if (mac == NULL || len == NULL) {
+ os_free(ctx);
+ return 0;
+ }
+
+ switch (ctx->alg) {
+ case CRYPTO_HASH_ALG_MD5:
+ if (*len < 16) {
+ *len = 16;
+ os_free(ctx);
+ return -1;
+ }
+ *len = 16;
+ MD5Final(mac, &ctx->u.md5);
+ break;
+ case CRYPTO_HASH_ALG_SHA1:
+ if (*len < 20) {
+ *len = 20;
+ os_free(ctx);
+ return -1;
+ }
+ *len = 20;
+ SHA1Final(mac, &ctx->u.sha1);
+ break;
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ if (*len < 16) {
+ *len = 16;
+ os_free(ctx);
+ return -1;
+ }
+ *len = 16;
+
+ MD5Final(mac, &ctx->u.md5);
+
+ os_memcpy(k_pad, ctx->key, ctx->key_len);
+ os_memset(k_pad + ctx->key_len, 0,
+ sizeof(k_pad) - ctx->key_len);
+ for (i = 0; i < sizeof(k_pad); i++)
+ k_pad[i] ^= 0x5c;
+ MD5Init(&ctx->u.md5);
+ MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad));
+ MD5Update(&ctx->u.md5, mac, 16);
+ MD5Final(mac, &ctx->u.md5);
+ break;
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ if (*len < 20) {
+ *len = 20;
+ os_free(ctx);
+ return -1;
+ }
+ *len = 20;
+
+ SHA1Final(mac, &ctx->u.sha1);
+
+ os_memcpy(k_pad, ctx->key, ctx->key_len);
+ os_memset(k_pad + ctx->key_len, 0,
+ sizeof(k_pad) - ctx->key_len);
+ for (i = 0; i < sizeof(k_pad); i++)
+ k_pad[i] ^= 0x5c;
+ SHA1Init(&ctx->u.sha1);
+ SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad));
+ SHA1Update(&ctx->u.sha1, mac, 20);
+ SHA1Final(mac, &ctx->u.sha1);
+ break;
+ }
+
+ os_free(ctx);
+
+ return 0;
+}
+
+
+int crypto_global_init(void)
+{
+ return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c
new file mode 100644
index 0000000..52b67a7
--- /dev/null
+++ b/src/crypto/crypto_libtomcrypt.c
@@ -0,0 +1,732 @@
+/*
+ * WPA Supplicant / Crypto wrapper for LibTomCrypt (for internal TLSv1)
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <tomcrypt.h>
+
+#include "common.h"
+#include "crypto.h"
+
+#ifndef mp_init_multi
+#define mp_init_multi ltc_init_multi
+#define mp_clear_multi ltc_deinit_multi
+#define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a)
+#define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b)
+#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c)
+#define mp_exptmod(a,b,c,d) ltc_mp.exptmod(a,b,c,d)
+#endif
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ hash_state md;
+ size_t i;
+
+ md4_init(&md);
+ for (i = 0; i < num_elem; i++)
+ md4_process(&md, addr[i], len[i]);
+ md4_done(&md, mac);
+ return 0;
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ u8 pkey[8], next, tmp;
+ int i;
+ symmetric_key skey;
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ pkey[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ pkey[i] = next | 1;
+
+ des_setup(pkey, 8, 0, &skey);
+ des_ecb_encrypt(clear, cypher, &skey);
+ des_done(&skey);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ hash_state md;
+ size_t i;
+
+ md5_init(&md);
+ for (i = 0; i < num_elem; i++)
+ md5_process(&md, addr[i], len[i]);
+ md5_done(&md, mac);
+ return 0;
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ hash_state md;
+ size_t i;
+
+ sha1_init(&md);
+ for (i = 0; i < num_elem; i++)
+ sha1_process(&md, addr[i], len[i]);
+ sha1_done(&md, mac);
+ return 0;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ symmetric_key *skey;
+ skey = os_malloc(sizeof(*skey));
+ if (skey == NULL)
+ return NULL;
+ if (aes_setup(key, len, 0, skey) != CRYPT_OK) {
+ os_free(skey);
+ return NULL;
+ }
+ return skey;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+ symmetric_key *skey = ctx;
+ aes_ecb_encrypt(plain, crypt, skey);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+ symmetric_key *skey = ctx;
+ aes_done(skey);
+ os_free(skey);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+ symmetric_key *skey;
+ skey = os_malloc(sizeof(*skey));
+ if (skey == NULL)
+ return NULL;
+ if (aes_setup(key, len, 0, skey) != CRYPT_OK) {
+ os_free(skey);
+ return NULL;
+ }
+ return skey;
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+ symmetric_key *skey = ctx;
+ aes_ecb_encrypt(plain, (u8 *) crypt, skey);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+ symmetric_key *skey = ctx;
+ aes_done(skey);
+ os_free(skey);
+}
+
+
+struct crypto_hash {
+ enum crypto_hash_alg alg;
+ int error;
+ union {
+ hash_state md;
+ hmac_state hmac;
+ } u;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_hash *ctx;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->alg = alg;
+
+ switch (alg) {
+ case CRYPTO_HASH_ALG_MD5:
+ if (md5_init(&ctx->u.md) != CRYPT_OK)
+ goto fail;
+ break;
+ case CRYPTO_HASH_ALG_SHA1:
+ if (sha1_init(&ctx->u.md) != CRYPT_OK)
+ goto fail;
+ break;
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ if (hmac_init(&ctx->u.hmac, find_hash("md5"), key, key_len) !=
+ CRYPT_OK)
+ goto fail;
+ break;
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ if (hmac_init(&ctx->u.hmac, find_hash("sha1"), key, key_len) !=
+ CRYPT_OK)
+ goto fail;
+ break;
+ default:
+ goto fail;
+ }
+
+ return ctx;
+
+fail:
+ os_free(ctx);
+ return NULL;
+}
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+ if (ctx == NULL || ctx->error)
+ return;
+
+ switch (ctx->alg) {
+ case CRYPTO_HASH_ALG_MD5:
+ ctx->error = md5_process(&ctx->u.md, data, len) != CRYPT_OK;
+ break;
+ case CRYPTO_HASH_ALG_SHA1:
+ ctx->error = sha1_process(&ctx->u.md, data, len) != CRYPT_OK;
+ break;
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ ctx->error = hmac_process(&ctx->u.hmac, data, len) != CRYPT_OK;
+ break;
+ }
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+ int ret = 0;
+ unsigned long clen;
+
+ if (ctx == NULL)
+ return -2;
+
+ if (mac == NULL || len == NULL) {
+ os_free(ctx);
+ return 0;
+ }
+
+ if (ctx->error) {
+ os_free(ctx);
+ return -2;
+ }
+
+ switch (ctx->alg) {
+ case CRYPTO_HASH_ALG_MD5:
+ if (*len < 16) {
+ *len = 16;
+ os_free(ctx);
+ return -1;
+ }
+ *len = 16;
+ if (md5_done(&ctx->u.md, mac) != CRYPT_OK)
+ ret = -2;
+ break;
+ case CRYPTO_HASH_ALG_SHA1:
+ if (*len < 20) {
+ *len = 20;
+ os_free(ctx);
+ return -1;
+ }
+ *len = 20;
+ if (sha1_done(&ctx->u.md, mac) != CRYPT_OK)
+ ret = -2;
+ break;
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ if (*len < 20) {
+ *len = 20;
+ os_free(ctx);
+ return -1;
+ }
+ /* continue */
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ if (*len < 16) {
+ *len = 16;
+ os_free(ctx);
+ return -1;
+ }
+ clen = *len;
+ if (hmac_done(&ctx->u.hmac, mac, &clen) != CRYPT_OK) {
+ os_free(ctx);
+ return -1;
+ }
+ *len = clen;
+ break;
+ default:
+ ret = -2;
+ break;
+ }
+
+ os_free(ctx);
+
+ return ret;
+}
+
+
+struct crypto_cipher {
+ int rc4;
+ union {
+ symmetric_CBC cbc;
+ struct {
+ size_t used_bytes;
+ u8 key[16];
+ size_t keylen;
+ } rc4;
+ } u;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_cipher *ctx;
+ int idx, res, rc4 = 0;
+
+ switch (alg) {
+ case CRYPTO_CIPHER_ALG_AES:
+ idx = find_cipher("aes");
+ break;
+ case CRYPTO_CIPHER_ALG_3DES:
+ idx = find_cipher("3des");
+ break;
+ case CRYPTO_CIPHER_ALG_DES:
+ idx = find_cipher("des");
+ break;
+ case CRYPTO_CIPHER_ALG_RC2:
+ idx = find_cipher("rc2");
+ break;
+ case CRYPTO_CIPHER_ALG_RC4:
+ idx = -1;
+ rc4 = 1;
+ break;
+ default:
+ return NULL;
+ }
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ if (rc4) {
+ ctx->rc4 = 1;
+ if (key_len > sizeof(ctx->u.rc4.key)) {
+ os_free(ctx);
+ return NULL;
+ }
+ ctx->u.rc4.keylen = key_len;
+ os_memcpy(ctx->u.rc4.key, key, key_len);
+ } else {
+ res = cbc_start(idx, iv, key, key_len, 0, &ctx->u.cbc);
+ if (res != CRYPT_OK) {
+ wpa_printf(MSG_DEBUG, "LibTomCrypt: Cipher start "
+ "failed: %s", error_to_string(res));
+ os_free(ctx);
+ return NULL;
+ }
+ }
+
+ return ctx;
+}
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+ u8 *crypt, size_t len)
+{
+ int res;
+
+ if (ctx->rc4) {
+ if (plain != crypt)
+ os_memcpy(crypt, plain, len);
+ rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+ ctx->u.rc4.used_bytes, crypt, len);
+ ctx->u.rc4.used_bytes += len;
+ return 0;
+ }
+
+ res = cbc_encrypt(plain, crypt, len, &ctx->u.cbc);
+ if (res != CRYPT_OK) {
+ wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC encryption "
+ "failed: %s", error_to_string(res));
+ return -1;
+ }
+ return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+ u8 *plain, size_t len)
+{
+ int res;
+
+ if (ctx->rc4) {
+ if (plain != crypt)
+ os_memcpy(plain, crypt, len);
+ rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+ ctx->u.rc4.used_bytes, plain, len);
+ ctx->u.rc4.used_bytes += len;
+ return 0;
+ }
+
+ res = cbc_decrypt(crypt, plain, len, &ctx->u.cbc);
+ if (res != CRYPT_OK) {
+ wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC decryption "
+ "failed: %s", error_to_string(res));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+ if (!ctx->rc4)
+ cbc_done(&ctx->u.cbc);
+ os_free(ctx);
+}
+
+
+struct crypto_public_key {
+ rsa_key rsa;
+};
+
+struct crypto_private_key {
+ rsa_key rsa;
+};
+
+
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
+{
+ int res;
+ struct crypto_public_key *pk;
+
+ pk = os_zalloc(sizeof(*pk));
+ if (pk == NULL)
+ return NULL;
+
+ res = rsa_import(key, len, &pk->rsa);
+ if (res != CRYPT_OK) {
+ wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import "
+ "public key (res=%d '%s')",
+ res, error_to_string(res));
+ os_free(pk);
+ return NULL;
+ }
+
+ if (pk->rsa.type != PK_PUBLIC) {
+ wpa_printf(MSG_ERROR, "LibTomCrypt: Public key was not of "
+ "correct type");
+ rsa_free(&pk->rsa);
+ os_free(pk);
+ return NULL;
+ }
+
+ return pk;
+}
+
+
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+ size_t len,
+ const char *passwd)
+{
+ int res;
+ struct crypto_private_key *pk;
+
+ pk = os_zalloc(sizeof(*pk));
+ if (pk == NULL)
+ return NULL;
+
+ res = rsa_import(key, len, &pk->rsa);
+ if (res != CRYPT_OK) {
+ wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import "
+ "private key (res=%d '%s')",
+ res, error_to_string(res));
+ os_free(pk);
+ return NULL;
+ }
+
+ if (pk->rsa.type != PK_PRIVATE) {
+ wpa_printf(MSG_ERROR, "LibTomCrypt: Private key was not of "
+ "correct type");
+ rsa_free(&pk->rsa);
+ os_free(pk);
+ return NULL;
+ }
+
+ return pk;
+}
+
+
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+ size_t len)
+{
+ /* No X.509 support in LibTomCrypt */
+ return NULL;
+}
+
+
+static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ size_t ps_len;
+ u8 *pos;
+
+ /*
+ * PKCS #1 v1.5, 8.1:
+ *
+ * EB = 00 || BT || PS || 00 || D
+ * BT = 00 or 01 for private-key operation; 02 for public-key operation
+ * PS = k-3-||D||; at least eight octets
+ * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero)
+ * k = length of modulus in octets (modlen)
+ */
+
+ if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) {
+ wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer "
+ "lengths (modlen=%lu outlen=%lu inlen=%lu)",
+ __func__, (unsigned long) modlen,
+ (unsigned long) *outlen,
+ (unsigned long) inlen);
+ return -1;
+ }
+
+ pos = out;
+ *pos++ = 0x00;
+ *pos++ = block_type; /* BT */
+ ps_len = modlen - inlen - 3;
+ switch (block_type) {
+ case 0:
+ os_memset(pos, 0x00, ps_len);
+ pos += ps_len;
+ break;
+ case 1:
+ os_memset(pos, 0xff, ps_len);
+ pos += ps_len;
+ break;
+ case 2:
+ if (os_get_random(pos, ps_len) < 0) {
+ wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get "
+ "random data for PS", __func__);
+ return -1;
+ }
+ while (ps_len--) {
+ if (*pos == 0x00)
+ *pos = 0x01;
+ pos++;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type "
+ "%d", __func__, block_type);
+ return -1;
+ }
+ *pos++ = 0x00;
+ os_memcpy(pos, in, inlen); /* D */
+
+ return 0;
+}
+
+
+static int crypto_rsa_encrypt_pkcs1(int block_type, rsa_key *key, int key_type,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ unsigned long len, modlen;
+ int res;
+
+ modlen = mp_unsigned_bin_size(key->N);
+
+ if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen,
+ out, outlen) < 0)
+ return -1;
+
+ len = *outlen;
+ res = rsa_exptmod(out, modlen, out, &len, key_type, key);
+ if (res != CRYPT_OK) {
+ wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s",
+ error_to_string(res));
+ return -1;
+ }
+ *outlen = len;
+
+ return 0;
+}
+
+
+int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ return crypto_rsa_encrypt_pkcs1(2, &key->rsa, PK_PUBLIC, in, inlen,
+ out, outlen);
+}
+
+
+int crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ return crypto_rsa_encrypt_pkcs1(1, &key->rsa, PK_PRIVATE, in, inlen,
+ out, outlen);
+}
+
+
+void crypto_public_key_free(struct crypto_public_key *key)
+{
+ if (key) {
+ rsa_free(&key->rsa);
+ os_free(key);
+ }
+}
+
+
+void crypto_private_key_free(struct crypto_private_key *key)
+{
+ if (key) {
+ rsa_free(&key->rsa);
+ os_free(key);
+ }
+}
+
+
+int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key,
+ const u8 *crypt, size_t crypt_len,
+ u8 *plain, size_t *plain_len)
+{
+ int res;
+ unsigned long len;
+ u8 *pos;
+
+ len = *plain_len;
+ res = rsa_exptmod(crypt, crypt_len, plain, &len, PK_PUBLIC,
+ &key->rsa);
+ if (res != CRYPT_OK) {
+ wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s",
+ error_to_string(res));
+ return -1;
+ }
+
+ /*
+ * PKCS #1 v1.5, 8.1:
+ *
+ * EB = 00 || BT || PS || 00 || D
+ * BT = 01
+ * PS = k-3-||D|| times FF
+ * k = length of modulus in octets
+ */
+
+ if (len < 3 + 8 + 16 /* min hash len */ ||
+ plain[0] != 0x00 || plain[1] != 0x01 || plain[2] != 0xff) {
+ wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+ "structure");
+ return -1;
+ }
+
+ pos = plain + 3;
+ while (pos < plain + len && *pos == 0xff)
+ pos++;
+ if (pos - plain - 2 < 8) {
+ /* PKCS #1 v1.5, 8.1: At least eight octets long PS */
+ wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature "
+ "padding");
+ return -1;
+ }
+
+ if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) {
+ wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+ "structure (2)");
+ return -1;
+ }
+ pos++;
+ len -= pos - plain;
+
+ /* Strip PKCS #1 header */
+ os_memmove(plain, pos, len);
+ *plain_len = len;
+
+ return 0;
+}
+
+
+int crypto_global_init(void)
+{
+ ltc_mp = tfm_desc;
+ /* TODO: only register algorithms that are really needed */
+ if (register_hash(&md4_desc) < 0 ||
+ register_hash(&md5_desc) < 0 ||
+ register_hash(&sha1_desc) < 0 ||
+ register_cipher(&aes_desc) < 0 ||
+ register_cipher(&des_desc) < 0 ||
+ register_cipher(&des3_desc) < 0) {
+ wpa_printf(MSG_ERROR, "TLSv1: Failed to register "
+ "hash/cipher functions");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
+
+
+#ifdef CONFIG_MODEXP
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len)
+{
+ void *b, *p, *m, *r;
+
+ if (mp_init_multi(&b, &p, &m, &r, NULL) != CRYPT_OK)
+ return -1;
+
+ if (mp_read_unsigned_bin(b, (u8 *) base, base_len) != CRYPT_OK ||
+ mp_read_unsigned_bin(p, (u8 *) power, power_len) != CRYPT_OK ||
+ mp_read_unsigned_bin(m, (u8 *) modulus, modulus_len) != CRYPT_OK)
+ goto fail;
+
+ if (mp_exptmod(b, p, m, r) != CRYPT_OK)
+ goto fail;
+
+ *result_len = mp_unsigned_bin_size(r);
+ if (mp_to_unsigned_bin(r, result) != CRYPT_OK)
+ goto fail;
+
+ mp_clear_multi(b, p, m, r, NULL);
+ return 0;
+
+fail:
+ mp_clear_multi(b, p, m, r, NULL);
+ return -1;
+}
+
+#endif /* CONFIG_MODEXP */
diff --git a/src/crypto/crypto_none.c b/src/crypto/crypto_none.c
new file mode 100644
index 0000000..9f43775
--- /dev/null
+++ b/src/crypto/crypto_none.c
@@ -0,0 +1,29 @@
+/*
+ * WPA Supplicant / Empty template functions for crypto wrapper
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return 0;
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+}
diff --git a/src/crypto/crypto_nss.c b/src/crypto/crypto_nss.c
new file mode 100644
index 0000000..fee4195
--- /dev/null
+++ b/src/crypto/crypto_nss.c
@@ -0,0 +1,213 @@
+/*
+ * Crypto wrapper functions for NSS
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <nspr/prtypes.h>
+#include <nspr/plarenas.h>
+#include <nspr/plhash.h>
+#include <nspr/prtime.h>
+#include <nspr/prinrval.h>
+#include <nspr/prclist.h>
+#include <nspr/prlock.h>
+#include <nss/sechash.h>
+#include <nss/pk11pub.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+static int nss_hash(HASH_HashType type, unsigned int max_res_len,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ HASHContext *ctx;
+ size_t i;
+ unsigned int reslen;
+
+ ctx = HASH_Create(type);
+ if (ctx == NULL)
+ return -1;
+
+ HASH_Begin(ctx);
+ for (i = 0; i < num_elem; i++)
+ HASH_Update(ctx, addr[i], len[i]);
+ HASH_End(ctx, mac, &reslen, max_res_len);
+ HASH_Destroy(ctx);
+
+ return 0;
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ PK11Context *ctx = NULL;
+ PK11SlotInfo *slot;
+ SECItem *param = NULL;
+ PK11SymKey *symkey = NULL;
+ SECItem item;
+ int olen;
+ u8 pkey[8], next, tmp;
+ int i;
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ pkey[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ pkey[i] = next | 1;
+
+ slot = PK11_GetBestSlot(CKM_DES_ECB, NULL);
+ if (slot == NULL) {
+ wpa_printf(MSG_ERROR, "NSS: PK11_GetBestSlot failed");
+ goto out;
+ }
+
+ item.type = siBuffer;
+ item.data = pkey;
+ item.len = 8;
+ symkey = PK11_ImportSymKey(slot, CKM_DES_ECB, PK11_OriginDerive,
+ CKA_ENCRYPT, &item, NULL);
+ if (symkey == NULL) {
+ wpa_printf(MSG_ERROR, "NSS: PK11_ImportSymKey failed");
+ goto out;
+ }
+
+ param = PK11_GenerateNewParam(CKM_DES_ECB, symkey);
+ if (param == NULL) {
+ wpa_printf(MSG_ERROR, "NSS: PK11_GenerateNewParam failed");
+ goto out;
+ }
+
+ ctx = PK11_CreateContextBySymKey(CKM_DES_ECB, CKA_ENCRYPT,
+ symkey, param);
+ if (ctx == NULL) {
+ wpa_printf(MSG_ERROR, "NSS: PK11_CreateContextBySymKey("
+ "CKM_DES_ECB) failed");
+ goto out;
+ }
+
+ if (PK11_CipherOp(ctx, cypher, &olen, 8, (void *) clear, 8) !=
+ SECSuccess) {
+ wpa_printf(MSG_ERROR, "NSS: PK11_CipherOp failed");
+ goto out;
+ }
+
+out:
+ if (ctx)
+ PK11_DestroyContext(ctx, PR_TRUE);
+ if (symkey)
+ PK11_FreeSymKey(symkey);
+ if (param)
+ SECITEM_FreeItem(param, PR_TRUE);
+}
+
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+ u8 *data, size_t data_len)
+{
+ return -1;
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nss_hash(HASH_AlgMD5, 16, num_elem, addr, len, mac);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return nss_hash(HASH_AlgSHA1, 20, num_elem, addr, len, mac);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ return nss_hash(HASH_AlgSHA256, 32, num_elem, addr, len, mac);
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ return NULL;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+ return NULL;
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len)
+{
+ return -1;
+}
+
+
+struct crypto_cipher {
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+{
+ return NULL;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+ u8 *crypt, size_t len)
+{
+ return -1;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+ u8 *plain, size_t len)
+{
+ return -1;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+}
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
new file mode 100644
index 0000000..08c98af
--- /dev/null
+++ b/src/crypto/crypto_openssl.c
@@ -0,0 +1,505 @@
+/*
+ * WPA Supplicant / wrapper functions for libcrypto
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+#include <openssl/des.h>
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/dh.h>
+
+#include "common.h"
+#include "wpabuf.h"
+#include "dh_group5.h"
+#include "crypto.h"
+
+#if OPENSSL_VERSION_NUMBER < 0x00907000
+#define DES_key_schedule des_key_schedule
+#define DES_cblock des_cblock
+#define DES_set_key(key, schedule) des_set_key((key), *(schedule))
+#define DES_ecb_encrypt(input, output, ks, enc) \
+ des_ecb_encrypt((input), (output), *(ks), (enc))
+#endif /* openssl < 0.9.7 */
+
+static BIGNUM * get_group5_prime(void)
+{
+#if OPENSSL_VERSION_NUMBER < 0x00908000
+ static const unsigned char RFC3526_PRIME_1536[] = {
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,
+ 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,
+ 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6,
+ 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD,
+ 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,
+ 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,
+ 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9,
+ 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED,
+ 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,
+ 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,
+ 0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36,
+ 0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F,
+ 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,
+ 0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,
+ 0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08,
+ 0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ };
+ return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL);
+#else /* openssl < 0.9.8 */
+ return get_rfc3526_prime_1536(NULL);
+#endif /* openssl < 0.9.8 */
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x00908000
+#ifndef OPENSSL_NO_SHA256
+#ifndef OPENSSL_FIPS
+#define NO_SHA256_WRAPPER
+#endif
+#endif
+
+#endif /* openssl < 0.9.8 */
+
+#ifdef OPENSSL_NO_SHA256
+#define NO_SHA256_WRAPPER
+#endif
+
+static int openssl_digest_vector(const EVP_MD *type, int non_fips,
+ size_t num_elem, const u8 *addr[],
+ const size_t *len, u8 *mac)
+{
+ EVP_MD_CTX ctx;
+ size_t i;
+ unsigned int mac_len;
+
+ EVP_MD_CTX_init(&ctx);
+#ifdef CONFIG_FIPS
+#ifdef OPENSSL_FIPS
+ if (non_fips)
+ EVP_MD_CTX_set_flags(&ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
+#endif /* OPENSSL_FIPS */
+#endif /* CONFIG_FIPS */
+ if (!EVP_DigestInit_ex(&ctx, type, NULL)) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+ for (i = 0; i < num_elem; i++) {
+ if (!EVP_DigestUpdate(&ctx, addr[i], len[i])) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate "
+ "failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+ }
+ if (!EVP_DigestFinal(&ctx, mac, &mac_len)) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_digest_vector(EVP_md4(), 0, num_elem, addr, len, mac);
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ u8 pkey[8], next, tmp;
+ int i;
+ DES_key_schedule ks;
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ pkey[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ pkey[i] = next | 1;
+
+ DES_set_key(&pkey, &ks);
+ DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks,
+ DES_ENCRYPT);
+}
+
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+ u8 *data, size_t data_len)
+{
+#ifdef OPENSSL_NO_RC4
+ return -1;
+#else /* OPENSSL_NO_RC4 */
+ EVP_CIPHER_CTX ctx;
+ int outl;
+ int res = -1;
+ unsigned char skip_buf[16];
+
+ EVP_CIPHER_CTX_init(&ctx);
+ if (!EVP_CIPHER_CTX_set_padding(&ctx, 0) ||
+ !EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1) ||
+ !EVP_CIPHER_CTX_set_key_length(&ctx, keylen) ||
+ !EVP_CipherInit_ex(&ctx, NULL, NULL, key, NULL, 1))
+ goto out;
+
+ while (skip >= sizeof(skip_buf)) {
+ size_t len = skip;
+ if (len > sizeof(skip_buf))
+ len = sizeof(skip_buf);
+ if (!EVP_CipherUpdate(&ctx, skip_buf, &outl, skip_buf, len))
+ goto out;
+ skip -= len;
+ }
+
+ if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len))
+ res = 0;
+
+out:
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return res;
+#endif /* OPENSSL_NO_RC4 */
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_digest_vector(EVP_md5(), 0, num_elem, addr, len, mac);
+}
+
+
+#ifdef CONFIG_FIPS
+int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[],
+ const size_t *len, u8 *mac)
+{
+ return openssl_digest_vector(EVP_md5(), 1, num_elem, addr, len, mac);
+}
+#endif /* CONFIG_FIPS */
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ return openssl_digest_vector(EVP_sha1(), 0, num_elem, addr, len, mac);
+}
+
+
+#ifndef NO_SHA256_WRAPPER
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ return openssl_digest_vector(EVP_sha256(), 0, num_elem, addr, len,
+ mac);
+}
+#endif /* NO_SHA256_WRAPPER */
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+ AES_KEY *ak;
+ ak = os_malloc(sizeof(*ak));
+ if (ak == NULL)
+ return NULL;
+ if (AES_set_encrypt_key(key, 8 * len, ak) < 0) {
+ os_free(ak);
+ return NULL;
+ }
+ return ak;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+ AES_encrypt(plain, crypt, ctx);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+ os_free(ctx);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+ AES_KEY *ak;
+ ak = os_malloc(sizeof(*ak));
+ if (ak == NULL)
+ return NULL;
+ if (AES_set_decrypt_key(key, 8 * len, ak) < 0) {
+ os_free(ak);
+ return NULL;
+ }
+ return ak;
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+ AES_decrypt(crypt, plain, ctx);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+ os_free(ctx);
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len)
+{
+ BIGNUM *bn_base, *bn_exp, *bn_modulus, *bn_result;
+ int ret = -1;
+ BN_CTX *ctx;
+
+ ctx = BN_CTX_new();
+ if (ctx == NULL)
+ return -1;
+
+ bn_base = BN_bin2bn(base, base_len, NULL);
+ bn_exp = BN_bin2bn(power, power_len, NULL);
+ bn_modulus = BN_bin2bn(modulus, modulus_len, NULL);
+ bn_result = BN_new();
+
+ if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL ||
+ bn_result == NULL)
+ goto error;
+
+ if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1)
+ goto error;
+
+ *result_len = BN_bn2bin(bn_result, result);
+ ret = 0;
+
+error:
+ BN_free(bn_base);
+ BN_free(bn_exp);
+ BN_free(bn_modulus);
+ BN_free(bn_result);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+
+struct crypto_cipher {
+ EVP_CIPHER_CTX enc;
+ EVP_CIPHER_CTX dec;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_cipher *ctx;
+ const EVP_CIPHER *cipher;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ switch (alg) {
+#ifndef OPENSSL_NO_RC4
+ case CRYPTO_CIPHER_ALG_RC4:
+ cipher = EVP_rc4();
+ break;
+#endif /* OPENSSL_NO_RC4 */
+#ifndef OPENSSL_NO_AES
+ case CRYPTO_CIPHER_ALG_AES:
+ switch (key_len) {
+ case 16:
+ cipher = EVP_aes_128_cbc();
+ break;
+ case 24:
+ cipher = EVP_aes_192_cbc();
+ break;
+ case 32:
+ cipher = EVP_aes_256_cbc();
+ break;
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+ break;
+#endif /* OPENSSL_NO_AES */
+#ifndef OPENSSL_NO_DES
+ case CRYPTO_CIPHER_ALG_3DES:
+ cipher = EVP_des_ede3_cbc();
+ break;
+ case CRYPTO_CIPHER_ALG_DES:
+ cipher = EVP_des_cbc();
+ break;
+#endif /* OPENSSL_NO_DES */
+#ifndef OPENSSL_NO_RC2
+ case CRYPTO_CIPHER_ALG_RC2:
+ cipher = EVP_rc2_ecb();
+ break;
+#endif /* OPENSSL_NO_RC2 */
+ default:
+ os_free(ctx);
+ return NULL;
+ }
+
+ EVP_CIPHER_CTX_init(&ctx->enc);
+ EVP_CIPHER_CTX_set_padding(&ctx->enc, 0);
+ if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) ||
+ !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) ||
+ !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) {
+ EVP_CIPHER_CTX_cleanup(&ctx->enc);
+ os_free(ctx);
+ return NULL;
+ }
+
+ EVP_CIPHER_CTX_init(&ctx->dec);
+ EVP_CIPHER_CTX_set_padding(&ctx->dec, 0);
+ if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) ||
+ !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) ||
+ !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) {
+ EVP_CIPHER_CTX_cleanup(&ctx->enc);
+ EVP_CIPHER_CTX_cleanup(&ctx->dec);
+ os_free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+ u8 *crypt, size_t len)
+{
+ int outl;
+ if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len))
+ return -1;
+ return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+ u8 *plain, size_t len)
+{
+ int outl;
+ outl = len;
+ if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len))
+ return -1;
+ return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+ EVP_CIPHER_CTX_cleanup(&ctx->enc);
+ EVP_CIPHER_CTX_cleanup(&ctx->dec);
+ os_free(ctx);
+}
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+ DH *dh;
+ struct wpabuf *pubkey = NULL, *privkey = NULL;
+ size_t publen, privlen;
+
+ *priv = NULL;
+ *publ = NULL;
+
+ dh = DH_new();
+ if (dh == NULL)
+ return NULL;
+
+ dh->g = BN_new();
+ if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
+ goto err;
+
+ dh->p = get_group5_prime();
+ if (dh->p == NULL)
+ goto err;
+
+ if (DH_generate_key(dh) != 1)
+ goto err;
+
+ publen = BN_num_bytes(dh->pub_key);
+ pubkey = wpabuf_alloc(publen);
+ if (pubkey == NULL)
+ goto err;
+ privlen = BN_num_bytes(dh->priv_key);
+ privkey = wpabuf_alloc(privlen);
+ if (privkey == NULL)
+ goto err;
+
+ BN_bn2bin(dh->pub_key, wpabuf_put(pubkey, publen));
+ BN_bn2bin(dh->priv_key, wpabuf_put(privkey, privlen));
+
+ *priv = privkey;
+ *publ = pubkey;
+ return dh;
+
+err:
+ wpabuf_free(pubkey);
+ wpabuf_free(privkey);
+ DH_free(dh);
+ return NULL;
+}
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+ const struct wpabuf *own_private)
+{
+ BIGNUM *pub_key;
+ struct wpabuf *res = NULL;
+ size_t rlen;
+ DH *dh = ctx;
+ int keylen;
+
+ if (ctx == NULL)
+ return NULL;
+
+ pub_key = BN_bin2bn(wpabuf_head(peer_public), wpabuf_len(peer_public),
+ NULL);
+ if (pub_key == NULL)
+ return NULL;
+
+ rlen = DH_size(dh);
+ res = wpabuf_alloc(rlen);
+ if (res == NULL)
+ goto err;
+
+ keylen = DH_compute_key(wpabuf_mhead(res), pub_key, dh);
+ if (keylen < 0)
+ goto err;
+ wpabuf_put(res, keylen);
+ BN_free(pub_key);
+
+ return res;
+
+err:
+ BN_free(pub_key);
+ wpabuf_free(res);
+ return NULL;
+}
+
+
+void dh5_free(void *ctx)
+{
+ DH *dh;
+ if (ctx == NULL)
+ return;
+ dh = ctx;
+ DH_free(dh);
+}
diff --git a/src/crypto/des-internal.c b/src/crypto/des-internal.c
new file mode 100644
index 0000000..ccea950
--- /dev/null
+++ b/src/crypto/des-internal.c
@@ -0,0 +1,499 @@
+/*
+ * DES and 3DES-EDE ciphers
+ *
+ * Modifications to LibTomCrypt implementation:
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "des_i.h"
+
+/*
+ * This implementation is based on a DES implementation included in
+ * LibTomCrypt. The version here is modified to fit in wpa_supplicant/hostapd
+ * coding style.
+ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com
+ */
+
+/**
+ DES code submitted by Dobes Vandermeer
+*/
+
+#define ROLc(x, y) \
+ ((((unsigned long) (x) << (unsigned long) ((y) & 31)) | \
+ (((unsigned long) (x) & 0xFFFFFFFFUL) >> \
+ (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#define RORc(x, y) \
+ (((((unsigned long) (x) & 0xFFFFFFFFUL) >> \
+ (unsigned long) ((y) & 31)) | \
+ ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & \
+ 0xFFFFFFFFUL)
+
+
+static const u32 bytebit[8] =
+{
+ 0200, 0100, 040, 020, 010, 04, 02, 01
+};
+
+static const u32 bigbyte[24] =
+{
+ 0x800000UL, 0x400000UL, 0x200000UL, 0x100000UL,
+ 0x80000UL, 0x40000UL, 0x20000UL, 0x10000UL,
+ 0x8000UL, 0x4000UL, 0x2000UL, 0x1000UL,
+ 0x800UL, 0x400UL, 0x200UL, 0x100UL,
+ 0x80UL, 0x40UL, 0x20UL, 0x10UL,
+ 0x8UL, 0x4UL, 0x2UL, 0x1L
+};
+
+/* Use the key schedule specific in the standard (ANSI X3.92-1981) */
+
+static const u8 pc1[56] = {
+ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3
+};
+
+static const u8 totrot[16] = {
+ 1, 2, 4, 6,
+ 8, 10, 12, 14,
+ 15, 17, 19, 21,
+ 23, 25, 27, 28
+};
+
+static const u8 pc2[48] = {
+ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+ 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
+};
+
+
+static const u32 SP1[64] =
+{
+ 0x01010400UL, 0x00000000UL, 0x00010000UL, 0x01010404UL,
+ 0x01010004UL, 0x00010404UL, 0x00000004UL, 0x00010000UL,
+ 0x00000400UL, 0x01010400UL, 0x01010404UL, 0x00000400UL,
+ 0x01000404UL, 0x01010004UL, 0x01000000UL, 0x00000004UL,
+ 0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00010400UL,
+ 0x00010400UL, 0x01010000UL, 0x01010000UL, 0x01000404UL,
+ 0x00010004UL, 0x01000004UL, 0x01000004UL, 0x00010004UL,
+ 0x00000000UL, 0x00000404UL, 0x00010404UL, 0x01000000UL,
+ 0x00010000UL, 0x01010404UL, 0x00000004UL, 0x01010000UL,
+ 0x01010400UL, 0x01000000UL, 0x01000000UL, 0x00000400UL,
+ 0x01010004UL, 0x00010000UL, 0x00010400UL, 0x01000004UL,
+ 0x00000400UL, 0x00000004UL, 0x01000404UL, 0x00010404UL,
+ 0x01010404UL, 0x00010004UL, 0x01010000UL, 0x01000404UL,
+ 0x01000004UL, 0x00000404UL, 0x00010404UL, 0x01010400UL,
+ 0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00000000UL,
+ 0x00010004UL, 0x00010400UL, 0x00000000UL, 0x01010004UL
+};
+
+static const u32 SP2[64] =
+{
+ 0x80108020UL, 0x80008000UL, 0x00008000UL, 0x00108020UL,
+ 0x00100000UL, 0x00000020UL, 0x80100020UL, 0x80008020UL,
+ 0x80000020UL, 0x80108020UL, 0x80108000UL, 0x80000000UL,
+ 0x80008000UL, 0x00100000UL, 0x00000020UL, 0x80100020UL,
+ 0x00108000UL, 0x00100020UL, 0x80008020UL, 0x00000000UL,
+ 0x80000000UL, 0x00008000UL, 0x00108020UL, 0x80100000UL,
+ 0x00100020UL, 0x80000020UL, 0x00000000UL, 0x00108000UL,
+ 0x00008020UL, 0x80108000UL, 0x80100000UL, 0x00008020UL,
+ 0x00000000UL, 0x00108020UL, 0x80100020UL, 0x00100000UL,
+ 0x80008020UL, 0x80100000UL, 0x80108000UL, 0x00008000UL,
+ 0x80100000UL, 0x80008000UL, 0x00000020UL, 0x80108020UL,
+ 0x00108020UL, 0x00000020UL, 0x00008000UL, 0x80000000UL,
+ 0x00008020UL, 0x80108000UL, 0x00100000UL, 0x80000020UL,
+ 0x00100020UL, 0x80008020UL, 0x80000020UL, 0x00100020UL,
+ 0x00108000UL, 0x00000000UL, 0x80008000UL, 0x00008020UL,
+ 0x80000000UL, 0x80100020UL, 0x80108020UL, 0x00108000UL
+};
+
+static const u32 SP3[64] =
+{
+ 0x00000208UL, 0x08020200UL, 0x00000000UL, 0x08020008UL,
+ 0x08000200UL, 0x00000000UL, 0x00020208UL, 0x08000200UL,
+ 0x00020008UL, 0x08000008UL, 0x08000008UL, 0x00020000UL,
+ 0x08020208UL, 0x00020008UL, 0x08020000UL, 0x00000208UL,
+ 0x08000000UL, 0x00000008UL, 0x08020200UL, 0x00000200UL,
+ 0x00020200UL, 0x08020000UL, 0x08020008UL, 0x00020208UL,
+ 0x08000208UL, 0x00020200UL, 0x00020000UL, 0x08000208UL,
+ 0x00000008UL, 0x08020208UL, 0x00000200UL, 0x08000000UL,
+ 0x08020200UL, 0x08000000UL, 0x00020008UL, 0x00000208UL,
+ 0x00020000UL, 0x08020200UL, 0x08000200UL, 0x00000000UL,
+ 0x00000200UL, 0x00020008UL, 0x08020208UL, 0x08000200UL,
+ 0x08000008UL, 0x00000200UL, 0x00000000UL, 0x08020008UL,
+ 0x08000208UL, 0x00020000UL, 0x08000000UL, 0x08020208UL,
+ 0x00000008UL, 0x00020208UL, 0x00020200UL, 0x08000008UL,
+ 0x08020000UL, 0x08000208UL, 0x00000208UL, 0x08020000UL,
+ 0x00020208UL, 0x00000008UL, 0x08020008UL, 0x00020200UL
+};
+
+static const u32 SP4[64] =
+{
+ 0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL,
+ 0x00802080UL, 0x00800081UL, 0x00800001UL, 0x00002001UL,
+ 0x00000000UL, 0x00802000UL, 0x00802000UL, 0x00802081UL,
+ 0x00000081UL, 0x00000000UL, 0x00800080UL, 0x00800001UL,
+ 0x00000001UL, 0x00002000UL, 0x00800000UL, 0x00802001UL,
+ 0x00000080UL, 0x00800000UL, 0x00002001UL, 0x00002080UL,
+ 0x00800081UL, 0x00000001UL, 0x00002080UL, 0x00800080UL,
+ 0x00002000UL, 0x00802080UL, 0x00802081UL, 0x00000081UL,
+ 0x00800080UL, 0x00800001UL, 0x00802000UL, 0x00802081UL,
+ 0x00000081UL, 0x00000000UL, 0x00000000UL, 0x00802000UL,
+ 0x00002080UL, 0x00800080UL, 0x00800081UL, 0x00000001UL,
+ 0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL,
+ 0x00802081UL, 0x00000081UL, 0x00000001UL, 0x00002000UL,
+ 0x00800001UL, 0x00002001UL, 0x00802080UL, 0x00800081UL,
+ 0x00002001UL, 0x00002080UL, 0x00800000UL, 0x00802001UL,
+ 0x00000080UL, 0x00800000UL, 0x00002000UL, 0x00802080UL
+};
+
+static const u32 SP5[64] =
+{
+ 0x00000100UL, 0x02080100UL, 0x02080000UL, 0x42000100UL,
+ 0x00080000UL, 0x00000100UL, 0x40000000UL, 0x02080000UL,
+ 0x40080100UL, 0x00080000UL, 0x02000100UL, 0x40080100UL,
+ 0x42000100UL, 0x42080000UL, 0x00080100UL, 0x40000000UL,
+ 0x02000000UL, 0x40080000UL, 0x40080000UL, 0x00000000UL,
+ 0x40000100UL, 0x42080100UL, 0x42080100UL, 0x02000100UL,
+ 0x42080000UL, 0x40000100UL, 0x00000000UL, 0x42000000UL,
+ 0x02080100UL, 0x02000000UL, 0x42000000UL, 0x00080100UL,
+ 0x00080000UL, 0x42000100UL, 0x00000100UL, 0x02000000UL,
+ 0x40000000UL, 0x02080000UL, 0x42000100UL, 0x40080100UL,
+ 0x02000100UL, 0x40000000UL, 0x42080000UL, 0x02080100UL,
+ 0x40080100UL, 0x00000100UL, 0x02000000UL, 0x42080000UL,
+ 0x42080100UL, 0x00080100UL, 0x42000000UL, 0x42080100UL,
+ 0x02080000UL, 0x00000000UL, 0x40080000UL, 0x42000000UL,
+ 0x00080100UL, 0x02000100UL, 0x40000100UL, 0x00080000UL,
+ 0x00000000UL, 0x40080000UL, 0x02080100UL, 0x40000100UL
+};
+
+static const u32 SP6[64] =
+{
+ 0x20000010UL, 0x20400000UL, 0x00004000UL, 0x20404010UL,
+ 0x20400000UL, 0x00000010UL, 0x20404010UL, 0x00400000UL,
+ 0x20004000UL, 0x00404010UL, 0x00400000UL, 0x20000010UL,
+ 0x00400010UL, 0x20004000UL, 0x20000000UL, 0x00004010UL,
+ 0x00000000UL, 0x00400010UL, 0x20004010UL, 0x00004000UL,
+ 0x00404000UL, 0x20004010UL, 0x00000010UL, 0x20400010UL,
+ 0x20400010UL, 0x00000000UL, 0x00404010UL, 0x20404000UL,
+ 0x00004010UL, 0x00404000UL, 0x20404000UL, 0x20000000UL,
+ 0x20004000UL, 0x00000010UL, 0x20400010UL, 0x00404000UL,
+ 0x20404010UL, 0x00400000UL, 0x00004010UL, 0x20000010UL,
+ 0x00400000UL, 0x20004000UL, 0x20000000UL, 0x00004010UL,
+ 0x20000010UL, 0x20404010UL, 0x00404000UL, 0x20400000UL,
+ 0x00404010UL, 0x20404000UL, 0x00000000UL, 0x20400010UL,
+ 0x00000010UL, 0x00004000UL, 0x20400000UL, 0x00404010UL,
+ 0x00004000UL, 0x00400010UL, 0x20004010UL, 0x00000000UL,
+ 0x20404000UL, 0x20000000UL, 0x00400010UL, 0x20004010UL
+};
+
+static const u32 SP7[64] =
+{
+ 0x00200000UL, 0x04200002UL, 0x04000802UL, 0x00000000UL,
+ 0x00000800UL, 0x04000802UL, 0x00200802UL, 0x04200800UL,
+ 0x04200802UL, 0x00200000UL, 0x00000000UL, 0x04000002UL,
+ 0x00000002UL, 0x04000000UL, 0x04200002UL, 0x00000802UL,
+ 0x04000800UL, 0x00200802UL, 0x00200002UL, 0x04000800UL,
+ 0x04000002UL, 0x04200000UL, 0x04200800UL, 0x00200002UL,
+ 0x04200000UL, 0x00000800UL, 0x00000802UL, 0x04200802UL,
+ 0x00200800UL, 0x00000002UL, 0x04000000UL, 0x00200800UL,
+ 0x04000000UL, 0x00200800UL, 0x00200000UL, 0x04000802UL,
+ 0x04000802UL, 0x04200002UL, 0x04200002UL, 0x00000002UL,
+ 0x00200002UL, 0x04000000UL, 0x04000800UL, 0x00200000UL,
+ 0x04200800UL, 0x00000802UL, 0x00200802UL, 0x04200800UL,
+ 0x00000802UL, 0x04000002UL, 0x04200802UL, 0x04200000UL,
+ 0x00200800UL, 0x00000000UL, 0x00000002UL, 0x04200802UL,
+ 0x00000000UL, 0x00200802UL, 0x04200000UL, 0x00000800UL,
+ 0x04000002UL, 0x04000800UL, 0x00000800UL, 0x00200002UL
+};
+
+static const u32 SP8[64] =
+{
+ 0x10001040UL, 0x00001000UL, 0x00040000UL, 0x10041040UL,
+ 0x10000000UL, 0x10001040UL, 0x00000040UL, 0x10000000UL,
+ 0x00040040UL, 0x10040000UL, 0x10041040UL, 0x00041000UL,
+ 0x10041000UL, 0x00041040UL, 0x00001000UL, 0x00000040UL,
+ 0x10040000UL, 0x10000040UL, 0x10001000UL, 0x00001040UL,
+ 0x00041000UL, 0x00040040UL, 0x10040040UL, 0x10041000UL,
+ 0x00001040UL, 0x00000000UL, 0x00000000UL, 0x10040040UL,
+ 0x10000040UL, 0x10001000UL, 0x00041040UL, 0x00040000UL,
+ 0x00041040UL, 0x00040000UL, 0x10041000UL, 0x00001000UL,
+ 0x00000040UL, 0x10040040UL, 0x00001000UL, 0x00041040UL,
+ 0x10001000UL, 0x00000040UL, 0x10000040UL, 0x10040000UL,
+ 0x10040040UL, 0x10000000UL, 0x00040000UL, 0x10001040UL,
+ 0x00000000UL, 0x10041040UL, 0x00040040UL, 0x10000040UL,
+ 0x10040000UL, 0x10001000UL, 0x10001040UL, 0x00000000UL,
+ 0x10041040UL, 0x00041000UL, 0x00041000UL, 0x00001040UL,
+ 0x00001040UL, 0x00040040UL, 0x10000000UL, 0x10041000UL
+};
+
+
+static void cookey(const u32 *raw1, u32 *keyout)
+{
+ u32 *cook;
+ const u32 *raw0;
+ u32 dough[32];
+ int i;
+
+ cook = dough;
+ for (i = 0; i < 16; i++, raw1++) {
+ raw0 = raw1++;
+ *cook = (*raw0 & 0x00fc0000L) << 6;
+ *cook |= (*raw0 & 0x00000fc0L) << 10;
+ *cook |= (*raw1 & 0x00fc0000L) >> 10;
+ *cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+ *cook = (*raw0 & 0x0003f000L) << 12;
+ *cook |= (*raw0 & 0x0000003fL) << 16;
+ *cook |= (*raw1 & 0x0003f000L) >> 4;
+ *cook++ |= (*raw1 & 0x0000003fL);
+ }
+
+ os_memcpy(keyout, dough, sizeof(dough));
+}
+
+
+static void deskey(const u8 *key, int decrypt, u32 *keyout)
+{
+ u32 i, j, l, m, n, kn[32];
+ u8 pc1m[56], pcr[56];
+
+ for (j = 0; j < 56; j++) {
+ l = (u32) pc1[j];
+ m = l & 7;
+ pc1m[j] = (u8)
+ ((key[l >> 3U] & bytebit[m]) == bytebit[m] ? 1 : 0);
+ }
+
+ for (i = 0; i < 16; i++) {
+ if (decrypt)
+ m = (15 - i) << 1;
+ else
+ m = i << 1;
+ n = m + 1;
+ kn[m] = kn[n] = 0L;
+ for (j = 0; j < 28; j++) {
+ l = j + (u32) totrot[i];
+ if (l < 28)
+ pcr[j] = pc1m[l];
+ else
+ pcr[j] = pc1m[l - 28];
+ }
+ for (/* j = 28 */; j < 56; j++) {
+ l = j + (u32) totrot[i];
+ if (l < 56)
+ pcr[j] = pc1m[l];
+ else
+ pcr[j] = pc1m[l - 28];
+ }
+ for (j = 0; j < 24; j++) {
+ if ((int) pcr[(int) pc2[j]] != 0)
+ kn[m] |= bigbyte[j];
+ if ((int) pcr[(int) pc2[j + 24]] != 0)
+ kn[n] |= bigbyte[j];
+ }
+ }
+
+ cookey(kn, keyout);
+}
+
+
+static void desfunc(u32 *block, const u32 *keys)
+{
+ u32 work, right, leftt;
+ int cur_round;
+
+ leftt = block[0];
+ right = block[1];
+
+ work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
+ right ^= work;
+ leftt ^= (work << 4);
+
+ work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+ right ^= work;
+ leftt ^= (work << 16);
+
+ work = ((right >> 2) ^ leftt) & 0x33333333L;
+ leftt ^= work;
+ right ^= (work << 2);
+
+ work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
+ leftt ^= work;
+ right ^= (work << 8);
+
+ right = ROLc(right, 1);
+ work = (leftt ^ right) & 0xaaaaaaaaL;
+
+ leftt ^= work;
+ right ^= work;
+ leftt = ROLc(leftt, 1);
+
+ for (cur_round = 0; cur_round < 8; cur_round++) {
+ work = RORc(right, 4) ^ *keys++;
+ leftt ^= SP7[work & 0x3fL]
+ ^ SP5[(work >> 8) & 0x3fL]
+ ^ SP3[(work >> 16) & 0x3fL]
+ ^ SP1[(work >> 24) & 0x3fL];
+ work = right ^ *keys++;
+ leftt ^= SP8[ work & 0x3fL]
+ ^ SP6[(work >> 8) & 0x3fL]
+ ^ SP4[(work >> 16) & 0x3fL]
+ ^ SP2[(work >> 24) & 0x3fL];
+
+ work = RORc(leftt, 4) ^ *keys++;
+ right ^= SP7[ work & 0x3fL]
+ ^ SP5[(work >> 8) & 0x3fL]
+ ^ SP3[(work >> 16) & 0x3fL]
+ ^ SP1[(work >> 24) & 0x3fL];
+ work = leftt ^ *keys++;
+ right ^= SP8[ work & 0x3fL]
+ ^ SP6[(work >> 8) & 0x3fL]
+ ^ SP4[(work >> 16) & 0x3fL]
+ ^ SP2[(work >> 24) & 0x3fL];
+ }
+
+ right = RORc(right, 1);
+ work = (leftt ^ right) & 0xaaaaaaaaL;
+ leftt ^= work;
+ right ^= work;
+ leftt = RORc(leftt, 1);
+ work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+ right ^= work;
+ leftt ^= (work << 8);
+ /* -- */
+ work = ((leftt >> 2) ^ right) & 0x33333333L;
+ right ^= work;
+ leftt ^= (work << 2);
+ work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+ leftt ^= work;
+ right ^= (work << 16);
+ work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+ leftt ^= work;
+ right ^= (work << 4);
+
+ block[0] = right;
+ block[1] = leftt;
+}
+
+
+/* wpa_supplicant/hostapd specific wrapper */
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+ u8 pkey[8], next, tmp;
+ int i;
+ u32 ek[32], work[2];
+
+ /* Add parity bits to the key */
+ next = 0;
+ for (i = 0; i < 7; i++) {
+ tmp = key[i];
+ pkey[i] = (tmp >> i) | next | 1;
+ next = tmp << (7 - i);
+ }
+ pkey[i] = next | 1;
+
+ deskey(pkey, 0, ek);
+
+ work[0] = WPA_GET_BE32(clear);
+ work[1] = WPA_GET_BE32(clear + 4);
+ desfunc(work, ek);
+ WPA_PUT_BE32(cypher, work[0]);
+ WPA_PUT_BE32(cypher + 4, work[1]);
+
+ os_memset(pkey, 0, sizeof(pkey));
+ os_memset(ek, 0, sizeof(ek));
+}
+
+
+void des_key_setup(const u8 *key, u32 *ek, u32 *dk)
+{
+ deskey(key, 0, ek);
+ deskey(key, 1, dk);
+}
+
+
+void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt)
+{
+ u32 work[2];
+ work[0] = WPA_GET_BE32(plain);
+ work[1] = WPA_GET_BE32(plain + 4);
+ desfunc(work, ek);
+ WPA_PUT_BE32(crypt, work[0]);
+ WPA_PUT_BE32(crypt + 4, work[1]);
+}
+
+
+void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain)
+{
+ u32 work[2];
+ work[0] = WPA_GET_BE32(crypt);
+ work[1] = WPA_GET_BE32(crypt + 4);
+ desfunc(work, dk);
+ WPA_PUT_BE32(plain, work[0]);
+ WPA_PUT_BE32(plain + 4, work[1]);
+}
+
+
+void des3_key_setup(const u8 *key, struct des3_key_s *dkey)
+{
+ deskey(key, 0, dkey->ek[0]);
+ deskey(key + 8, 1, dkey->ek[1]);
+ deskey(key + 16, 0, dkey->ek[2]);
+
+ deskey(key, 1, dkey->dk[2]);
+ deskey(key + 8, 0, dkey->dk[1]);
+ deskey(key + 16, 1, dkey->dk[0]);
+}
+
+
+void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt)
+{
+ u32 work[2];
+
+ work[0] = WPA_GET_BE32(plain);
+ work[1] = WPA_GET_BE32(plain + 4);
+ desfunc(work, key->ek[0]);
+ desfunc(work, key->ek[1]);
+ desfunc(work, key->ek[2]);
+ WPA_PUT_BE32(crypt, work[0]);
+ WPA_PUT_BE32(crypt + 4, work[1]);
+}
+
+
+void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain)
+{
+ u32 work[2];
+
+ work[0] = WPA_GET_BE32(crypt);
+ work[1] = WPA_GET_BE32(crypt + 4);
+ desfunc(work, key->dk[0]);
+ desfunc(work, key->dk[1]);
+ desfunc(work, key->dk[2]);
+ WPA_PUT_BE32(plain, work[0]);
+ WPA_PUT_BE32(plain + 4, work[1]);
+}
diff --git a/src/crypto/des_i.h b/src/crypto/des_i.h
new file mode 100644
index 0000000..6f27414
--- /dev/null
+++ b/src/crypto/des_i.h
@@ -0,0 +1,31 @@
+/*
+ * DES and 3DES-EDE ciphers
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef DES_I_H
+#define DES_I_H
+
+struct des3_key_s {
+ u32 ek[3][32];
+ u32 dk[3][32];
+};
+
+void des_key_setup(const u8 *key, u32 *ek, u32 *dk);
+void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt);
+void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain);
+
+void des3_key_setup(const u8 *key, struct des3_key_s *dkey);
+void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt);
+void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain);
+
+#endif /* DES_I_H */
diff --git a/src/crypto/dh_group5.c b/src/crypto/dh_group5.c
new file mode 100644
index 0000000..8c475bf
--- /dev/null
+++ b/src/crypto/dh_group5.c
@@ -0,0 +1,40 @@
+/*
+ * Diffie-Hellman group 5 operations
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "dh_groups.h"
+#include "dh_group5.h"
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+ *publ = dh_init(dh_groups_get(5), priv);
+ if (*publ == 0)
+ return NULL;
+ return (void *) 1;
+}
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+ const struct wpabuf *own_private)
+{
+ return dh_derive_shared(peer_public, own_private, dh_groups_get(5));
+}
+
+
+void dh5_free(void *ctx)
+{
+}
diff --git a/src/crypto/dh_group5.h b/src/crypto/dh_group5.h
new file mode 100644
index 0000000..595f111
--- /dev/null
+++ b/src/crypto/dh_group5.h
@@ -0,0 +1,23 @@
+/*
+ * Diffie-Hellman group 5 operations
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef DH_GROUP5_H
+#define DH_GROUP5_H
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ);
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+ const struct wpabuf *own_private);
+void dh5_free(void *ctx);
+
+#endif /* DH_GROUP5_H */
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
new file mode 100644
index 0000000..e5b7d4c
--- /dev/null
+++ b/src/crypto/dh_groups.c
@@ -0,0 +1,633 @@
+/*
+ * Diffie-Hellman groups
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "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
+};
+
+/* RFC 4306, B.2. Group 2 - 1024 Bit MODP
+ * Generator: 2
+ * Prime: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }
+ */
+static const u8 dh_group2_generator[1] = { 0x02 };
+static const u8 dh_group2_prime[128] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+#endif /* ALL_DH_GROUPS */
+
+/* RFC 3526, 2. Group 5 - 1536 Bit MODP
+ * Generator: 2
+ * Prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }
+ */
+static const u8 dh_group5_generator[1] = { 0x02 };
+static const u8 dh_group5_prime[192] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+ 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+ 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+ 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+ 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+#ifdef ALL_DH_GROUPS
+
+/* RFC 3526, 3. Group 14 - 2048 Bit MODP
+ * Generator: 2
+ * Prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
+ */
+static const u8 dh_group14_generator[1] = { 0x02 };
+static const u8 dh_group14_prime[256] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+ 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+ 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+ 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+ 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+ 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+ 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+ 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+ 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+ 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 4. Group 15 - 3072 Bit MODP
+ * Generator: 2
+ * Prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }
+ */
+static const u8 dh_group15_generator[1] = { 0x02 };
+static const u8 dh_group15_prime[384] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+ 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+ 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+ 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+ 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+ 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+ 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+ 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+ 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+ 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+ 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+ 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+ 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+ 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+ 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+ 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+ 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+ 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+ 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+ 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+ 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+ 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+ 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+ 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+ 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+ 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 5. Group 16 - 4096 Bit MODP
+ * Generator: 2
+ * Prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
+ */
+static const u8 dh_group16_generator[1] = { 0x02 };
+static const u8 dh_group16_prime[512] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+ 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+ 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+ 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+ 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+ 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+ 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+ 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+ 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+ 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+ 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+ 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+ 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+ 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+ 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+ 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+ 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+ 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+ 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+ 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+ 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+ 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+ 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+ 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+ 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+ 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+ 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+ 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+ 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+ 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+ 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+ 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+ 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+ 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+ 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+ 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+ 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+ 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+ 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+ 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+ 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+ 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 6. Group 17 - 6144 Bit MODP
+ * Generator: 2
+ * Prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }
+ */
+static const u8 dh_group17_generator[1] = { 0x02 };
+static const u8 dh_group17_prime[768] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+ 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+ 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+ 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+ 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+ 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+ 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+ 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+ 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+ 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+ 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+ 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+ 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+ 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+ 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+ 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+ 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+ 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+ 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+ 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+ 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+ 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+ 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+ 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+ 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+ 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+ 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+ 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+ 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+ 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+ 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+ 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+ 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+ 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+ 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+ 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+ 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+ 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+ 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+ 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+ 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+ 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+ 0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
+ 0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
+ 0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+ 0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
+ 0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
+ 0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+ 0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
+ 0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
+ 0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+ 0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
+ 0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
+ 0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+ 0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
+ 0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
+ 0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+ 0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
+ 0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
+ 0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+ 0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
+ 0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
+ 0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+ 0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
+ 0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
+ 0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+ 0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
+ 0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
+ 0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+ 0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
+ 0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
+ 0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+ 0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
+ 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 7. Group 18 - 8192 Bit MODP
+ * Generator: 2
+ * Prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }
+ */
+static const u8 dh_group18_generator[1] = { 0x02 };
+static const u8 dh_group18_prime[1024] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+ 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+ 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+ 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+ 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+ 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+ 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+ 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+ 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+ 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+ 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+ 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+ 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+ 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+ 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+ 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+ 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+ 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+ 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+ 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+ 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+ 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+ 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+ 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+ 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+ 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+ 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+ 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+ 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+ 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+ 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+ 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+ 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+ 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+ 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+ 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+ 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+ 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+ 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+ 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+ 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+ 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+ 0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
+ 0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
+ 0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+ 0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
+ 0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
+ 0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+ 0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
+ 0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
+ 0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+ 0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
+ 0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
+ 0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+ 0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
+ 0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
+ 0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+ 0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
+ 0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
+ 0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+ 0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
+ 0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
+ 0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+ 0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
+ 0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
+ 0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+ 0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
+ 0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
+ 0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+ 0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
+ 0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
+ 0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+ 0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
+ 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xBE, 0x11, 0x59,
+ 0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4,
+ 0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C,
+ 0xD8, 0xBE, 0xC4, 0xD0, 0x73, 0xB9, 0x31, 0xBA,
+ 0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00,
+ 0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED,
+ 0x25, 0x76, 0xF6, 0x93, 0x6B, 0xA4, 0x24, 0x66,
+ 0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68,
+ 0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78,
+ 0x23, 0x8F, 0x16, 0xCB, 0xE3, 0x9D, 0x65, 0x2D,
+ 0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9,
+ 0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07,
+ 0x13, 0xEB, 0x57, 0xA8, 0x1A, 0x23, 0xF0, 0xC7,
+ 0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B,
+ 0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD,
+ 0xFA, 0x9D, 0x4B, 0x7F, 0xA2, 0xC0, 0x87, 0xE8,
+ 0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A,
+ 0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6,
+ 0x6D, 0x2A, 0x13, 0xF8, 0x3F, 0x44, 0xF8, 0x2D,
+ 0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36,
+ 0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1,
+ 0x64, 0xF3, 0x1C, 0xC5, 0x08, 0x46, 0x85, 0x1D,
+ 0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1,
+ 0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73,
+ 0xFA, 0xF3, 0x6B, 0xC3, 0x1E, 0xCF, 0xA2, 0x68,
+ 0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92,
+ 0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7,
+ 0x88, 0x9A, 0x00, 0x2E, 0xD5, 0xEE, 0x38, 0x2B,
+ 0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47,
+ 0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA,
+ 0x9E, 0x30, 0x50, 0xE2, 0x76, 0x56, 0x94, 0xDF,
+ 0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71,
+ 0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+#endif /* ALL_DH_GROUPS */
+
+
+#define DH_GROUP(id) \
+{ id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \
+dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime) }
+
+
+static struct dh_group dh_groups[] = {
+ DH_GROUP(5),
+#ifdef ALL_DH_GROUPS
+ DH_GROUP(1),
+ DH_GROUP(2),
+ DH_GROUP(14),
+ DH_GROUP(15),
+ DH_GROUP(16),
+ DH_GROUP(17),
+ DH_GROUP(18)
+#endif /* ALL_DH_GROUPS */
+};
+
+#define NUM_DH_GROUPS (sizeof(dh_groups) / sizeof(dh_groups[0]))
+
+
+const struct dh_group * dh_groups_get(int id)
+{
+ size_t i;
+
+ for (i = 0; i < NUM_DH_GROUPS; i++) {
+ if (dh_groups[i].id == id)
+ return &dh_groups[i];
+ }
+ return NULL;
+}
+
+
+/**
+ * dh_init - Initialize Diffie-Hellman handshake
+ * @dh: Selected Diffie-Hellman group
+ * @priv: Pointer for returning Diffie-Hellman private key
+ * Returns: Diffie-Hellman public value
+ */
+struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
+{
+ struct wpabuf *pv;
+ size_t pv_len;
+
+ if (dh == NULL)
+ return NULL;
+
+ wpabuf_free(*priv);
+ *priv = wpabuf_alloc(dh->prime_len);
+ if (*priv == NULL)
+ return NULL;
+
+ if (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/src/crypto/dh_groups.h b/src/crypto/dh_groups.h
new file mode 100644
index 0000000..5c61539
--- /dev/null
+++ b/src/crypto/dh_groups.h
@@ -0,0 +1,32 @@
+/*
+ * Diffie-Hellman groups
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef DH_GROUPS_H
+#define DH_GROUPS_H
+
+struct dh_group {
+ int id;
+ const u8 *generator;
+ size_t generator_len;
+ const u8 *prime;
+ size_t prime_len;
+};
+
+const struct dh_group * dh_groups_get(int id);
+struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv);
+struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
+ const struct wpabuf *own_private,
+ const struct dh_group *dh);
+
+#endif /* DH_GROUPS_H */
diff --git a/src/crypto/fips_prf_cryptoapi.c b/src/crypto/fips_prf_cryptoapi.c
new file mode 100644
index 0000000..17d3116
--- /dev/null
+++ b/src/crypto/fips_prf_cryptoapi.c
@@ -0,0 +1,25 @@
+/*
+ * FIPS 186-2 PRF for Microsoft CryptoAPI
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+ /* FIX: how to do this with CryptoAPI? */
+ return -1;
+}
diff --git a/src/crypto/fips_prf_gnutls.c b/src/crypto/fips_prf_gnutls.c
new file mode 100644
index 0000000..f742e98
--- /dev/null
+++ b/src/crypto/fips_prf_gnutls.c
@@ -0,0 +1,26 @@
+/*
+ * FIPS 186-2 PRF for libgcrypt
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <gcrypt.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+ /* FIX: how to do this with libgcrypt? */
+ return -1;
+}
diff --git a/src/crypto/fips_prf_internal.c b/src/crypto/fips_prf_internal.c
new file mode 100644
index 0000000..a85cb14
--- /dev/null
+++ b/src/crypto/fips_prf_internal.c
@@ -0,0 +1,74 @@
+/*
+ * FIPS 186-2 PRF for internal crypto implementation
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "sha1_i.h"
+#include "crypto.h"
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+ u8 xkey[64];
+ u32 t[5], _t[5];
+ int i, j, m, k;
+ u8 *xpos = x;
+ u32 carry;
+
+ if (seed_len > sizeof(xkey))
+ seed_len = sizeof(xkey);
+
+ /* FIPS 186-2 + change notice 1 */
+
+ os_memcpy(xkey, seed, seed_len);
+ os_memset(xkey + seed_len, 0, 64 - seed_len);
+ t[0] = 0x67452301;
+ t[1] = 0xEFCDAB89;
+ t[2] = 0x98BADCFE;
+ t[3] = 0x10325476;
+ t[4] = 0xC3D2E1F0;
+
+ m = xlen / 40;
+ for (j = 0; j < m; j++) {
+ /* XSEED_j = 0 */
+ for (i = 0; i < 2; i++) {
+ /* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+ /* w_i = G(t, XVAL) */
+ os_memcpy(_t, t, 20);
+ SHA1Transform(_t, xkey);
+ _t[0] = host_to_be32(_t[0]);
+ _t[1] = host_to_be32(_t[1]);
+ _t[2] = host_to_be32(_t[2]);
+ _t[3] = host_to_be32(_t[3]);
+ _t[4] = host_to_be32(_t[4]);
+ os_memcpy(xpos, _t, 20);
+
+ /* XKEY = (1 + XKEY + w_i) mod 2^b */
+ carry = 1;
+ for (k = 19; k >= 0; k--) {
+ carry += xkey[k] + xpos[k];
+ xkey[k] = carry & 0xff;
+ carry >>= 8;
+ }
+
+ xpos += SHA1_MAC_LEN;
+ }
+ /* x_j = w_0|w_1 */
+ }
+
+ return 0;
+}
diff --git a/src/crypto/fips_prf_nss.c b/src/crypto/fips_prf_nss.c
new file mode 100644
index 0000000..f941983
--- /dev/null
+++ b/src/crypto/fips_prf_nss.c
@@ -0,0 +1,25 @@
+/*
+ * FIPS 186-2 PRF for NSS
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <openssl/sha.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+ return -1;
+}
diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c
new file mode 100644
index 0000000..d0af983
--- /dev/null
+++ b/src/crypto/fips_prf_openssl.c
@@ -0,0 +1,83 @@
+/*
+ * FIPS 186-2 PRF for libcrypto
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <openssl/sha.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+static void sha1_transform(u8 *state, const u8 data[64])
+{
+ SHA_CTX context;
+ os_memset(&context, 0, sizeof(context));
+ os_memcpy(&context.h0, state, 5 * 4);
+ SHA1_Transform(&context, data);
+ os_memcpy(state, &context.h0, 5 * 4);
+}
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+ u8 xkey[64];
+ u32 t[5], _t[5];
+ int i, j, m, k;
+ u8 *xpos = x;
+ u32 carry;
+
+ if (seed_len > sizeof(xkey))
+ seed_len = sizeof(xkey);
+
+ /* FIPS 186-2 + change notice 1 */
+
+ os_memcpy(xkey, seed, seed_len);
+ os_memset(xkey + seed_len, 0, 64 - seed_len);
+ t[0] = 0x67452301;
+ t[1] = 0xEFCDAB89;
+ t[2] = 0x98BADCFE;
+ t[3] = 0x10325476;
+ t[4] = 0xC3D2E1F0;
+
+ m = xlen / 40;
+ for (j = 0; j < m; j++) {
+ /* XSEED_j = 0 */
+ for (i = 0; i < 2; i++) {
+ /* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+ /* w_i = G(t, XVAL) */
+ os_memcpy(_t, t, 20);
+ sha1_transform((u8 *) _t, xkey);
+ _t[0] = host_to_be32(_t[0]);
+ _t[1] = host_to_be32(_t[1]);
+ _t[2] = host_to_be32(_t[2]);
+ _t[3] = host_to_be32(_t[3]);
+ _t[4] = host_to_be32(_t[4]);
+ os_memcpy(xpos, _t, 20);
+
+ /* XKEY = (1 + XKEY + w_i) mod 2^b */
+ carry = 1;
+ for (k = 19; k >= 0; k--) {
+ carry += xkey[k] + xpos[k];
+ xkey[k] = carry & 0xff;
+ carry >>= 8;
+ }
+
+ xpos += 20;
+ }
+ /* x_j = w_0|w_1 */
+ }
+
+ return 0;
+}
diff --git a/src/crypto/md4-internal.c b/src/crypto/md4-internal.c
new file mode 100644
index 0000000..d9f499f
--- /dev/null
+++ b/src/crypto/md4-internal.c
@@ -0,0 +1,278 @@
+/*
+ * MD4 hash implementation
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+#define MD4_BLOCK_LENGTH 64
+#define MD4_DIGEST_LENGTH 16
+
+typedef struct MD4Context {
+ u32 state[4]; /* state */
+ u64 count; /* number of bits, mod 2^64 */
+ u8 buffer[MD4_BLOCK_LENGTH]; /* input buffer */
+} MD4_CTX;
+
+
+static void MD4Init(MD4_CTX *ctx);
+static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len);
+static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx);
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ MD4_CTX ctx;
+ size_t i;
+
+ MD4Init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ MD4Update(&ctx, addr[i], len[i]);
+ MD4Final(mac, &ctx);
+ return 0;
+}
+
+
+/* ===== start - public domain MD4 implementation ===== */
+/* $OpenBSD: md4.c,v 1.7 2005/08/08 08:05:35 espie Exp $ */
+
+/*
+ * This code implements the MD4 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ * Todd C. Miller modified the MD5 code to do MD4 based on RFC 1186.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD4Context structure, pass it to MD4Init, call MD4Update as
+ * needed on buffers full of bytes, and then call MD4Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#define MD4_DIGEST_STRING_LENGTH (MD4_DIGEST_LENGTH * 2 + 1)
+
+
+static void
+MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]);
+
+#define PUT_64BIT_LE(cp, value) do { \
+ (cp)[7] = (value) >> 56; \
+ (cp)[6] = (value) >> 48; \
+ (cp)[5] = (value) >> 40; \
+ (cp)[4] = (value) >> 32; \
+ (cp)[3] = (value) >> 24; \
+ (cp)[2] = (value) >> 16; \
+ (cp)[1] = (value) >> 8; \
+ (cp)[0] = (value); } while (0)
+
+#define PUT_32BIT_LE(cp, value) do { \
+ (cp)[3] = (value) >> 24; \
+ (cp)[2] = (value) >> 16; \
+ (cp)[1] = (value) >> 8; \
+ (cp)[0] = (value); } while (0)
+
+static u8 PADDING[MD4_BLOCK_LENGTH] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * Start MD4 accumulation.
+ * Set bit count to 0 and buffer to mysterious initialization constants.
+ */
+static void MD4Init(MD4_CTX *ctx)
+{
+ ctx->count = 0;
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xefcdab89;
+ ctx->state[2] = 0x98badcfe;
+ ctx->state[3] = 0x10325476;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len)
+{
+ size_t have, need;
+
+ /* Check how many bytes we already have and how many more we need. */
+ have = (size_t)((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1));
+ need = MD4_BLOCK_LENGTH - have;
+
+ /* Update bitcount */
+ ctx->count += (u64)len << 3;
+
+ if (len >= need) {
+ if (have != 0) {
+ os_memcpy(ctx->buffer + have, input, need);
+ MD4Transform(ctx->state, ctx->buffer);
+ input += need;
+ len -= need;
+ have = 0;
+ }
+
+ /* Process data in MD4_BLOCK_LENGTH-byte chunks. */
+ while (len >= MD4_BLOCK_LENGTH) {
+ MD4Transform(ctx->state, input);
+ input += MD4_BLOCK_LENGTH;
+ len -= MD4_BLOCK_LENGTH;
+ }
+ }
+
+ /* Handle any remaining bytes of data. */
+ if (len != 0)
+ os_memcpy(ctx->buffer + have, input, len);
+}
+
+/*
+ * Pad pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void MD4Pad(MD4_CTX *ctx)
+{
+ u8 count[8];
+ size_t padlen;
+
+ /* Convert count to 8 bytes in little endian order. */
+ PUT_64BIT_LE(count, ctx->count);
+
+ /* Pad out to 56 mod 64. */
+ padlen = MD4_BLOCK_LENGTH -
+ ((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1));
+ if (padlen < 1 + 8)
+ padlen += MD4_BLOCK_LENGTH;
+ MD4Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */
+ MD4Update(ctx, count, 8);
+}
+
+/*
+ * Final wrapup--call MD4Pad, fill in digest and zero out ctx.
+ */
+static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx)
+{
+ int i;
+
+ MD4Pad(ctx);
+ if (digest != NULL) {
+ for (i = 0; i < 4; i++)
+ PUT_32BIT_LE(digest + i * 4, ctx->state[i]);
+ os_memset(ctx, 0, sizeof(*ctx));
+ }
+}
+
+
+/* The three core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) ((x & y) | (x & z) | (y & z))
+#define F3(x, y, z) (x ^ y ^ z)
+
+/* This is the central step in the MD4 algorithm. */
+#define MD4STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s) )
+
+/*
+ * The core of the MD4 algorithm, this alters an existing MD4 hash to
+ * reflect the addition of 16 longwords of new data. MD4Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH])
+{
+ u32 a, b, c, d, in[MD4_BLOCK_LENGTH / 4];
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ os_memcpy(in, block, sizeof(in));
+#else
+ for (a = 0; a < MD4_BLOCK_LENGTH / 4; a++) {
+ in[a] = (u32)(
+ (u32)(block[a * 4 + 0]) |
+ (u32)(block[a * 4 + 1]) << 8 |
+ (u32)(block[a * 4 + 2]) << 16 |
+ (u32)(block[a * 4 + 3]) << 24);
+ }
+#endif
+
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+
+ MD4STEP(F1, a, b, c, d, in[ 0], 3);
+ MD4STEP(F1, d, a, b, c, in[ 1], 7);
+ MD4STEP(F1, c, d, a, b, in[ 2], 11);
+ MD4STEP(F1, b, c, d, a, in[ 3], 19);
+ MD4STEP(F1, a, b, c, d, in[ 4], 3);
+ MD4STEP(F1, d, a, b, c, in[ 5], 7);
+ MD4STEP(F1, c, d, a, b, in[ 6], 11);
+ MD4STEP(F1, b, c, d, a, in[ 7], 19);
+ MD4STEP(F1, a, b, c, d, in[ 8], 3);
+ MD4STEP(F1, d, a, b, c, in[ 9], 7);
+ MD4STEP(F1, c, d, a, b, in[10], 11);
+ MD4STEP(F1, b, c, d, a, in[11], 19);
+ MD4STEP(F1, a, b, c, d, in[12], 3);
+ MD4STEP(F1, d, a, b, c, in[13], 7);
+ MD4STEP(F1, c, d, a, b, in[14], 11);
+ MD4STEP(F1, b, c, d, a, in[15], 19);
+
+ MD4STEP(F2, a, b, c, d, in[ 0] + 0x5a827999, 3);
+ MD4STEP(F2, d, a, b, c, in[ 4] + 0x5a827999, 5);
+ MD4STEP(F2, c, d, a, b, in[ 8] + 0x5a827999, 9);
+ MD4STEP(F2, b, c, d, a, in[12] + 0x5a827999, 13);
+ MD4STEP(F2, a, b, c, d, in[ 1] + 0x5a827999, 3);
+ MD4STEP(F2, d, a, b, c, in[ 5] + 0x5a827999, 5);
+ MD4STEP(F2, c, d, a, b, in[ 9] + 0x5a827999, 9);
+ MD4STEP(F2, b, c, d, a, in[13] + 0x5a827999, 13);
+ MD4STEP(F2, a, b, c, d, in[ 2] + 0x5a827999, 3);
+ MD4STEP(F2, d, a, b, c, in[ 6] + 0x5a827999, 5);
+ MD4STEP(F2, c, d, a, b, in[10] + 0x5a827999, 9);
+ MD4STEP(F2, b, c, d, a, in[14] + 0x5a827999, 13);
+ MD4STEP(F2, a, b, c, d, in[ 3] + 0x5a827999, 3);
+ MD4STEP(F2, d, a, b, c, in[ 7] + 0x5a827999, 5);
+ MD4STEP(F2, c, d, a, b, in[11] + 0x5a827999, 9);
+ MD4STEP(F2, b, c, d, a, in[15] + 0x5a827999, 13);
+
+ MD4STEP(F3, a, b, c, d, in[ 0] + 0x6ed9eba1, 3);
+ MD4STEP(F3, d, a, b, c, in[ 8] + 0x6ed9eba1, 9);
+ MD4STEP(F3, c, d, a, b, in[ 4] + 0x6ed9eba1, 11);
+ MD4STEP(F3, b, c, d, a, in[12] + 0x6ed9eba1, 15);
+ MD4STEP(F3, a, b, c, d, in[ 2] + 0x6ed9eba1, 3);
+ MD4STEP(F3, d, a, b, c, in[10] + 0x6ed9eba1, 9);
+ MD4STEP(F3, c, d, a, b, in[ 6] + 0x6ed9eba1, 11);
+ MD4STEP(F3, b, c, d, a, in[14] + 0x6ed9eba1, 15);
+ MD4STEP(F3, a, b, c, d, in[ 1] + 0x6ed9eba1, 3);
+ MD4STEP(F3, d, a, b, c, in[ 9] + 0x6ed9eba1, 9);
+ MD4STEP(F3, c, d, a, b, in[ 5] + 0x6ed9eba1, 11);
+ MD4STEP(F3, b, c, d, a, in[13] + 0x6ed9eba1, 15);
+ MD4STEP(F3, a, b, c, d, in[ 3] + 0x6ed9eba1, 3);
+ MD4STEP(F3, d, a, b, c, in[11] + 0x6ed9eba1, 9);
+ MD4STEP(F3, c, d, a, b, in[ 7] + 0x6ed9eba1, 11);
+ MD4STEP(F3, b, c, d, a, in[15] + 0x6ed9eba1, 15);
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+}
+/* ===== end - public domain MD4 implementation ===== */
diff --git a/src/crypto/md5-internal.c b/src/crypto/md5-internal.c
new file mode 100644
index 0000000..f8692a9
--- /dev/null
+++ b/src/crypto/md5-internal.c
@@ -0,0 +1,293 @@
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "md5.h"
+#include "md5_i.h"
+#include "crypto.h"
+
+
+static void MD5Transform(u32 buf[4], u32 const in[16]);
+
+
+typedef struct MD5Context MD5_CTX;
+
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ MD5_CTX ctx;
+ size_t i;
+
+ MD5Init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ MD5Update(&ctx, addr[i], len[i]);
+ MD5Final(mac, &ctx);
+ return 0;
+}
+
+
+/* ===== start - public domain MD5 implementation ===== */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifndef WORDS_BIGENDIAN
+#define byteReverse(buf, len) /* Nothing */
+#else
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+ u32 t;
+ do {
+ t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(u32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ u32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((u32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ os_memcpy(p, buf, len);
+ return;
+ }
+ os_memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (u32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ os_memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (u32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ os_memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ os_memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (u32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ os_memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ os_memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((u32 *) ctx->in)[14] = ctx->bits[0];
+ ((u32 *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (u32 *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ os_memcpy(digest, ctx->buf, 16);
+ os_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(u32 buf[4], u32 const in[16])
+{
+ register u32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+/* ===== end - public domain MD5 implementation ===== */
diff --git a/src/crypto/md5-non-fips.c b/src/crypto/md5-non-fips.c
new file mode 100644
index 0000000..6f29201
--- /dev/null
+++ b/src/crypto/md5-non-fips.c
@@ -0,0 +1,113 @@
+/*
+ * MD5 hash implementation and interface functions (non-FIPS allowed cases)
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "md5.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_md5_vector_non_fips_allow - HMAC-MD5 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[],
+ const size_t *len, u8 *mac)
+{
+ u8 k_pad[64]; /* padding - key XORd with ipad/opad */
+ u8 tk[16];
+ const u8 *_addr[6];
+ size_t i, _len[6];
+
+ if (num_elem > 5) {
+ /*
+ * Fixed limit on the number of fragments to avoid having to
+ * allocate memory (which could fail).
+ */
+ return -1;
+ }
+
+ /* if key is longer than 64 bytes reset it to key = MD5(key) */
+ if (key_len > 64) {
+ if (md5_vector_non_fips_allow(1, &key, &key_len, tk))
+ return -1;
+ key = tk;
+ key_len = 16;
+ }
+
+ /* the HMAC_MD5 transform looks like:
+ *
+ * MD5(K XOR opad, MD5(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected */
+
+ /* start out by storing key in ipad */
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+
+ /* XOR key with ipad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x36;
+
+ /* perform inner MD5 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ for (i = 0; i < num_elem; i++) {
+ _addr[i + 1] = addr[i];
+ _len[i + 1] = len[i];
+ }
+ if (md5_vector_non_fips_allow(1 + num_elem, _addr, _len, mac))
+ return -1;
+
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with opad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x5c;
+
+ /* perform outer MD5 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ _addr[1] = mac;
+ _len[1] = MD5_MAC_LEN;
+ return md5_vector_non_fips_allow(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_md5_non_fips_allow - HMAC-MD5 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_md5_vector_non_fips_allow(key, key_len, 1, &data,
+ &data_len, mac);
+}
diff --git a/src/crypto/md5.c b/src/crypto/md5.c
new file mode 100644
index 0000000..7f14e9b
--- /dev/null
+++ b/src/crypto/md5.c
@@ -0,0 +1,111 @@
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "md5.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ u8 k_pad[64]; /* padding - key XORd with ipad/opad */
+ u8 tk[16];
+ const u8 *_addr[6];
+ size_t i, _len[6];
+
+ if (num_elem > 5) {
+ /*
+ * Fixed limit on the number of fragments to avoid having to
+ * allocate memory (which could fail).
+ */
+ return -1;
+ }
+
+ /* if key is longer than 64 bytes reset it to key = MD5(key) */
+ if (key_len > 64) {
+ if (md5_vector(1, &key, &key_len, tk))
+ return -1;
+ key = tk;
+ key_len = 16;
+ }
+
+ /* the HMAC_MD5 transform looks like:
+ *
+ * MD5(K XOR opad, MD5(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected */
+
+ /* start out by storing key in ipad */
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+
+ /* XOR key with ipad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x36;
+
+ /* perform inner MD5 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ for (i = 0; i < num_elem; i++) {
+ _addr[i + 1] = addr[i];
+ _len[i + 1] = len[i];
+ }
+ if (md5_vector(1 + num_elem, _addr, _len, mac))
+ return -1;
+
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with opad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x5c;
+
+ /* perform outer MD5 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ _addr[1] = mac;
+ _len[1] = MD5_MAC_LEN;
+ return md5_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/src/crypto/md5.h b/src/crypto/md5.h
new file mode 100644
index 0000000..8952590
--- /dev/null
+++ b/src/crypto/md5.h
@@ -0,0 +1,35 @@
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#define MD5_MAC_LEN 16
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac);
+#ifdef CONFIG_FIPS
+int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[],
+ const size_t *len, u8 *mac);
+int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac);
+#else /* CONFIG_FIPS */
+#define hmac_md5_vector_non_fips_allow hmac_md5_vector
+#define hmac_md5_non_fips_allow hmac_md5
+#endif /* CONFIG_FIPS */
+
+#endif /* MD5_H */
diff --git a/src/crypto/md5_i.h b/src/crypto/md5_i.h
new file mode 100644
index 0000000..b7f6596
--- /dev/null
+++ b/src/crypto/md5_i.h
@@ -0,0 +1,29 @@
+/*
+ * MD5 internal definitions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef MD5_I_H
+#define MD5_I_H
+
+struct MD5Context {
+ u32 buf[4];
+ u32 bits[2];
+ u8 in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+ unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+
+#endif /* MD5_I_H */
diff --git a/src/crypto/milenage.c b/src/crypto/milenage.c
new file mode 100644
index 0000000..cf0c60e
--- /dev/null
+++ b/src/crypto/milenage.c
@@ -0,0 +1,329 @@
+/*
+ * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
+ * Copyright (c) 2006-2007 <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file implements an example authentication algorithm defined for 3GPP
+ * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow
+ * EAP-AKA to be tested properly with real USIM cards.
+ *
+ * This implementations assumes that the r1..r5 and c1..c5 constants defined in
+ * TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00,
+ * c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to
+ * be AES (Rijndael).
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "milenage.h"
+
+
+/**
+ * milenage_f1 - Milenage f1 and f1* algorithms
+ * @opc: OPc = 128-bit value derived from OP and K
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sqn: SQN = 48-bit sequence number
+ * @amf: AMF = 16-bit authentication management field
+ * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL
+ * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL
+ * Returns: 0 on success, -1 on failure
+ */
+int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand,
+ const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s)
+{
+ u8 tmp1[16], tmp2[16], tmp3[16];
+ int i;
+
+ /* tmp1 = TEMP = E_K(RAND XOR OP_C) */
+ for (i = 0; i < 16; i++)
+ tmp1[i] = _rand[i] ^ opc[i];
+ if (aes_128_encrypt_block(k, tmp1, tmp1))
+ return -1;
+
+ /* tmp2 = IN1 = SQN || AMF || SQN || AMF */
+ os_memcpy(tmp2, sqn, 6);
+ os_memcpy(tmp2 + 6, amf, 2);
+ os_memcpy(tmp2 + 8, tmp2, 8);
+
+ /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */
+
+ /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */
+ for (i = 0; i < 16; i++)
+ tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i];
+ /* XOR with TEMP = E_K(RAND XOR OP_C) */
+ for (i = 0; i < 16; i++)
+ tmp3[i] ^= tmp1[i];
+ /* XOR with c1 (= ..00, i.e., NOP) */
+
+ /* f1 || f1* = E_K(tmp3) XOR OP_c */
+ if (aes_128_encrypt_block(k, tmp3, tmp1))
+ return -1;
+ for (i = 0; i < 16; i++)
+ tmp1[i] ^= opc[i];
+ if (mac_a)
+ os_memcpy(mac_a, tmp1, 8); /* f1 */
+ if (mac_s)
+ os_memcpy(mac_s, tmp1 + 8, 8); /* f1* */
+ return 0;
+}
+
+
+/**
+ * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms
+ * @opc: OPc = 128-bit value derived from OP and K
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL
+ * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL
+ * Returns: 0 on success, -1 on failure
+ */
+int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
+ u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar)
+{
+ u8 tmp1[16], tmp2[16], tmp3[16];
+ int i;
+
+ /* tmp2 = TEMP = E_K(RAND XOR OP_C) */
+ for (i = 0; i < 16; i++)
+ tmp1[i] = _rand[i] ^ opc[i];
+ if (aes_128_encrypt_block(k, tmp1, tmp2))
+ return -1;
+
+ /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */
+ /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */
+ /* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */
+ /* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */
+
+ /* f2 and f5 */
+ /* rotate by r2 (= 0, i.e., NOP) */
+ for (i = 0; i < 16; i++)
+ tmp1[i] = tmp2[i] ^ opc[i];
+ tmp1[15] ^= 1; /* XOR c2 (= ..01) */
+ /* f5 || f2 = E_K(tmp1) XOR OP_c */
+ if (aes_128_encrypt_block(k, tmp1, tmp3))
+ return -1;
+ for (i = 0; i < 16; i++)
+ tmp3[i] ^= opc[i];
+ if (res)
+ os_memcpy(res, tmp3 + 8, 8); /* f2 */
+ if (ak)
+ os_memcpy(ak, tmp3, 6); /* f5 */
+
+ /* f3 */
+ if (ck) {
+ /* rotate by r3 = 0x20 = 4 bytes */
+ for (i = 0; i < 16; i++)
+ tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i];
+ tmp1[15] ^= 2; /* XOR c3 (= ..02) */
+ if (aes_128_encrypt_block(k, tmp1, ck))
+ return -1;
+ for (i = 0; i < 16; i++)
+ ck[i] ^= opc[i];
+ }
+
+ /* f4 */
+ if (ik) {
+ /* rotate by r4 = 0x40 = 8 bytes */
+ for (i = 0; i < 16; i++)
+ tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i];
+ tmp1[15] ^= 4; /* XOR c4 (= ..04) */
+ if (aes_128_encrypt_block(k, tmp1, ik))
+ return -1;
+ for (i = 0; i < 16; i++)
+ ik[i] ^= opc[i];
+ }
+
+ /* f5* */
+ if (akstar) {
+ /* rotate by r5 = 0x60 = 12 bytes */
+ for (i = 0; i < 16; i++)
+ tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i];
+ tmp1[15] ^= 8; /* XOR c5 (= ..08) */
+ if (aes_128_encrypt_block(k, tmp1, tmp1))
+ return -1;
+ for (i = 0; i < 6; i++)
+ akstar[i] = tmp1[i] ^ opc[i];
+ }
+
+ return 0;
+}
+
+
+/**
+ * milenage_generate - Generate AKA AUTN,IK,CK,RES
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @amf: AMF = 16-bit authentication management field
+ * @k: K = 128-bit subscriber key
+ * @sqn: SQN = 48-bit sequence number
+ * @_rand: RAND = 128-bit random challenge
+ * @autn: Buffer for AUTN = 128-bit authentication token
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @res_len: Max length for res; set to used length or 0 on failure
+ */
+void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k,
+ const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik,
+ u8 *ck, u8 *res, size_t *res_len)
+{
+ int i;
+ u8 mac_a[8], ak[6];
+
+ if (*res_len < 8) {
+ *res_len = 0;
+ return;
+ }
+ if (milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) ||
+ milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) {
+ *res_len = 0;
+ return;
+ }
+ *res_len = 8;
+
+ /* AUTN = (SQN ^ AK) || AMF || MAC */
+ for (i = 0; i < 6; i++)
+ autn[i] = sqn[i] ^ ak[i];
+ os_memcpy(autn + 6, amf, 2);
+ os_memcpy(autn + 8, mac_a, 8);
+}
+
+
+/**
+ * milenage_auts - Milenage AUTS validation
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @auts: AUTS = 112-bit authentication token from client
+ * @sqn: Buffer for SQN = 48-bit sequence number
+ * Returns: 0 = success (sqn filled), -1 on failure
+ */
+int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
+ u8 *sqn)
+{
+ u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+ u8 ak[6], mac_s[8];
+ int i;
+
+ if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
+ return -1;
+ for (i = 0; i < 6; i++)
+ sqn[i] = auts[i] ^ ak[i];
+ if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) ||
+ memcmp(mac_s, auts + 6, 8) != 0)
+ return -1;
+ return 0;
+}
+
+
+/**
+ * gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sres: Buffer for SRES = 32-bit SRES
+ * @kc: Buffer for Kc = 64-bit Kc
+ * Returns: 0 on success, -1 on failure
+ */
+int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc)
+{
+ u8 res[8], ck[16], ik[16];
+ int i;
+
+ if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL))
+ return -1;
+
+ for (i = 0; i < 8; i++)
+ kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8];
+
+#ifdef GSM_MILENAGE_ALT_SRES
+ os_memcpy(sres, res, 4);
+#else /* GSM_MILENAGE_ALT_SRES */
+ for (i = 0; i < 4; i++)
+ sres[i] = res[i] ^ res[i + 4];
+#endif /* GSM_MILENAGE_ALT_SRES */
+ return 0;
+}
+
+
+/**
+ * milenage_generate - Generate AKA AUTN,IK,CK,RES
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @sqn: SQN = 48-bit sequence number
+ * @_rand: RAND = 128-bit random challenge
+ * @autn: AUTN = 128-bit authentication token
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @res_len: Variable that will be set to RES length
+ * @auts: 112-bit buffer for AUTS
+ * Returns: 0 on success, -1 on failure, or -2 on synchronization failure
+ */
+int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand,
+ const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len,
+ u8 *auts)
+{
+ int i;
+ u8 mac_a[8], ak[6], rx_sqn[6];
+ const u8 *amf;
+
+ wpa_hexdump(MSG_DEBUG, "Milenage: AUTN", autn, 16);
+ wpa_hexdump(MSG_DEBUG, "Milenage: RAND", _rand, 16);
+
+ if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL))
+ return -1;
+
+ *res_len = 8;
+ wpa_hexdump_key(MSG_DEBUG, "Milenage: RES", res, *res_len);
+ wpa_hexdump_key(MSG_DEBUG, "Milenage: CK", ck, 16);
+ wpa_hexdump_key(MSG_DEBUG, "Milenage: IK", ik, 16);
+ wpa_hexdump_key(MSG_DEBUG, "Milenage: AK", ak, 6);
+
+ /* AUTN = (SQN ^ AK) || AMF || MAC */
+ for (i = 0; i < 6; i++)
+ rx_sqn[i] = autn[i] ^ ak[i];
+ wpa_hexdump(MSG_DEBUG, "Milenage: SQN", rx_sqn, 6);
+
+ if (os_memcmp(rx_sqn, sqn, 6) <= 0) {
+ u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+ if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "Milenage: AK*", ak, 6);
+ for (i = 0; i < 6; i++)
+ auts[i] = sqn[i] ^ ak[i];
+ if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6))
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "Milenage: AUTS", auts, 14);
+ return -2;
+ }
+
+ amf = autn + 6;
+ wpa_hexdump(MSG_DEBUG, "Milenage: AMF", amf, 2);
+ if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL))
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8);
+
+ if (os_memcmp(mac_a, autn + 8, 8) != 0) {
+ wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch");
+ wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A",
+ autn + 8, 8);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/crypto/milenage.h b/src/crypto/milenage.h
new file mode 100644
index 0000000..d5054d6
--- /dev/null
+++ b/src/crypto/milenage.h
@@ -0,0 +1,33 @@
+/*
+ * UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
+ * Copyright (c) 2006-2007 <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef MILENAGE_H
+#define MILENAGE_H
+
+void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k,
+ const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik,
+ u8 *ck, u8 *res, size_t *res_len);
+int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
+ u8 *sqn);
+int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres,
+ u8 *kc);
+int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand,
+ const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len,
+ u8 *auts);
+int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand,
+ const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s);
+int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
+ u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar);
+
+#endif /* MILENAGE_H */
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
new file mode 100644
index 0000000..dae15ab
--- /dev/null
+++ b/src/crypto/ms_funcs.c
@@ -0,0 +1,476 @@
+/*
+ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "ms_funcs.h"
+#include "crypto.h"
+
+
+/**
+ * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @challenge: 8-octet Challenge (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
+ const u8 *username, size_t username_len,
+ u8 *challenge)
+{
+ u8 hash[SHA1_MAC_LEN];
+ const unsigned char *addr[3];
+ size_t len[3];
+
+ addr[0] = peer_challenge;
+ len[0] = 16;
+ addr[1] = auth_challenge;
+ len[1] = 16;
+ addr[2] = username;
+ len[2] = username_len;
+
+ if (sha1_vector(3, addr, len, hash))
+ return -1;
+ os_memcpy(challenge, hash, 8);
+ return 0;
+}
+
+
+/**
+ * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
+ * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password_len: Length of password
+ * @password_hash: 16-octet PasswordHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int nt_password_hash(const u8 *password, size_t password_len,
+ u8 *password_hash)
+{
+ u8 buf[512], *pos;
+ size_t i, len;
+
+ if (password_len > 256)
+ password_len = 256;
+
+ /* Convert password into unicode */
+ for (i = 0; i < password_len; i++) {
+ buf[2 * i] = password[i];
+ buf[2 * i + 1] = 0;
+ }
+
+ len = password_len * 2;
+ pos = buf;
+ return md4_vector(1, (const u8 **) &pos, &len, password_hash);
+}
+
+
+/**
+ * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @password_hash_hash: 16-octet PasswordHashHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash)
+{
+ size_t len = 16;
+ return md4_vector(1, &password_hash, &len, password_hash_hash);
+}
+
+
+/**
+ * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5
+ * @challenge: 8-octet Challenge (IN)
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @response: 24-octet Response (OUT)
+ */
+void challenge_response(const u8 *challenge, const u8 *password_hash,
+ u8 *response)
+{
+ u8 zpwd[7];
+ des_encrypt(challenge, password_hash, response);
+ des_encrypt(challenge, password_hash + 7, response + 8);
+ zpwd[0] = password_hash[14];
+ zpwd[1] = password_hash[15];
+ os_memset(zpwd + 2, 0, 5);
+ des_encrypt(challenge, zpwd, response + 16);
+}
+
+
+/**
+ * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password_len: Length of password
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *password, size_t password_len,
+ u8 *response)
+{
+ u8 challenge[8];
+ u8 password_hash[16];
+
+ challenge_hash(peer_challenge, auth_challenge, username, username_len,
+ challenge);
+ if (nt_password_hash(password, password_len, password_hash))
+ return -1;
+ challenge_response(challenge, password_hash, response);
+ return 0;
+}
+
+
+/**
+ * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_nt_response_pwhash(const u8 *auth_challenge,
+ const u8 *peer_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *password_hash,
+ u8 *response)
+{
+ u8 challenge[8];
+
+ if (challenge_hash(peer_challenge, auth_challenge,
+ username, username_len,
+ challenge))
+ return -1;
+ challenge_response(challenge, password_hash, response);
+ return 0;
+}
+
+
+/**
+ * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @nt_response: 24-octet NT-Response (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
+ * encoded as a 42-octet ASCII string (S=hexdump_of_response)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_authenticator_response_pwhash(
+ const u8 *password_hash,
+ const u8 *peer_challenge, const u8 *auth_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *nt_response, u8 *response)
+{
+ static const u8 magic1[39] = {
+ 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+ 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+ 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
+ };
+ static const u8 magic2[41] = {
+ 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+ 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+ 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+ 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E
+ };
+
+ u8 password_hash_hash[16], challenge[8];
+ const unsigned char *addr1[3];
+ const size_t len1[3] = { 16, 24, sizeof(magic1) };
+ const unsigned char *addr2[3];
+ const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) };
+
+ addr1[0] = password_hash_hash;
+ addr1[1] = nt_response;
+ addr1[2] = magic1;
+
+ addr2[0] = response;
+ addr2[1] = challenge;
+ addr2[2] = magic2;
+
+ if (hash_nt_password_hash(password_hash, password_hash_hash))
+ return -1;
+ if (sha1_vector(3, addr1, len1, response))
+ return -1;
+
+ challenge_hash(peer_challenge, auth_challenge, username, username_len,
+ challenge);
+ return sha1_vector(3, addr2, len2, response);
+}
+
+
+/**
+ * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
+ * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password_len: Length of password
+ * @nt_response: 24-octet NT-Response (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
+ * encoded as a 42-octet ASCII string (S=hexdump_of_response)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_authenticator_response(const u8 *password, size_t password_len,
+ const u8 *peer_challenge,
+ const u8 *auth_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *nt_response, u8 *response)
+{
+ u8 password_hash[16];
+ if (nt_password_hash(password, password_len, password_hash))
+ return -1;
+ return generate_authenticator_response_pwhash(
+ password_hash, peer_challenge, auth_challenge,
+ username, username_len, nt_response, response);
+}
+
+
+/**
+ * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
+ * @challenge: 8-octet Challenge (IN)
+ * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password_len: Length of password
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int nt_challenge_response(const u8 *challenge, const u8 *password,
+ size_t password_len, u8 *response)
+{
+ u8 password_hash[16];
+ if (nt_password_hash(password, password_len, password_hash))
+ return -1;
+ challenge_response(challenge, password_hash, response);
+ return 0;
+}
+
+
+/**
+ * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4
+ * @password_hash_hash: 16-octet PasswordHashHash (IN)
+ * @nt_response: 24-octet NTResponse (IN)
+ * @master_key: 16-octet MasterKey (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
+ u8 *master_key)
+{
+ static const u8 magic1[27] = {
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
+ };
+ const unsigned char *addr[3];
+ const size_t len[3] = { 16, 24, sizeof(magic1) };
+ u8 hash[SHA1_MAC_LEN];
+
+ addr[0] = password_hash_hash;
+ addr[1] = nt_response;
+ addr[2] = magic1;
+
+ if (sha1_vector(3, addr, len, hash))
+ return -1;
+ os_memcpy(master_key, hash, 16);
+ return 0;
+}
+
+
+/**
+ * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4
+ * @master_key: 16-octet MasterKey (IN)
+ * @session_key: 8-to-16 octet SessionKey (OUT)
+ * @session_key_len: SessionKeyLength (Length of session_key) (IN)
+ * @is_send: IsSend (IN, BOOLEAN)
+ * @is_server: IsServer (IN, BOOLEAN)
+ * Returns: 0 on success, -1 on failure
+ */
+int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
+ size_t session_key_len, int is_send,
+ int is_server)
+{
+ static const u8 magic2[84] = {
+ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+ 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+ 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e
+ };
+ static const u8 magic3[84] = {
+ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+ 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e
+ };
+ static const u8 shs_pad1[40] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ static const u8 shs_pad2[40] = {
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2
+ };
+ u8 digest[SHA1_MAC_LEN];
+ const unsigned char *addr[4];
+ const size_t len[4] = { 16, 40, 84, 40 };
+
+ addr[0] = master_key;
+ addr[1] = shs_pad1;
+ if (is_send) {
+ addr[2] = is_server ? magic3 : magic2;
+ } else {
+ addr[2] = is_server ? magic2 : magic3;
+ }
+ addr[3] = shs_pad2;
+
+ if (sha1_vector(4, addr, len, digest))
+ return -1;
+
+ if (session_key_len > SHA1_MAC_LEN)
+ session_key_len = SHA1_MAC_LEN;
+ os_memcpy(session_key, digest, session_key_len);
+ return 0;
+}
+
+
+#define PWBLOCK_LEN 516
+
+/**
+ * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
+ * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password_len: Length of password
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @pw_block: 516-byte PwBlock (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int encrypt_pw_block_with_password_hash(
+ const u8 *password, size_t password_len,
+ const u8 *password_hash, u8 *pw_block)
+{
+ size_t i, offset;
+ u8 *pos;
+
+ if (password_len > 256)
+ return -1;
+
+ os_memset(pw_block, 0, PWBLOCK_LEN);
+ offset = (256 - password_len) * 2;
+ if (os_get_random(pw_block, offset) < 0)
+ return -1;
+ for (i = 0; i < password_len; i++)
+ pw_block[offset + i * 2] = password[i];
+ /*
+ * PasswordLength is 4 octets, but since the maximum password length is
+ * 256, only first two (in little endian byte order) can be non-zero.
+ */
+ pos = &pw_block[2 * 256];
+ WPA_PUT_LE16(pos, password_len * 2);
+ rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN);
+ return 0;
+}
+
+
+/**
+ * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @new_password_len: Length of new_password
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
+ * @old_password_len: Length of old_password
+ * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int new_password_encrypted_with_old_nt_password_hash(
+ const u8 *new_password, size_t new_password_len,
+ const u8 *old_password, size_t old_password_len,
+ u8 *encrypted_pw_block)
+{
+ u8 password_hash[16];
+
+ if (nt_password_hash(old_password, old_password_len, password_hash))
+ return -1;
+ if (encrypt_pw_block_with_password_hash(new_password, new_password_len,
+ password_hash,
+ encrypted_pw_block))
+ return -1;
+ return 0;
+}
+
+
+/**
+ * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13
+ * @password_hash: 16-octer PasswordHash (IN)
+ * @block: 16-octet Block (IN)
+ * @cypher: 16-octer Cypher (OUT)
+ */
+void nt_password_hash_encrypted_with_block(const u8 *password_hash,
+ const u8 *block, u8 *cypher)
+{
+ des_encrypt(password_hash, block, cypher);
+ des_encrypt(password_hash + 8, block + 7, cypher + 8);
+}
+
+
+/**
+ * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @new_password_len: Length of new_password
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
+ * @old_password_len: Length of old_password
+ * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int old_nt_password_hash_encrypted_with_new_nt_password_hash(
+ const u8 *new_password, size_t new_password_len,
+ const u8 *old_password, size_t old_password_len,
+ u8 *encrypted_password_hash)
+{
+ u8 old_password_hash[16], new_password_hash[16];
+
+ if (nt_password_hash(old_password, old_password_len,
+ old_password_hash) ||
+ nt_password_hash(new_password, new_password_len,
+ new_password_hash))
+ return -1;
+ nt_password_hash_encrypted_with_block(old_password_hash,
+ new_password_hash,
+ encrypted_password_hash);
+ return 0;
+}
diff --git a/src/crypto/ms_funcs.h b/src/crypto/ms_funcs.h
new file mode 100644
index 0000000..298dbcf
--- /dev/null
+++ b/src/crypto/ms_funcs.h
@@ -0,0 +1,64 @@
+/*
+ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef MS_FUNCS_H
+#define MS_FUNCS_H
+
+int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *password, size_t password_len,
+ u8 *response);
+int generate_nt_response_pwhash(const u8 *auth_challenge,
+ const u8 *peer_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *password_hash,
+ u8 *response);
+int generate_authenticator_response(const u8 *password, size_t password_len,
+ const u8 *peer_challenge,
+ const u8 *auth_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *nt_response, u8 *response);
+int generate_authenticator_response_pwhash(
+ const u8 *password_hash,
+ const u8 *peer_challenge, const u8 *auth_challenge,
+ const u8 *username, size_t username_len,
+ const u8 *nt_response, u8 *response);
+int nt_challenge_response(const u8 *challenge, const u8 *password,
+ size_t password_len, u8 *response);
+
+void challenge_response(const u8 *challenge, const u8 *password_hash,
+ u8 *response);
+int nt_password_hash(const u8 *password, size_t password_len,
+ u8 *password_hash);
+int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash);
+int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
+ u8 *master_key);
+int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
+ size_t session_key_len, int is_send,
+ int is_server);
+int __must_check encrypt_pw_block_with_password_hash(
+ const u8 *password, size_t password_len,
+ const u8 *password_hash, u8 *pw_block);
+int __must_check new_password_encrypted_with_old_nt_password_hash(
+ const u8 *new_password, size_t new_password_len,
+ const u8 *old_password, size_t old_password_len,
+ u8 *encrypted_pw_block);
+void nt_password_hash_encrypted_with_block(const u8 *password_hash,
+ const u8 *block, u8 *cypher);
+int old_nt_password_hash_encrypted_with_new_nt_password_hash(
+ const u8 *new_password, size_t new_password_len,
+ const u8 *old_password, size_t old_password_len,
+ u8 *encrypted_password_hash);
+
+#endif /* MS_FUNCS_H */
diff --git a/src/crypto/random.c b/src/crypto/random.c
new file mode 100644
index 0000000..a30afde
--- /dev/null
+++ b/src/crypto/random.c
@@ -0,0 +1,337 @@
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING 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 <fcntl.h>
+#endif /* __linux__ */
+
+#include "utils/common.h"
+#include "utils/eloop.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 MIN_COLLECT_ENTROPY 1000
+static unsigned int entropy = 0;
+static unsigned int total_collected = 0;
+
+
+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++;
+ wpa_printf(MSG_MSGDUMP, "Add randomness: count=%u entropy=%u",
+ count, entropy);
+ 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;
+ }
+
+ 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;
+ }
+ 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))
+ 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);
+}
+
+
+#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();
+}
+
+#endif /* __linux__ */
+
+
+void random_init(void)
+{
+#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__ */
+}
+
+
+void random_deinit(void)
+{
+#ifdef __linux__
+ random_close_fd();
+#endif /* __linux__ */
+}
diff --git a/src/crypto/random.h b/src/crypto/random.h
new file mode 100644
index 0000000..5dabd2b
--- /dev/null
+++ b/src/crypto/random.h
@@ -0,0 +1,34 @@
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published 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 RANDOM_H
+#define RANDOM_H
+
+#ifdef CONFIG_NO_RANDOM_POOL
+#define random_init() 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(void);
+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/src/crypto/rc4.c b/src/crypto/rc4.c
new file mode 100644
index 0000000..5ab1be1
--- /dev/null
+++ b/src/crypto/rc4.c
@@ -0,0 +1,60 @@
+/*
+ * RC4 stream cipher
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+ u8 *data, size_t data_len)
+{
+ u32 i, j, k;
+ u8 S[256], *pos;
+ size_t kpos;
+
+ /* Setup RC4 state */
+ for (i = 0; i < 256; i++)
+ S[i] = i;
+ j = 0;
+ kpos = 0;
+ for (i = 0; i < 256; i++) {
+ j = (j + S[i] + key[kpos]) & 0xff;
+ kpos++;
+ if (kpos >= keylen)
+ kpos = 0;
+ S_SWAP(i, j);
+ }
+
+ /* Skip the start of the stream */
+ i = j = 0;
+ for (k = 0; k < skip; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ }
+
+ /* Apply RC4 to data */
+ pos = data;
+ for (k = 0; k < data_len; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ *pos++ ^= S[(S[i] + S[j]) & 0xff];
+ }
+
+ return 0;
+}
diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c
new file mode 100644
index 0000000..3f05ca1
--- /dev/null
+++ b/src/crypto/sha1-internal.c
@@ -0,0 +1,308 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "sha1_i.h"
+#include "md5.h"
+#include "crypto.h"
+
+typedef struct SHA1Context SHA1_CTX;
+
+void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+
+
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ SHA1_CTX ctx;
+ size_t i;
+
+ SHA1Init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ SHA1Update(&ctx, addr[i], len[i]);
+ SHA1Final(mac, &ctx);
+ return 0;
+}
+
+
+/* ===== start - public domain SHA1 implementation ===== */
+
+/*
+SHA-1 in C
+By Steve Reid <sreid@sea-to-sky.net>
+100% Public Domain
+
+-----------------
+Modified 7/98
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1Update changed from
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
+len)
+to
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it. This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1Update to
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sreid@sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Saul.Kravitz@celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.
+
+-----------------
+Modified 4/01
+By Jouni Malinen <j@w1.fi>
+Minor changes to match the coding style used in Dynamics.
+
+Modified September 24, 2004
+By Jouni Malinen <j@w1.fi>
+Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined.
+
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+#define SHA1HANDSOFF
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef WORDS_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \
+ (rol(block->l[i], 8) & 0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \
+ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) \
+ z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R1(v,w,x,y,z,i) \
+ z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R2(v,w,x,y,z,i) \
+ z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30);
+#define R3(v,w,x,y,z,i) \
+ z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+ w = rol(w, 30);
+#define R4(v,w,x,y,z,i) \
+ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+ w=rol(w, 30);
+
+
+#ifdef VERBOSE /* SAK */
+void SHAPrintContext(SHA1_CTX *context, char *msg)
+{
+ printf("%s (%d,%d) %x %x %x %x %x\n",
+ msg,
+ context->count[0], context->count[1],
+ context->state[0],
+ context->state[1],
+ context->state[2],
+ context->state[3],
+ context->state[4]);
+}
+#endif
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(u32 state[5], const unsigned char buffer[64])
+{
+ u32 a, b, c, d, e;
+ typedef union {
+ unsigned char c[64];
+ u32 l[16];
+ } CHAR64LONG16;
+ CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+ CHAR64LONG16 workspace;
+ block = &workspace;
+ os_memcpy(block, buffer, 64);
+#else
+ block = (CHAR64LONG16 *) buffer;
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+#ifdef SHA1HANDSOFF
+ os_memset(block, 0, 64);
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const void *_data, u32 len)
+{
+ u32 i, j;
+ const unsigned char *data = _data;
+
+#ifdef VERBOSE
+ SHAPrintContext(context, "before");
+#endif
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3))
+ context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63) {
+ os_memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ os_memcpy(&context->buffer[j], &data[i], len - i);
+#ifdef VERBOSE
+ SHAPrintContext(context, "after ");
+#endif
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+ u32 i;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)
+ ((context->count[(i >= 4 ? 0 : 1)] >>
+ ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+ SHA1Update(context, (unsigned char *) "\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ SHA1Update(context, (unsigned char *) "\0", 1);
+ }
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform()
+ */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) &
+ 255);
+ }
+ /* Wipe variables */
+ i = 0;
+ os_memset(context->buffer, 0, 64);
+ os_memset(context->state, 0, 20);
+ os_memset(context->count, 0, 8);
+ os_memset(finalcount, 0, 8);
+}
+
+/* ===== end - public domain SHA1 implementation ===== */
diff --git a/src/crypto/sha1-pbkdf2.c b/src/crypto/sha1-pbkdf2.c
new file mode 100644
index 0000000..11323de
--- /dev/null
+++ b/src/crypto/sha1-pbkdf2.c
@@ -0,0 +1,100 @@
+/*
+ * SHA1-based key derivation function (PBKDF2) for IEEE 802.11i
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "md5.h"
+#include "crypto.h"
+
+static int pbkdf2_sha1_f(const char *passphrase, const char *ssid,
+ size_t ssid_len, int iterations, unsigned int count,
+ u8 *digest)
+{
+ unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN];
+ int i, j;
+ unsigned char count_buf[4];
+ const u8 *addr[2];
+ size_t len[2];
+ size_t passphrase_len = os_strlen(passphrase);
+
+ addr[0] = (u8 *) ssid;
+ len[0] = ssid_len;
+ addr[1] = count_buf;
+ len[1] = 4;
+
+ /* F(P, S, c, i) = U1 xor U2 xor ... Uc
+ * U1 = PRF(P, S || i)
+ * U2 = PRF(P, U1)
+ * Uc = PRF(P, Uc-1)
+ */
+
+ count_buf[0] = (count >> 24) & 0xff;
+ count_buf[1] = (count >> 16) & 0xff;
+ count_buf[2] = (count >> 8) & 0xff;
+ count_buf[3] = count & 0xff;
+ if (hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len,
+ tmp))
+ return -1;
+ os_memcpy(digest, tmp, SHA1_MAC_LEN);
+
+ for (i = 1; i < iterations; i++) {
+ if (hmac_sha1((u8 *) passphrase, passphrase_len, tmp,
+ SHA1_MAC_LEN, tmp2))
+ return -1;
+ os_memcpy(tmp, tmp2, SHA1_MAC_LEN);
+ for (j = 0; j < SHA1_MAC_LEN; j++)
+ digest[j] ^= tmp2[j];
+ }
+
+ return 0;
+}
+
+
+/**
+ * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i
+ * @passphrase: ASCII passphrase
+ * @ssid: SSID
+ * @ssid_len: SSID length in bytes
+ * @iterations: Number of iterations to run
+ * @buf: Buffer for the generated key
+ * @buflen: Length of the buffer in bytes
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive PSK for WPA-PSK. For this protocol,
+ * iterations is set to 4096 and buflen to 32. This function is described in
+ * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0.
+ */
+int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen)
+{
+ unsigned int count = 0;
+ unsigned char *pos = buf;
+ size_t left = buflen, plen;
+ unsigned char digest[SHA1_MAC_LEN];
+
+ while (left > 0) {
+ count++;
+ if (pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations,
+ count, digest))
+ return -1;
+ plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left;
+ os_memcpy(pos, digest, plen);
+ pos += plen;
+ left -= plen;
+ }
+
+ return 0;
+}
diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c
new file mode 100644
index 0000000..2c8c029
--- /dev/null
+++ b/src/crypto/sha1-tlsprf.c
@@ -0,0 +1,109 @@
+/*
+ * TLS PRF (SHA1 + MD5)
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "md5.h"
+#include "crypto.h"
+
+
+/**
+ * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
+ * @secret: Key for PRF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
+ */
+int tls_prf(const u8 *secret, size_t secret_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+ size_t L_S1, L_S2, i;
+ const u8 *S1, *S2;
+ u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN];
+ u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN];
+ int MD5_pos, SHA1_pos;
+ const u8 *MD5_addr[3];
+ size_t MD5_len[3];
+ const unsigned char *SHA1_addr[3];
+ size_t SHA1_len[3];
+
+ if (secret_len & 1)
+ return -1;
+
+ MD5_addr[0] = A_MD5;
+ MD5_len[0] = MD5_MAC_LEN;
+ MD5_addr[1] = (unsigned char *) label;
+ MD5_len[1] = os_strlen(label);
+ MD5_addr[2] = seed;
+ MD5_len[2] = seed_len;
+
+ SHA1_addr[0] = A_SHA1;
+ SHA1_len[0] = SHA1_MAC_LEN;
+ SHA1_addr[1] = (unsigned char *) label;
+ SHA1_len[1] = os_strlen(label);
+ SHA1_addr[2] = seed;
+ SHA1_len[2] = seed_len;
+
+ /* RFC 2246, Chapter 5
+ * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+ * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+ * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
+ */
+
+ L_S1 = L_S2 = (secret_len + 1) / 2;
+ S1 = secret;
+ S2 = secret + L_S1;
+ if (secret_len & 1) {
+ /* The last byte of S1 will be shared with S2 */
+ S2--;
+ }
+
+ hmac_md5_vector_non_fips_allow(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1],
+ A_MD5);
+ hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1);
+
+ MD5_pos = MD5_MAC_LEN;
+ SHA1_pos = SHA1_MAC_LEN;
+ for (i = 0; i < outlen; i++) {
+ if (MD5_pos == MD5_MAC_LEN) {
+ hmac_md5_vector_non_fips_allow(S1, L_S1, 3, MD5_addr,
+ MD5_len, P_MD5);
+ MD5_pos = 0;
+ hmac_md5_non_fips_allow(S1, L_S1, A_MD5, MD5_MAC_LEN,
+ A_MD5);
+ }
+ if (SHA1_pos == SHA1_MAC_LEN) {
+ hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len,
+ P_SHA1);
+ SHA1_pos = 0;
+ hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1);
+ }
+
+ out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos];
+
+ MD5_pos++;
+ SHA1_pos++;
+ }
+
+ return 0;
+}
diff --git a/src/crypto/sha1-tprf.c b/src/crypto/sha1-tprf.c
new file mode 100644
index 0000000..4a80e96
--- /dev/null
+++ b/src/crypto/sha1-tprf.c
@@ -0,0 +1,76 @@
+/*
+ * SHA1 T-PRF for EAP-FAST
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+/**
+ * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key for EAP-FAST. T-PRF is defined in RFC 4851, Section 5.5.
+ */
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
+{
+ unsigned char counter = 0;
+ size_t pos, plen;
+ u8 hash[SHA1_MAC_LEN];
+ size_t label_len = os_strlen(label);
+ u8 output_len[2];
+ const unsigned char *addr[5];
+ size_t len[5];
+
+ addr[0] = hash;
+ len[0] = 0;
+ addr[1] = (unsigned char *) label;
+ len[1] = label_len + 1;
+ addr[2] = seed;
+ len[2] = seed_len;
+ addr[3] = output_len;
+ len[3] = 2;
+ addr[4] = &counter;
+ len[4] = 1;
+
+ output_len[0] = (buf_len >> 8) & 0xff;
+ output_len[1] = buf_len & 0xff;
+ pos = 0;
+ while (pos < buf_len) {
+ counter++;
+ plen = buf_len - pos;
+ if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
+ return -1;
+ if (plen >= SHA1_MAC_LEN) {
+ os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
+ pos += SHA1_MAC_LEN;
+ } else {
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ len[0] = SHA1_MAC_LEN;
+ }
+
+ return 0;
+}
diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c
new file mode 100644
index 0000000..fe00bdb
--- /dev/null
+++ b/src/crypto/sha1.c
@@ -0,0 +1,163 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+ unsigned char tk[20];
+ const u8 *_addr[6];
+ size_t _len[6], i;
+
+ if (num_elem > 5) {
+ /*
+ * Fixed limit on the number of fragments to avoid having to
+ * allocate memory (which could fail).
+ */
+ return -1;
+ }
+
+ /* if key is longer than 64 bytes reset it to key = SHA1(key) */
+ if (key_len > 64) {
+ if (sha1_vector(1, &key, &key_len, tk))
+ return -1;
+ key = tk;
+ key_len = 20;
+ }
+
+ /* the HMAC_SHA1 transform looks like:
+ *
+ * SHA1(K XOR opad, SHA1(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected */
+
+ /* start out by storing key in ipad */
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with ipad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x36;
+
+ /* perform inner SHA1 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ for (i = 0; i < num_elem; i++) {
+ _addr[i + 1] = addr[i];
+ _len[i + 1] = len[i];
+ }
+ if (sha1_vector(1 + num_elem, _addr, _len, mac))
+ return -1;
+
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with opad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x5c;
+
+ /* perform outer SHA1 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ _addr[1] = mac;
+ _len[1] = SHA1_MAC_LEN;
+ return sha1_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 of failure
+ */
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+/**
+ * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., PMK in IEEE 802.11i).
+ */
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+ u8 counter = 0;
+ size_t pos, plen;
+ u8 hash[SHA1_MAC_LEN];
+ size_t label_len = os_strlen(label) + 1;
+ const unsigned char *addr[3];
+ size_t len[3];
+
+ addr[0] = (u8 *) label;
+ len[0] = label_len;
+ addr[1] = data;
+ len[1] = data_len;
+ addr[2] = &counter;
+ len[2] = 1;
+
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ if (plen >= SHA1_MAC_LEN) {
+ if (hmac_sha1_vector(key, key_len, 3, addr, len,
+ &buf[pos]))
+ return -1;
+ pos += SHA1_MAC_LEN;
+ } else {
+ if (hmac_sha1_vector(key, key_len, 3, addr, len,
+ hash))
+ return -1;
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+
+ return 0;
+}
diff --git a/src/crypto/sha1.h b/src/crypto/sha1.h
new file mode 100644
index 0000000..c1a6233
--- /dev/null
+++ b/src/crypto/sha1.h
@@ -0,0 +1,33 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SHA1_H
+#define SHA1_H
+
+#define SHA1_MAC_LEN 20
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac);
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len);
+int __must_check tls_prf(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *out, size_t outlen);
+int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen);
+#endif /* SHA1_H */
diff --git a/src/crypto/sha1_i.h b/src/crypto/sha1_i.h
new file mode 100644
index 0000000..ec2f82f
--- /dev/null
+++ b/src/crypto/sha1_i.h
@@ -0,0 +1,29 @@
+/*
+ * SHA1 internal definitions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SHA1_I_H
+#define SHA1_I_H
+
+struct SHA1Context {
+ u32 state[5];
+ u32 count[2];
+ unsigned char buffer[64];
+};
+
+void SHA1Init(struct SHA1Context *context);
+void SHA1Update(struct SHA1Context *context, const void *data, u32 len);
+void SHA1Final(unsigned char digest[20], struct SHA1Context *context);
+void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+
+#endif /* SHA1_I_H */
diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c
new file mode 100644
index 0000000..b061373
--- /dev/null
+++ b/src/crypto/sha256-internal.c
@@ -0,0 +1,243 @@
+/*
+ * SHA-256 hash implementation and interface functions
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "crypto.h"
+
+struct sha256_state {
+ u64 length;
+ u32 state[8], curlen;
+ u8 buf[64];
+};
+
+static void sha256_init(struct sha256_state *md);
+static int sha256_process(struct sha256_state *md, const unsigned char *in,
+ unsigned long inlen);
+static int sha256_done(struct sha256_state *md, unsigned char *out);
+
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ struct sha256_state ctx;
+ size_t i;
+
+ sha256_init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ if (sha256_process(&ctx, addr[i], len[i]))
+ return -1;
+ if (sha256_done(&ctx, mac))
+ return -1;
+ return 0;
+}
+
+
+/* ===== start - public domain SHA256 implementation ===== */
+
+/* This is based on SHA256 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+/* the K array */
+static const unsigned long K[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+ 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+ 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+ 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+ 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+ 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+ 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+ 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+ 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+ 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+
+/* Various logical functions */
+#define RORc(x, y) \
+( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \
+ ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) RORc((x), (n))
+#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+/* compress 512-bits */
+static int sha256_compress(struct sha256_state *md, unsigned char *buf)
+{
+ u32 S[8], W[64], t0, t1;
+ u32 t;
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++) {
+ S[i] = md->state[i];
+ }
+
+ /* copy the state into 512-bits into W[0..15] */
+ for (i = 0; i < 16; i++)
+ W[i] = WPA_GET_BE32(buf + (4 * i));
+
+ /* fill W[16..63] */
+ for (i = 16; i < 64; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+ W[i - 16];
+ }
+
+ /* Compress */
+#define RND(a,b,c,d,e,f,g,h,i) \
+ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
+ t1 = Sigma0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+ for (i = 0; i < 64; ++i) {
+ RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
+ t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+ S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+ }
+
+ /* feedback */
+ for (i = 0; i < 8; i++) {
+ md->state[i] = md->state[i] + S[i];
+ }
+ return 0;
+}
+
+
+/* Initialize the hash state */
+static void sha256_init(struct sha256_state *md)
+{
+ md->curlen = 0;
+ md->length = 0;
+ md->state[0] = 0x6A09E667UL;
+ md->state[1] = 0xBB67AE85UL;
+ md->state[2] = 0x3C6EF372UL;
+ md->state[3] = 0xA54FF53AUL;
+ md->state[4] = 0x510E527FUL;
+ md->state[5] = 0x9B05688CUL;
+ md->state[6] = 0x1F83D9ABUL;
+ md->state[7] = 0x5BE0CD19UL;
+}
+
+/**
+ Process a block of memory though the hash
+ @param md The hash state
+ @param in The data to hash
+ @param inlen The length of the data (octets)
+ @return CRYPT_OK if successful
+*/
+static int sha256_process(struct sha256_state *md, const unsigned char *in,
+ unsigned long inlen)
+{
+ unsigned long n;
+#define block_size 64
+
+ if (md->curlen > sizeof(md->buf))
+ return -1;
+
+ while (inlen > 0) {
+ if (md->curlen == 0 && inlen >= block_size) {
+ if (sha256_compress(md, (unsigned char *) in) < 0)
+ return -1;
+ md->length += block_size * 8;
+ in += block_size;
+ inlen -= block_size;
+ } else {
+ n = MIN(inlen, (block_size - md->curlen));
+ os_memcpy(md->buf + md->curlen, in, n);
+ md->curlen += n;
+ in += n;
+ inlen -= n;
+ if (md->curlen == block_size) {
+ if (sha256_compress(md, md->buf) < 0)
+ return -1;
+ md->length += 8 * block_size;
+ md->curlen = 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (32 bytes)
+ @return CRYPT_OK if successful
+*/
+static int sha256_done(struct sha256_state *md, unsigned char *out)
+{
+ int i;
+
+ if (md->curlen >= sizeof(md->buf))
+ return -1;
+
+ /* increase the length of the message */
+ md->length += md->curlen * 8;
+
+ /* append the '1' bit */
+ md->buf[md->curlen++] = (unsigned char) 0x80;
+
+ /* if the length is currently above 56 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (md->curlen > 56) {
+ while (md->curlen < 64) {
+ md->buf[md->curlen++] = (unsigned char) 0;
+ }
+ sha256_compress(md, md->buf);
+ md->curlen = 0;
+ }
+
+ /* pad upto 56 bytes of zeroes */
+ while (md->curlen < 56) {
+ md->buf[md->curlen++] = (unsigned char) 0;
+ }
+
+ /* store length */
+ WPA_PUT_BE64(md->buf + 56, md->length);
+ sha256_compress(md, md->buf);
+
+ /* copy output */
+ for (i = 0; i < 8; i++)
+ WPA_PUT_BE32(out + (4 * i), md->state[i]);
+
+ return 0;
+}
+
+/* ===== end - public domain SHA256 implementation ===== */
diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c
new file mode 100644
index 0000000..7f320f9
--- /dev/null
+++ b/src/crypto/sha256.c
@@ -0,0 +1,157 @@
+/*
+ * SHA-256 hash implementation and interface functions
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (32 bytes)
+ */
+void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+ unsigned char tk[32];
+ const u8 *_addr[6];
+ size_t _len[6], i;
+
+ if (num_elem > 5) {
+ /*
+ * Fixed limit on the number of fragments to avoid having to
+ * allocate memory (which could fail).
+ */
+ return;
+ }
+
+ /* if key is longer than 64 bytes reset it to key = SHA256(key) */
+ if (key_len > 64) {
+ sha256_vector(1, &key, &key_len, tk);
+ key = tk;
+ key_len = 32;
+ }
+
+ /* the HMAC_SHA256 transform looks like:
+ *
+ * SHA256(K XOR opad, SHA256(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected */
+
+ /* start out by storing key in ipad */
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with ipad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x36;
+
+ /* perform inner SHA256 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ for (i = 0; i < num_elem; i++) {
+ _addr[i + 1] = addr[i];
+ _len[i + 1] = len[i];
+ }
+ sha256_vector(1 + num_elem, _addr, _len, mac);
+
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with opad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x5c;
+
+ /* perform outer SHA256 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ _addr[1] = mac;
+ _len[1] = SHA256_MAC_LEN;
+ sha256_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha256 - HMAC-SHA256 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (20 bytes)
+ */
+void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+/**
+ * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+ u16 counter = 1;
+ size_t pos, plen;
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+ u8 counter_le[2], length_le[2];
+
+ addr[0] = counter_le;
+ len[0] = 2;
+ addr[1] = (u8 *) label;
+ len[1] = os_strlen(label);
+ addr[2] = data;
+ len[2] = data_len;
+ addr[3] = length_le;
+ len[3] = sizeof(length_le);
+
+ WPA_PUT_LE16(length_le, buf_len * 8);
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ WPA_PUT_LE16(counter_le, counter);
+ if (plen >= SHA256_MAC_LEN) {
+ hmac_sha256_vector(key, key_len, 4, addr, len,
+ &buf[pos]);
+ pos += SHA256_MAC_LEN;
+ } else {
+ hmac_sha256_vector(key, key_len, 4, addr, len, hash);
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+}
diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h
new file mode 100644
index 0000000..dc597f0
--- /dev/null
+++ b/src/crypto/sha256.h
@@ -0,0 +1,27 @@
+/*
+ * SHA256 hash implementation and interface functions
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SHA256_H
+#define SHA256_H
+
+#define SHA256_MAC_LEN 32
+
+void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac);
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+
+#endif /* SHA256_H */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
new file mode 100644
index 0000000..0928b5b
--- /dev/null
+++ b/src/crypto/tls.h
@@ -0,0 +1,569 @@
+/*
+ * SSL/TLS interface definition
+ * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef TLS_H
+#define TLS_H
+
+struct tls_connection;
+
+struct tls_keys {
+ const u8 *master_key; /* TLS master secret */
+ size_t master_key_len;
+ const u8 *client_random;
+ size_t client_random_len;
+ const u8 *server_random;
+ size_t server_random_len;
+ const u8 *inner_secret; /* TLS/IA inner secret */
+ size_t inner_secret_len;
+};
+
+enum tls_event {
+ TLS_CERT_CHAIN_FAILURE,
+ TLS_PEER_CERTIFICATE
+};
+
+/*
+ * Note: These are used as identifier with external programs and as such, the
+ * values must not be changed.
+ */
+enum tls_fail_reason {
+ TLS_FAIL_UNSPECIFIED = 0,
+ TLS_FAIL_UNTRUSTED = 1,
+ TLS_FAIL_REVOKED = 2,
+ TLS_FAIL_NOT_YET_VALID = 3,
+ TLS_FAIL_EXPIRED = 4,
+ TLS_FAIL_SUBJECT_MISMATCH = 5,
+ TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
+ TLS_FAIL_BAD_CERTIFICATE = 7,
+ TLS_FAIL_SERVER_CHAIN_PROBE = 8
+};
+
+union tls_event_data {
+ struct {
+ int depth;
+ const char *subject;
+ enum tls_fail_reason reason;
+ const char *reason_txt;
+ const struct wpabuf *cert;
+ } cert_fail;
+
+ struct {
+ int depth;
+ const char *subject;
+ const struct wpabuf *cert;
+ const u8 *hash;
+ size_t hash_len;
+ } peer_cert;
+};
+
+struct tls_config {
+ const char *opensc_engine_path;
+ const char *pkcs11_engine_path;
+ const char *pkcs11_module_path;
+ int fips_mode;
+
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data);
+ void *cb_ctx;
+};
+
+#define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0)
+#define TLS_CONN_DISABLE_TIME_CHECKS BIT(1)
+
+/**
+ * struct tls_connection_params - Parameters for TLS connection
+ * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER
+ * format
+ * @ca_cert_blob: ca_cert as inlined data or %NULL if not used
+ * @ca_cert_blob_len: ca_cert_blob length
+ * @ca_path: Path to CA certificates (OpenSSL specific)
+ * @subject_match: String to match in the subject of the peer certificate or
+ * %NULL to allow all subjects
+ * @altsubject_match: String to match in the alternative subject of the peer
+ * certificate or %NULL to allow all alternative subjects
+ * @client_cert: File or reference name for client X.509 certificate in PEM or
+ * DER format
+ * @client_cert_blob: client_cert as inlined data or %NULL if not used
+ * @client_cert_blob_len: client_cert_blob length
+ * @private_key: File or reference name for client private key in PEM or DER
+ * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY)
+ * @private_key_blob: private_key as inlined data or %NULL if not used
+ * @private_key_blob_len: private_key_blob length
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used
+ * @dh_blob: dh_file as inlined data or %NULL if not used
+ * @dh_blob_len: dh_blob length
+ * @engine: 1 = use engine (e.g., a smartcard) for private key operations
+ * (this is OpenSSL specific for now)
+ * @engine_id: engine id string (this is OpenSSL specific for now)
+ * @ppin: pointer to the pin variable in the configuration
+ * (this is OpenSSL specific for now)
+ * @key_id: the private key's id when using engine (this is OpenSSL
+ * specific for now)
+ * @cert_id: the certificate's id when using engine
+ * @ca_cert_id: the CA certificate's id when using engine
+ * @tls_ia: Whether to enable TLS/IA (for EAP-TTLSv1)
+ * @flags: Parameter options (TLS_CONN_*)
+ *
+ * TLS connection parameters to be configured with tls_connection_set_params()
+ * and tls_global_set_params().
+ *
+ * Certificates and private key can be configured either as a reference name
+ * (file path or reference to certificate store) or by providing the same data
+ * as a pointer to the data in memory. Only one option will be used for each
+ * field.
+ */
+struct tls_connection_params {
+ const char *ca_cert;
+ const u8 *ca_cert_blob;
+ size_t ca_cert_blob_len;
+ const char *ca_path;
+ const char *subject_match;
+ const char *altsubject_match;
+ const char *client_cert;
+ const u8 *client_cert_blob;
+ size_t client_cert_blob_len;
+ const char *private_key;
+ const u8 *private_key_blob;
+ size_t private_key_blob_len;
+ const char *private_key_passwd;
+ const char *dh_file;
+ const u8 *dh_blob;
+ size_t dh_blob_len;
+ int tls_ia;
+
+ /* OpenSSL specific variables */
+ int engine;
+ const char *engine_id;
+ const char *pin;
+ const char *key_id;
+ const char *cert_id;
+ const char *ca_cert_id;
+
+ unsigned int flags;
+};
+
+
+/**
+ * tls_init - Initialize TLS library
+ * @conf: Configuration data for TLS library
+ * Returns: Context data to be used as tls_ctx in calls to other functions,
+ * or %NULL on failure.
+ *
+ * Called once during program startup and once for each RSN pre-authentication
+ * session. In other words, there can be two concurrent TLS contexts. If global
+ * library initialization is needed (i.e., one that is shared between both
+ * authentication types), the TLS library wrapper should maintain a reference
+ * counter and do global initialization only when moving from 0 to 1 reference.
+ */
+void * tls_init(const struct tls_config *conf);
+
+/**
+ * tls_deinit - Deinitialize TLS library
+ * @tls_ctx: TLS context data from tls_init()
+ *
+ * Called once during program shutdown and once for each RSN pre-authentication
+ * session. If global library deinitialization is needed (i.e., one that is
+ * shared between both authentication types), the TLS library wrapper should
+ * maintain a reference counter and do global deinitialization only when moving
+ * from 1 to 0 references.
+ */
+void tls_deinit(void *tls_ctx);
+
+/**
+ * tls_get_errors - Process pending errors
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Number of found error, 0 if no errors detected.
+ *
+ * Process all pending TLS errors.
+ */
+int tls_get_errors(void *tls_ctx);
+
+/**
+ * tls_connection_init - Initialize a new TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Connection context data, conn for other function calls
+ */
+struct tls_connection * tls_connection_init(void *tls_ctx);
+
+/**
+ * tls_connection_deinit - Free TLS connection data
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Release all resources allocated for TLS connection.
+ */
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_established - Has the TLS connection been completed?
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if TLS connection has been completed, 0 if not.
+ */
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_shutdown - Shutdown TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * Shutdown current TLS connection without releasing all resources. New
+ * connection can be started by using the same conn without having to call
+ * tls_connection_init() or setting certificates etc. again. The new
+ * connection should try to use session resumption.
+ */
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn);
+
+enum {
+ TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3,
+ TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2
+};
+
+/**
+ * tls_connection_set_params - Set TLS connection parameters
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @params: Connection parameters
+ * Returns: 0 on success, -1 on failure,
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing
+ * PKCS#11 engine failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
+ * PKCS#11 engine private key.
+ */
+int __must_check
+tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params);
+
+/**
+ * tls_global_set_params - Set TLS parameters for all TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @params: Global TLS parameters
+ * Returns: 0 on success, -1 on failure,
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing
+ * PKCS#11 engine failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
+ * PKCS#11 engine private key.
+ */
+int __must_check tls_global_set_params(
+ void *tls_ctx, const struct tls_connection_params *params);
+
+/**
+ * tls_global_set_verify - Set global certificate verification options
+ * @tls_ctx: TLS context data from tls_init()
+ * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate,
+ * 2 = verify CRL for all certificates
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_global_set_verify(void *tls_ctx, int check_crl);
+
+/**
+ * tls_connection_set_verify - Set certificate verification options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @verify_peer: 1 = verify peer certificate
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_set_verify(void *tls_ctx,
+ struct tls_connection *conn,
+ int verify_peer);
+
+/**
+ * tls_connection_set_ia - Set TLS/IA parameters
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @tls_ia: 1 = enable TLS/IA
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to configure TLS/IA in server mode where
+ * tls_connection_set_params() is not used.
+ */
+int __must_check tls_connection_set_ia(void *tls_ctx,
+ struct tls_connection *conn,
+ int tls_ia);
+
+/**
+ * tls_connection_get_keys - Get master key and random data from TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @keys: Structure of key/random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_get_keys(void *tls_ctx,
+ struct tls_connection *conn,
+ struct tls_keys *keys);
+
+/**
+ * tls_connection_prf - Use TLS-PRF to derive keying material
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is optional to implement if tls_connection_get_keys() provides
+ * access to master secret and server/client random values. If these values are
+ * not exported from the TLS library, tls_connection_prf() is required so that
+ * further keying material can be derived from the master secret. If not
+ * implemented, the function will still need to be defined, but it can just
+ * return -1. Example implementation of this function is in tls_prf() function
+ * when it is called with seed set to client_random|server_random (or
+ * server_random|client_random).
+ */
+int __must_check tls_connection_prf(void *tls_ctx,
+ struct tls_connection *conn,
+ const char *label,
+ int server_random_first,
+ u8 *out, size_t out_len);
+
+/**
+ * tls_connection_handshake - Process TLS handshake (client side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS server
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * Returns: Output data, %NULL on failure
+ *
+ * The caller is responsible for freeing the returned output data. If the final
+ * handshake message includes application data, this is decrypted and
+ * appl_data (if not %NULL) is set to point this data. The caller is
+ * responsible for freeing appl_data.
+ *
+ * This function is used during TLS handshake. The first call is done with
+ * in_data == %NULL and the library is expected to return ClientHello packet.
+ * This packet is then send to the server and a response from server is given
+ * to TLS library by calling this function again with in_data pointing to the
+ * TLS message from the server.
+ *
+ * If the TLS handshake fails, this function may return %NULL. However, if the
+ * TLS library has a TLS alert to send out, that should be returned as the
+ * output data. In this case, tls_connection_get_failed() must return failure
+ * (> 0).
+ *
+ * tls_connection_established() should return 1 once the TLS handshake has been
+ * completed successfully.
+ */
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data);
+
+/**
+ * tls_connection_server_handshake - Process TLS handshake (server side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS peer
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * Returns: Output data, %NULL on failure
+ *
+ * The caller is responsible for freeing the returned output data.
+ */
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data);
+
+/**
+ * tls_connection_encrypt - Encrypt data into TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Plaintext data to be encrypted
+ * Returns: Encrypted TLS data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel. The caller is responsible for freeing the
+ * returned output data.
+ */
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data);
+
+/**
+ * tls_connection_decrypt - Decrypt data from TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Encrypted TLS data
+ * Returns: Decrypted TLS data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel. The caller is responsible for
+ * freeing the returned output data.
+ */
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data);
+
+/**
+ * tls_connection_resumed - Was session resumption used
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn);
+
+enum {
+ TLS_CIPHER_NONE,
+ TLS_CIPHER_RC4_SHA /* 0x0005 */,
+ TLS_CIPHER_AES128_SHA /* 0x002f */,
+ TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */,
+ TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */
+};
+
+/**
+ * tls_connection_set_cipher_list - Configure acceptable cipher suites
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_set_cipher_list(void *tls_ctx,
+ struct tls_connection *conn,
+ u8 *ciphers);
+
+/**
+ * tls_get_cipher - Get current cipher name
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int __must_check tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen);
+
+/**
+ * tls_connection_enable_workaround - Enable TLS workaround options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to enable connection-specific workaround options for
+ * buffer SSL/TLS implementations.
+ */
+int __must_check tls_connection_enable_workaround(void *tls_ctx,
+ struct tls_connection *conn);
+
+/**
+ * tls_connection_client_hello_ext - Set TLS extension for ClientHello
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @ext_type: Extension type
+ * @data: Extension payload (%NULL to remove extension)
+ * @data_len: Extension payload length
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_client_hello_ext(void *tls_ctx,
+ struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len);
+
+/**
+ * tls_connection_get_failed - Get connection failure status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Returns >0 if connection has failed, 0 if not.
+ */
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_get_read_alerts - Get connection read alert status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Number of times a fatal read (remote end reported error) has
+ * happened during this connection.
+ */
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_get_write_alerts - Get connection write alert status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Number of times a fatal write (locally detected error) has happened
+ * during this connection.
+ */
+int tls_connection_get_write_alerts(void *tls_ctx,
+ struct tls_connection *conn);
+
+/**
+ * tls_connection_get_keyblock_size - Get TLS key_block size
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tls_connection_get_keyblock_size(void *tls_ctx,
+ struct tls_connection *conn);
+
+#define TLS_CAPABILITY_IA 0x0001 /* TLS Inner Application (TLS/IA) */
+/**
+ * tls_capabilities - Get supported TLS capabilities
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*)
+ */
+unsigned int tls_capabilities(void *tls_ctx);
+
+/**
+ * tls_connection_ia_send_phase_finished - Send a TLS/IA PhaseFinished message
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @final: 1 = FinalPhaseFinished, 0 = IntermediatePhaseFinished
+ * Returns: Encrypted TLS/IA data, %NULL on failure
+ *
+ * This function is used to send the TLS/IA end phase message, e.g., when the
+ * EAP server completes EAP-TTLSv1.
+ */
+struct wpabuf * tls_connection_ia_send_phase_finished(
+ void *tls_ctx, struct tls_connection *conn, int final);
+
+/**
+ * tls_connection_ia_final_phase_finished - Has final phase been completed
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if valid FinalPhaseFinished has been received, 0 if not, or -1
+ * on failure
+ */
+int __must_check tls_connection_ia_final_phase_finished(
+ void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_ia_permute_inner_secret - Permute TLS/IA inner secret
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @key: Session key material (session_key vectors with 2-octet length), or
+ * %NULL if no session key was generating in the current phase
+ * @key_len: Length of session key material
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_ia_permute_inner_secret(
+ void *tls_ctx, struct tls_connection *conn,
+ const u8 *key, size_t key_len);
+
+typedef int (*tls_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+int __must_check tls_connection_set_session_ticket_cb(
+ void *tls_ctx, struct tls_connection *conn,
+ tls_session_ticket_cb cb, void *ctx);
+
+#endif /* TLS_H */
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
new file mode 100644
index 0000000..c3a7358
--- /dev/null
+++ b/src/crypto/tls_gnutls.c
@@ -0,0 +1,1457 @@
+/*
+ * SSL/TLS interface functions for GnuTLS
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#ifdef PKCS12_FUNCS
+#include <gnutls/pkcs12.h>
+#endif /* PKCS12_FUNCS */
+
+#ifdef CONFIG_GNUTLS_EXTRA
+#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
+#define GNUTLS_IA
+#include <gnutls/extra.h>
+#if LIBGNUTLS_VERSION_NUMBER == 0x010302
+/* This function is not included in the current gnutls/extra.h even though it
+ * should be, so define it here as a workaround for the time being. */
+int gnutls_ia_verify_endphase(gnutls_session_t session, char *checksum);
+#endif /* LIBGNUTLS_VERSION_NUMBER == 0x010302 */
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
+#endif /* CONFIG_GNUTLS_EXTRA */
+
+#include "common.h"
+#include "tls.h"
+
+
+#ifndef TLS_RANDOM_SIZE
+#define TLS_RANDOM_SIZE 32
+#endif
+#ifndef TLS_MASTER_SIZE
+#define TLS_MASTER_SIZE 48
+#endif
+
+
+#if LIBGNUTLS_VERSION_NUMBER < 0x010302
+/* GnuTLS 1.3.2 added functions for using master secret. Older versions require
+ * use of internal structures to get the master_secret and
+ * {server,client}_random.
+ */
+#define GNUTLS_INTERNAL_STRUCTURE_HACK
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
+
+
+#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
+/*
+ * It looks like gnutls does not provide access to client/server_random and
+ * master_key. This is somewhat unfortunate since these are needed for key
+ * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible
+ * hack that copies the gnutls_session_int definition from gnutls_int.h so that
+ * we can get the needed information.
+ */
+
+typedef u8 uint8;
+typedef unsigned char opaque;
+typedef struct {
+ uint8 suite[2];
+} cipher_suite_st;
+
+typedef struct {
+ gnutls_connection_end_t entity;
+ gnutls_kx_algorithm_t kx_algorithm;
+ gnutls_cipher_algorithm_t read_bulk_cipher_algorithm;
+ gnutls_mac_algorithm_t read_mac_algorithm;
+ gnutls_compression_method_t read_compression_algorithm;
+ gnutls_cipher_algorithm_t write_bulk_cipher_algorithm;
+ gnutls_mac_algorithm_t write_mac_algorithm;
+ gnutls_compression_method_t write_compression_algorithm;
+ cipher_suite_st current_cipher_suite;
+ opaque master_secret[TLS_MASTER_SIZE];
+ opaque client_random[TLS_RANDOM_SIZE];
+ opaque server_random[TLS_RANDOM_SIZE];
+ /* followed by stuff we are not interested in */
+} security_parameters_st;
+
+struct gnutls_session_int {
+ security_parameters_st security_parameters;
+ /* followed by things we are not interested in */
+};
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
+
+static int tls_gnutls_ref_count = 0;
+
+struct tls_global {
+ /* Data for session resumption */
+ void *session_data;
+ size_t session_data_size;
+
+ int server;
+
+ int params_set;
+ gnutls_certificate_credentials_t xcred;
+};
+
+struct tls_connection {
+ gnutls_session session;
+ char *subject_match, *altsubject_match;
+ int read_alerts, write_alerts, failed;
+
+ u8 *pre_shared_secret;
+ size_t pre_shared_secret_len;
+ int established;
+ int verify_peer;
+
+ struct wpabuf *push_buf;
+ struct wpabuf *pull_buf;
+ const u8 *pull_buf_offset;
+
+ int params_set;
+ gnutls_certificate_credentials_t xcred;
+
+ int tls_ia;
+ int final_phase_finished;
+
+#ifdef GNUTLS_IA
+ gnutls_ia_server_credentials_t iacred_srv;
+ gnutls_ia_client_credentials_t iacred_cli;
+
+ /* Session keys generated in the current phase for inner secret
+ * permutation before generating/verifying PhaseFinished. */
+ u8 *session_keys;
+ size_t session_keys_len;
+
+ u8 inner_secret[TLS_MASTER_SIZE];
+#endif /* GNUTLS_IA */
+};
+
+
+static void tls_log_func(int level, const char *msg)
+{
+ char *s, *pos;
+ if (level == 6 || level == 7) {
+ /* These levels seem to be mostly I/O debug and msg dumps */
+ return;
+ }
+
+ s = os_strdup(msg);
+ if (s == NULL)
+ return;
+
+ pos = s;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+ wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG,
+ "gnutls<%d> %s", level, s);
+ os_free(s);
+}
+
+
+extern int wpa_debug_show_keys;
+
+void * tls_init(const struct tls_config *conf)
+{
+ struct tls_global *global;
+
+#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
+ /* Because of the horrible hack to get master_secret and client/server
+ * random, we need to make sure that the gnutls version is something
+ * that is expected to have same structure definition for the session
+ * data.. */
+ const char *ver;
+ const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9",
+ "1.3.2",
+ NULL };
+ int i;
+#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+
+ global = os_zalloc(sizeof(*global));
+ if (global == NULL)
+ return NULL;
+
+ if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) {
+ os_free(global);
+ return NULL;
+ }
+ tls_gnutls_ref_count++;
+
+#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
+ ver = gnutls_check_version(NULL);
+ if (ver == NULL) {
+ tls_deinit(global);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver);
+ for (i = 0; ok_ver[i]; i++) {
+ if (strcmp(ok_ver[i], ver) == 0)
+ break;
+ }
+ if (ok_ver[i] == NULL) {
+ wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs "
+ "to be tested and enabled in tls_gnutls.c", ver);
+ tls_deinit(global);
+ return NULL;
+ }
+#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+
+ gnutls_global_set_log_function(tls_log_func);
+ if (wpa_debug_show_keys)
+ gnutls_global_set_log_level(11);
+ return global;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+ struct tls_global *global = ssl_ctx;
+ if (global) {
+ if (global->params_set)
+ gnutls_certificate_free_credentials(global->xcred);
+ os_free(global->session_data);
+ os_free(global);
+ }
+
+ tls_gnutls_ref_count--;
+ if (tls_gnutls_ref_count == 0)
+ gnutls_global_deinit();
+}
+
+
+int tls_get_errors(void *ssl_ctx)
+{
+ return 0;
+}
+
+
+static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf,
+ size_t len)
+{
+ struct tls_connection *conn = (struct tls_connection *) ptr;
+ const u8 *end;
+ if (conn->pull_buf == NULL) {
+ errno = EWOULDBLOCK;
+ return -1;
+ }
+
+ end = wpabuf_head_u8(conn->pull_buf) + wpabuf_len(conn->pull_buf);
+ if ((size_t) (end - conn->pull_buf_offset) < len)
+ len = end - conn->pull_buf_offset;
+ os_memcpy(buf, conn->pull_buf_offset, len);
+ conn->pull_buf_offset += len;
+ if (conn->pull_buf_offset == end) {
+ wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
+ wpabuf_free(conn->pull_buf);
+ conn->pull_buf = NULL;
+ conn->pull_buf_offset = NULL;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
+ __func__,
+ (unsigned long) (end - conn->pull_buf_offset));
+ }
+ return len;
+}
+
+
+static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf,
+ size_t len)
+{
+ struct tls_connection *conn = (struct tls_connection *) ptr;
+
+ if (wpabuf_resize(&conn->push_buf, len) < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+ wpabuf_put_data(conn->push_buf, buf, len);
+
+ return len;
+}
+
+
+static int tls_gnutls_init_session(struct tls_global *global,
+ struct tls_connection *conn)
+{
+ const int cert_types[2] = { GNUTLS_CRT_X509, 0 };
+ const int protos[2] = { GNUTLS_TLS1, 0 };
+ int ret;
+
+ ret = gnutls_init(&conn->session,
+ global->server ? GNUTLS_SERVER : GNUTLS_CLIENT);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS "
+ "connection: %s", gnutls_strerror(ret));
+ return -1;
+ }
+
+ ret = gnutls_set_default_priority(conn->session);
+ if (ret < 0)
+ goto fail;
+
+ ret = gnutls_certificate_type_set_priority(conn->session, cert_types);
+ if (ret < 0)
+ goto fail;
+
+ ret = gnutls_protocol_set_priority(conn->session, protos);
+ if (ret < 0)
+ goto fail;
+
+ gnutls_transport_set_pull_function(conn->session, tls_pull_func);
+ gnutls_transport_set_push_function(conn->session, tls_push_func);
+ gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn);
+
+ return 0;
+
+fail:
+ wpa_printf(MSG_INFO, "TLS: Failed to setup new TLS connection: %s",
+ gnutls_strerror(ret));
+ gnutls_deinit(conn->session);
+ return -1;
+}
+
+
+struct tls_connection * tls_connection_init(void *ssl_ctx)
+{
+ struct tls_global *global = ssl_ctx;
+ struct tls_connection *conn;
+ int ret;
+
+ conn = os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
+
+ if (tls_gnutls_init_session(global, conn)) {
+ os_free(conn);
+ return NULL;
+ }
+
+ if (global->params_set) {
+ ret = gnutls_credentials_set(conn->session,
+ GNUTLS_CRD_CERTIFICATE,
+ global->xcred);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO, "Failed to configure "
+ "credentials: %s", gnutls_strerror(ret));
+ os_free(conn);
+ return NULL;
+ }
+ }
+
+ if (gnutls_certificate_allocate_credentials(&conn->xcred)) {
+ os_free(conn);
+ return NULL;
+ }
+
+ return conn;
+}
+
+
+void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return;
+
+#ifdef GNUTLS_IA
+ if (conn->iacred_srv)
+ gnutls_ia_free_server_credentials(conn->iacred_srv);
+ if (conn->iacred_cli)
+ gnutls_ia_free_client_credentials(conn->iacred_cli);
+ if (conn->session_keys) {
+ os_memset(conn->session_keys, 0, conn->session_keys_len);
+ os_free(conn->session_keys);
+ }
+#endif /* GNUTLS_IA */
+
+ gnutls_certificate_free_credentials(conn->xcred);
+ gnutls_deinit(conn->session);
+ os_free(conn->pre_shared_secret);
+ os_free(conn->subject_match);
+ os_free(conn->altsubject_match);
+ wpabuf_free(conn->push_buf);
+ wpabuf_free(conn->pull_buf);
+ os_free(conn);
+}
+
+
+int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
+{
+ return conn ? conn->established : 0;
+}
+
+
+int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+{
+ struct tls_global *global = ssl_ctx;
+ int ret;
+
+ if (conn == NULL)
+ return -1;
+
+ /* Shutdown previous TLS connection without notifying the peer
+ * because the connection was already terminated in practice
+ * and "close notify" shutdown alert would confuse AS. */
+ gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
+ wpabuf_free(conn->push_buf);
+ conn->push_buf = NULL;
+ conn->established = 0;
+ conn->final_phase_finished = 0;
+#ifdef GNUTLS_IA
+ if (conn->session_keys) {
+ os_memset(conn->session_keys, 0, conn->session_keys_len);
+ os_free(conn->session_keys);
+ }
+ conn->session_keys_len = 0;
+#endif /* GNUTLS_IA */
+
+ gnutls_deinit(conn->session);
+ if (tls_gnutls_init_session(global, conn)) {
+ wpa_printf(MSG_INFO, "GnuTLS: Failed to preparare new session "
+ "for session resumption use");
+ return -1;
+ }
+
+ ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
+ conn->params_set ? conn->xcred :
+ global->xcred);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO, "GnuTLS: Failed to configure credentials "
+ "for session resumption: %s", gnutls_strerror(ret));
+ return -1;
+ }
+
+ if (global->session_data) {
+ ret = gnutls_session_set_data(conn->session,
+ global->session_data,
+ global->session_data_size);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO, "GnuTLS: Failed to set session "
+ "data: %s", gnutls_strerror(ret));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+#if 0
+static int tls_match_altsubject(X509 *cert, const char *match)
+{
+ GENERAL_NAME *gen;
+ char *field, *tmp;
+ void *ext;
+ int i, found = 0;
+ size_t len;
+
+ ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+ for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+ gen = sk_GENERAL_NAME_value(ext, i);
+ switch (gen->type) {
+ case GEN_EMAIL:
+ field = "EMAIL";
+ break;
+ case GEN_DNS:
+ field = "DNS";
+ break;
+ case GEN_URI:
+ field = "URI";
+ break;
+ default:
+ field = NULL;
+ wpa_printf(MSG_DEBUG, "TLS: altSubjectName: "
+ "unsupported type=%d", gen->type);
+ break;
+ }
+
+ if (!field)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s",
+ field, gen->d.ia5->data);
+ len = os_strlen(field) + 1 +
+ strlen((char *) gen->d.ia5->data) + 1;
+ tmp = os_malloc(len);
+ if (tmp == NULL)
+ continue;
+ snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data);
+ if (strstr(tmp, match))
+ found++;
+ os_free(tmp);
+ }
+
+ return found;
+}
+#endif
+
+
+#if 0
+static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+ char buf[256];
+ X509 *err_cert;
+ int err, depth;
+ SSL *ssl;
+ struct tls_connection *conn;
+ char *match, *altmatch;
+
+ err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+ err = X509_STORE_CTX_get_error(x509_ctx);
+ depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+ ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+ X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
+
+ conn = SSL_get_app_data(ssl);
+ match = conn ? conn->subject_match : NULL;
+ altmatch = conn ? conn->altsubject_match : NULL;
+
+ if (!preverify_ok) {
+ wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
+ " error %d (%s) depth %d for '%s'", err,
+ X509_verify_cert_error_string(err), depth, buf);
+ } else {
+ wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - "
+ "preverify_ok=%d err=%d (%s) depth=%d buf='%s'",
+ preverify_ok, err,
+ X509_verify_cert_error_string(err), depth, buf);
+ if (depth == 0 && match && strstr(buf, match) == NULL) {
+ wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
+ "match with '%s'", buf, match);
+ preverify_ok = 0;
+ } else if (depth == 0 && altmatch &&
+ !tls_match_altsubject(err_cert, altmatch)) {
+ wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
+ "'%s' not found", altmatch);
+ preverify_ok = 0;
+ }
+ }
+
+ return preverify_ok;
+}
+#endif
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params)
+{
+ int ret;
+
+ if (conn == NULL || params == NULL)
+ return -1;
+
+ os_free(conn->subject_match);
+ conn->subject_match = NULL;
+ if (params->subject_match) {
+ conn->subject_match = os_strdup(params->subject_match);
+ if (conn->subject_match == NULL)
+ return -1;
+ }
+
+ os_free(conn->altsubject_match);
+ conn->altsubject_match = NULL;
+ if (params->altsubject_match) {
+ conn->altsubject_match = os_strdup(params->altsubject_match);
+ if (conn->altsubject_match == NULL)
+ return -1;
+ }
+
+ /* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
+ * to force peer validation(?) */
+
+ if (params->ca_cert) {
+ conn->verify_peer = 1;
+ ret = gnutls_certificate_set_x509_trust_file(
+ conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
+ "in PEM format: %s", params->ca_cert,
+ gnutls_strerror(ret));
+ ret = gnutls_certificate_set_x509_trust_file(
+ conn->xcred, params->ca_cert,
+ GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to read CA cert "
+ "'%s' in DER format: %s",
+ params->ca_cert,
+ gnutls_strerror(ret));
+ return -1;
+ }
+ }
+
+ if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
+ gnutls_certificate_set_verify_flags(
+ conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
+ }
+
+ if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
+ gnutls_certificate_set_verify_flags(
+ conn->xcred,
+ GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
+ }
+ }
+
+ if (params->client_cert && params->private_key) {
+ /* TODO: private_key_passwd? */
+ ret = gnutls_certificate_set_x509_key_file(
+ conn->xcred, params->client_cert, params->private_key,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+ "in PEM format: %s", gnutls_strerror(ret));
+ ret = gnutls_certificate_set_x509_key_file(
+ conn->xcred, params->client_cert,
+ params->private_key, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to read client "
+ "cert/key in DER format: %s",
+ gnutls_strerror(ret));
+ return ret;
+ }
+ }
+ } else if (params->private_key) {
+ int pkcs12_ok = 0;
+#ifdef PKCS12_FUNCS
+ /* Try to load in PKCS#12 format */
+#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
+ ret = gnutls_certificate_set_x509_simple_pkcs12_file(
+ conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
+ params->private_key_passwd);
+ if (ret != 0) {
+ wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+ "PKCS#12 format: %s", gnutls_strerror(ret));
+ return -1;
+ } else
+ pkcs12_ok = 1;
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
+#endif /* PKCS12_FUNCS */
+
+ if (!pkcs12_ok) {
+ wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
+ "included");
+ return -1;
+ }
+ }
+
+ conn->tls_ia = params->tls_ia;
+ conn->params_set = 1;
+
+ ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
+ conn->xcred);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO, "Failed to configure credentials: %s",
+ gnutls_strerror(ret));
+ }
+
+#ifdef GNUTLS_IA
+ if (conn->iacred_cli)
+ gnutls_ia_free_client_credentials(conn->iacred_cli);
+
+ ret = gnutls_ia_allocate_client_credentials(&conn->iacred_cli);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s",
+ gnutls_strerror(ret));
+ return -1;
+ }
+
+ ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA,
+ conn->iacred_cli);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s",
+ gnutls_strerror(ret));
+ gnutls_ia_free_client_credentials(conn->iacred_cli);
+ conn->iacred_cli = NULL;
+ return -1;
+ }
+#endif /* GNUTLS_IE */
+
+ return ret;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+ const struct tls_connection_params *params)
+{
+ struct tls_global *global = tls_ctx;
+ int ret;
+
+ /* Currently, global parameters are only set when running in server
+ * mode. */
+ global->server = 1;
+
+ if (global->params_set) {
+ gnutls_certificate_free_credentials(global->xcred);
+ global->params_set = 0;
+ }
+
+ ret = gnutls_certificate_allocate_credentials(&global->xcred);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "Failed to allocate global credentials "
+ "%s", gnutls_strerror(ret));
+ return -1;
+ }
+
+ if (params->ca_cert) {
+ ret = gnutls_certificate_set_x509_trust_file(
+ global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
+ "in PEM format: %s", params->ca_cert,
+ gnutls_strerror(ret));
+ ret = gnutls_certificate_set_x509_trust_file(
+ global->xcred, params->ca_cert,
+ GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to read CA cert "
+ "'%s' in DER format: %s",
+ params->ca_cert,
+ gnutls_strerror(ret));
+ goto fail;
+ }
+ }
+
+ if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
+ gnutls_certificate_set_verify_flags(
+ global->xcred,
+ GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
+ }
+
+ if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
+ gnutls_certificate_set_verify_flags(
+ global->xcred,
+ GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
+ }
+ }
+
+ if (params->client_cert && params->private_key) {
+ /* TODO: private_key_passwd? */
+ ret = gnutls_certificate_set_x509_key_file(
+ global->xcred, params->client_cert,
+ params->private_key, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+ "in PEM format: %s", gnutls_strerror(ret));
+ ret = gnutls_certificate_set_x509_key_file(
+ global->xcred, params->client_cert,
+ params->private_key, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to read client "
+ "cert/key in DER format: %s",
+ gnutls_strerror(ret));
+ goto fail;
+ }
+ }
+ } else if (params->private_key) {
+ int pkcs12_ok = 0;
+#ifdef PKCS12_FUNCS
+ /* Try to load in PKCS#12 format */
+#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
+ ret = gnutls_certificate_set_x509_simple_pkcs12_file(
+ global->xcred, params->private_key,
+ GNUTLS_X509_FMT_DER, params->private_key_passwd);
+ if (ret != 0) {
+ wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+ "PKCS#12 format: %s", gnutls_strerror(ret));
+ goto fail;
+ } else
+ pkcs12_ok = 1;
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
+#endif /* PKCS12_FUNCS */
+
+ if (!pkcs12_ok) {
+ wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
+ "included");
+ goto fail;
+ }
+ }
+
+ global->params_set = 1;
+
+ return 0;
+
+fail:
+ gnutls_certificate_free_credentials(global->xcred);
+ return -1;
+}
+
+
+int tls_global_set_verify(void *ssl_ctx, int check_crl)
+{
+ /* TODO */
+ return 0;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+ int verify_peer)
+{
+ if (conn == NULL || conn->session == NULL)
+ return -1;
+
+ conn->verify_peer = verify_peer;
+ gnutls_certificate_server_set_request(conn->session,
+ verify_peer ? GNUTLS_CERT_REQUIRE
+ : GNUTLS_CERT_REQUEST);
+
+ return 0;
+}
+
+
+int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
+ struct tls_keys *keys)
+{
+#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
+ security_parameters_st *sec;
+#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+
+ if (conn == NULL || conn->session == NULL || keys == NULL)
+ return -1;
+
+ os_memset(keys, 0, sizeof(*keys));
+
+#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
+ sec = &conn->session->security_parameters;
+ keys->master_key = sec->master_secret;
+ keys->master_key_len = TLS_MASTER_SIZE;
+ keys->client_random = sec->client_random;
+ keys->server_random = sec->server_random;
+#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+ keys->client_random =
+ (u8 *) gnutls_session_get_client_random(conn->session);
+ keys->server_random =
+ (u8 *) gnutls_session_get_server_random(conn->session);
+ /* No access to master_secret */
+#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+
+#ifdef GNUTLS_IA
+ gnutls_ia_extract_inner_secret(conn->session,
+ (char *) conn->inner_secret);
+ keys->inner_secret = conn->inner_secret;
+ keys->inner_secret_len = TLS_MASTER_SIZE;
+#endif /* GNUTLS_IA */
+
+ keys->client_random_len = TLS_RANDOM_SIZE;
+ keys->server_random_len = TLS_RANDOM_SIZE;
+
+ return 0;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+ const char *label, int server_random_first,
+ u8 *out, size_t out_len)
+{
+#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
+ if (conn == NULL || conn->session == NULL)
+ return -1;
+
+ return gnutls_prf(conn->session, os_strlen(label), label,
+ server_random_first, 0, NULL, out_len, (char *) out);
+#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
+ return -1;
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
+}
+
+
+static int tls_connection_verify_peer(struct tls_connection *conn,
+ gnutls_alert_description_t *err)
+{
+ unsigned int status, num_certs, i;
+ struct os_time now;
+ const gnutls_datum_t *certs;
+ gnutls_x509_crt_t cert;
+
+ if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) {
+ wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
+ "certificate chain");
+ *err = GNUTLS_A_INTERNAL_ERROR;
+ return -1;
+ }
+
+ if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
+ wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
+ if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
+ wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
+ "algorithm");
+ *err = GNUTLS_A_INSUFFICIENT_SECURITY;
+ }
+ if (status & GNUTLS_CERT_NOT_ACTIVATED) {
+ wpa_printf(MSG_INFO, "TLS: Certificate not yet "
+ "activated");
+ *err = GNUTLS_A_CERTIFICATE_EXPIRED;
+ }
+ if (status & GNUTLS_CERT_EXPIRED) {
+ wpa_printf(MSG_INFO, "TLS: Certificate expired");
+ *err = GNUTLS_A_CERTIFICATE_EXPIRED;
+ }
+ return -1;
+ }
+
+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+ wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a "
+ "known issuer");
+ *err = GNUTLS_A_UNKNOWN_CA;
+ return -1;
+ }
+
+ if (status & GNUTLS_CERT_REVOKED) {
+ wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked");
+ *err = GNUTLS_A_CERTIFICATE_REVOKED;
+ return -1;
+ }
+
+ os_get_time(&now);
+
+ certs = gnutls_certificate_get_peers(conn->session, &num_certs);
+ if (certs == NULL) {
+ wpa_printf(MSG_INFO, "TLS: No peer certificate chain "
+ "received");
+ *err = GNUTLS_A_UNKNOWN_CA;
+ return -1;
+ }
+
+ for (i = 0; i < num_certs; i++) {
+ char *buf;
+ size_t len;
+ if (gnutls_x509_crt_init(&cert) < 0) {
+ wpa_printf(MSG_INFO, "TLS: Certificate initialization "
+ "failed");
+ *err = GNUTLS_A_BAD_CERTIFICATE;
+ return -1;
+ }
+
+ if (gnutls_x509_crt_import(cert, &certs[i],
+ GNUTLS_X509_FMT_DER) < 0) {
+ wpa_printf(MSG_INFO, "TLS: Could not parse peer "
+ "certificate %d/%d", i + 1, num_certs);
+ gnutls_x509_crt_deinit(cert);
+ *err = GNUTLS_A_BAD_CERTIFICATE;
+ return -1;
+ }
+
+ gnutls_x509_crt_get_dn(cert, NULL, &len);
+ len++;
+ buf = os_malloc(len + 1);
+ if (buf) {
+ buf[0] = buf[len] = '\0';
+ gnutls_x509_crt_get_dn(cert, buf, &len);
+ }
+ wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
+ i + 1, num_certs, buf);
+
+ if (i == 0) {
+ /* TODO: validate subject_match and altsubject_match */
+ }
+
+ os_free(buf);
+
+ if (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
+ gnutls_x509_crt_get_activation_time(cert) > now.sec) {
+ wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
+ "not valid at this time",
+ i + 1, num_certs);
+ gnutls_x509_crt_deinit(cert);
+ *err = GNUTLS_A_CERTIFICATE_EXPIRED;
+ return -1;
+ }
+
+ gnutls_x509_crt_deinit(cert);
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn)
+{
+ int res;
+ struct wpabuf *ad;
+ wpa_printf(MSG_DEBUG, "GnuTLS: Check for possible Application Data");
+ ad = wpabuf_alloc((wpabuf_len(conn->pull_buf) + 500) * 3);
+ if (ad == NULL)
+ return NULL;
+
+ res = gnutls_record_recv(conn->session, wpabuf_mhead(ad),
+ wpabuf_size(ad));
+ wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
+ "(%s)", __func__, (int) res,
+ gnutls_strerror(res));
+ wpabuf_free(ad);
+ return NULL;
+ }
+
+ wpabuf_put(ad, res);
+ wpa_printf(MSG_DEBUG, "GnuTLS: Received %d bytes of Application Data",
+ res);
+ return ad;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ struct tls_global *global = tls_ctx;
+ struct wpabuf *out_data;
+ int ret;
+
+ if (appl_data)
+ *appl_data = NULL;
+
+ if (in_data && wpabuf_len(in_data) > 0) {
+ if (conn->pull_buf) {
+ wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+ "pull_buf", __func__,
+ (unsigned long) wpabuf_len(conn->pull_buf));
+ wpabuf_free(conn->pull_buf);
+ }
+ conn->pull_buf = wpabuf_dup(in_data);
+ if (conn->pull_buf == NULL)
+ return NULL;
+ conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
+ }
+
+ ret = gnutls_handshake(conn->session);
+ if (ret < 0) {
+ switch (ret) {
+ case GNUTLS_E_AGAIN:
+ if (global->server && conn->established &&
+ conn->push_buf == NULL) {
+ /* Need to return something to trigger
+ * completion of EAP-TLS. */
+ conn->push_buf = wpabuf_alloc(0);
+ }
+ break;
+ case GNUTLS_E_FATAL_ALERT_RECEIVED:
+ wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
+ __func__, gnutls_alert_get_name(
+ gnutls_alert_get(conn->session)));
+ conn->read_alerts++;
+ /* continue */
+ default:
+ wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
+ "-> %s", __func__, gnutls_strerror(ret));
+ conn->failed++;
+ }
+ } else {
+ size_t size;
+ gnutls_alert_description_t err;
+
+ if (conn->verify_peer &&
+ tls_connection_verify_peer(conn, &err)) {
+ wpa_printf(MSG_INFO, "TLS: Peer certificate chain "
+ "failed validation");
+ conn->failed++;
+ gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err);
+ goto out;
+ }
+
+#ifdef CONFIG_GNUTLS_EXTRA
+ if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) {
+ wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation");
+ conn->failed++;
+ return NULL;
+ }
+#endif /* CONFIG_GNUTLS_EXTRA */
+
+ if (conn->tls_ia)
+ wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake");
+ else {
+ wpa_printf(MSG_DEBUG, "TLS: Handshake completed "
+ "successfully");
+ }
+ conn->established = 1;
+ if (conn->push_buf == NULL) {
+ /* Need to return something to get final TLS ACK. */
+ conn->push_buf = wpabuf_alloc(0);
+ }
+
+ gnutls_session_get_data(conn->session, NULL, &size);
+ if (global->session_data == NULL ||
+ global->session_data_size < size) {
+ os_free(global->session_data);
+ global->session_data = os_malloc(size);
+ }
+ if (global->session_data) {
+ global->session_data_size = size;
+ gnutls_session_get_data(conn->session,
+ global->session_data,
+ &global->session_data_size);
+ }
+
+ if (conn->pull_buf && appl_data)
+ *appl_data = gnutls_get_appl_data(conn);
+ }
+
+out:
+ out_data = conn->push_buf;
+ conn->push_buf = NULL;
+ return out_data;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ ssize_t res;
+ struct wpabuf *buf;
+
+#ifdef GNUTLS_IA
+ if (conn->tls_ia)
+ res = gnutls_ia_send(conn->session, wpabuf_head(in_data),
+ wpabuf_len(in_data));
+ else
+#endif /* GNUTLS_IA */
+ res = gnutls_record_send(conn->session, wpabuf_head(in_data),
+ wpabuf_len(in_data));
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "%s: Encryption failed: %s",
+ __func__, gnutls_strerror(res));
+ return NULL;
+ }
+
+ buf = conn->push_buf;
+ conn->push_buf = NULL;
+ return buf;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ ssize_t res;
+ struct wpabuf *out;
+
+ if (conn->pull_buf) {
+ wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+ "pull_buf", __func__,
+ (unsigned long) wpabuf_len(conn->pull_buf));
+ wpabuf_free(conn->pull_buf);
+ }
+ conn->pull_buf = wpabuf_dup(in_data);
+ if (conn->pull_buf == NULL)
+ return NULL;
+ conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
+
+ /*
+ * Even though we try to disable TLS compression, it is possible that
+ * this cannot be done with all TLS libraries. Add extra buffer space
+ * to handle the possibility of the decrypted data being longer than
+ * input data.
+ */
+ out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+ if (out == NULL)
+ return NULL;
+
+#ifdef GNUTLS_IA
+ if (conn->tls_ia) {
+ res = gnutls_ia_recv(conn->session, wpabuf_mhead(out),
+ wpabuf_size(out));
+ if (res == GNUTLS_E_WARNING_IA_IPHF_RECEIVED ||
+ res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) {
+ int final = res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED;
+ wpa_printf(MSG_DEBUG, "%s: Received %sPhaseFinished",
+ __func__, final ? "Final" : "Intermediate");
+
+ res = gnutls_ia_permute_inner_secret(
+ conn->session, conn->session_keys_len,
+ (char *) conn->session_keys);
+ if (conn->session_keys) {
+ os_memset(conn->session_keys, 0,
+ conn->session_keys_len);
+ os_free(conn->session_keys);
+ }
+ conn->session_keys = NULL;
+ conn->session_keys_len = 0;
+ if (res) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to permute "
+ "inner secret: %s",
+ __func__, gnutls_strerror(res));
+ wpabuf_free(out);
+ return NULL;
+ }
+
+ res = gnutls_ia_verify_endphase(conn->session,
+ wpabuf_head(out));
+ if (res == 0) {
+ wpa_printf(MSG_DEBUG, "%s: Correct endphase "
+ "checksum", __func__);
+ } else {
+ wpa_printf(MSG_INFO, "%s: Endphase "
+ "verification failed: %s",
+ __func__, gnutls_strerror(res));
+ wpabuf_free(out);
+ return NULL;
+ }
+
+ if (final)
+ conn->final_phase_finished = 1;
+
+ return out;
+ }
+
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
+ "(%s)", __func__, (int) res,
+ gnutls_strerror(res));
+ wpabuf_free(out);
+ return NULL;
+ }
+ wpabuf_put(out, res);
+ return out;
+ }
+#endif /* GNUTLS_IA */
+
+ res = gnutls_record_recv(conn->session, wpabuf_mhead(out),
+ wpabuf_size(out));
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
+ "(%s)", __func__, (int) res, gnutls_strerror(res));
+ wpabuf_free(out);
+ return NULL;
+ }
+ wpabuf_put(out, res);
+
+ return out;
+}
+
+
+int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return 0;
+ return gnutls_session_is_resumed(conn->session);
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+ u8 *ciphers)
+{
+ /* TODO */
+ return -1;
+}
+
+
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ /* TODO */
+ buf[0] = '\0';
+ return 0;
+}
+
+
+int tls_connection_enable_workaround(void *ssl_ctx,
+ struct tls_connection *conn)
+{
+ gnutls_record_disable_padding(conn->session);
+ return 0;
+}
+
+
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len)
+{
+ /* TODO */
+ return -1;
+}
+
+
+int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+ return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+ return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+ return conn->write_alerts;
+}
+
+
+int tls_connection_get_keyblock_size(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ /* TODO */
+ return -1;
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+ unsigned int capa = 0;
+
+#ifdef GNUTLS_IA
+ capa |= TLS_CAPABILITY_IA;
+#endif /* GNUTLS_IA */
+
+ return capa;
+}
+
+
+int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
+ int tls_ia)
+{
+#ifdef GNUTLS_IA
+ int ret;
+
+ if (conn == NULL)
+ return -1;
+
+ conn->tls_ia = tls_ia;
+ if (!tls_ia)
+ return 0;
+
+ ret = gnutls_ia_allocate_server_credentials(&conn->iacred_srv);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s",
+ gnutls_strerror(ret));
+ return -1;
+ }
+
+ ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA,
+ conn->iacred_srv);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s",
+ gnutls_strerror(ret));
+ gnutls_ia_free_server_credentials(conn->iacred_srv);
+ conn->iacred_srv = NULL;
+ return -1;
+ }
+
+ return 0;
+#else /* GNUTLS_IA */
+ return -1;
+#endif /* GNUTLS_IA */
+}
+
+
+struct wpabuf * tls_connection_ia_send_phase_finished(
+ void *tls_ctx, struct tls_connection *conn, int final)
+{
+#ifdef GNUTLS_IA
+ int ret;
+ struct wpabuf *buf;
+
+ if (conn == NULL || conn->session == NULL || !conn->tls_ia)
+ return NULL;
+
+ ret = gnutls_ia_permute_inner_secret(conn->session,
+ conn->session_keys_len,
+ (char *) conn->session_keys);
+ if (conn->session_keys) {
+ os_memset(conn->session_keys, 0, conn->session_keys_len);
+ os_free(conn->session_keys);
+ }
+ conn->session_keys = NULL;
+ conn->session_keys_len = 0;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to permute inner secret: %s",
+ __func__, gnutls_strerror(ret));
+ return NULL;
+ }
+
+ ret = gnutls_ia_endphase_send(conn->session, final);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to send endphase: %s",
+ __func__, gnutls_strerror(ret));
+ return NULL;
+ }
+
+ buf = conn->push_buf;
+ conn->push_buf = NULL;
+ return buf;
+#else /* GNUTLS_IA */
+ return NULL;
+#endif /* GNUTLS_IA */
+}
+
+
+int tls_connection_ia_final_phase_finished(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+
+ return conn->final_phase_finished;
+}
+
+
+int tls_connection_ia_permute_inner_secret(void *tls_ctx,
+ struct tls_connection *conn,
+ const u8 *key, size_t key_len)
+{
+#ifdef GNUTLS_IA
+ if (conn == NULL || !conn->tls_ia)
+ return -1;
+
+ if (conn->session_keys) {
+ os_memset(conn->session_keys, 0, conn->session_keys_len);
+ os_free(conn->session_keys);
+ }
+ conn->session_keys_len = 0;
+
+ if (key) {
+ conn->session_keys = os_malloc(key_len);
+ if (conn->session_keys == NULL)
+ return -1;
+ os_memcpy(conn->session_keys, key, key_len);
+ conn->session_keys_len = key_len;
+ } else {
+ conn->session_keys = NULL;
+ conn->session_keys_len = 0;
+ }
+
+ return 0;
+#else /* GNUTLS_IA */
+ return -1;
+#endif /* GNUTLS_IA */
+}
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+ struct tls_connection *conn,
+ tls_session_ticket_cb cb, void *ctx)
+{
+ return -1;
+}
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
new file mode 100644
index 0000000..64124d8
--- /dev/null
+++ b/src/crypto/tls_internal.c
@@ -0,0 +1,651 @@
+/*
+ * TLS interface functions and an internal TLS implementation
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file interface functions for hostapd/wpa_supplicant to use the
+ * integrated TLSv1 implementation.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "tls.h"
+#include "tls/tlsv1_client.h"
+#include "tls/tlsv1_server.h"
+
+
+static int tls_ref_count = 0;
+
+struct tls_global {
+ int server;
+ struct tlsv1_credentials *server_cred;
+ int check_crl;
+};
+
+struct tls_connection {
+ struct tlsv1_client *client;
+ struct tlsv1_server *server;
+};
+
+
+void * tls_init(const struct tls_config *conf)
+{
+ struct tls_global *global;
+
+ if (tls_ref_count == 0) {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (tlsv1_client_global_init())
+ return NULL;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (tlsv1_server_global_init())
+ return NULL;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ }
+ tls_ref_count++;
+
+ global = os_zalloc(sizeof(*global));
+ if (global == NULL)
+ return NULL;
+
+ return global;
+}
+
+void tls_deinit(void *ssl_ctx)
+{
+ struct tls_global *global = ssl_ctx;
+ tls_ref_count--;
+ if (tls_ref_count == 0) {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ tlsv1_client_global_deinit();
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ tlsv1_cred_free(global->server_cred);
+ tlsv1_server_global_deinit();
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ }
+ os_free(global);
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+ return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+ struct tls_connection *conn;
+ struct tls_global *global = tls_ctx;
+
+ conn = os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
+
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (!global->server) {
+ conn->client = tlsv1_client_init();
+ if (conn->client == NULL) {
+ os_free(conn);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (global->server) {
+ conn->server = tlsv1_server_init(global->server_cred);
+ if (conn->server == NULL) {
+ os_free(conn);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+
+ return conn;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ tlsv1_client_deinit(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ tlsv1_server_deinit(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ os_free(conn);
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_established(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_established(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return 0;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_shutdown(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_shutdown(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ struct tlsv1_credentials *cred;
+
+ if (conn->client == NULL)
+ return -1;
+
+ cred = tlsv1_cred_alloc();
+ if (cred == NULL)
+ return -1;
+
+ if (tlsv1_set_ca_cert(cred, params->ca_cert,
+ params->ca_cert_blob, params->ca_cert_blob_len,
+ params->ca_path)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
+ "certificates");
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ if (tlsv1_set_cert(cred, params->client_cert,
+ params->client_cert_blob,
+ params->client_cert_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to configure client "
+ "certificate");
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ if (tlsv1_set_private_key(cred, params->private_key,
+ params->private_key_passwd,
+ params->private_key_blob,
+ params->private_key_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
+ params->dh_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ if (tlsv1_client_set_cred(conn->client, cred) < 0) {
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
+ return 0;
+#else /* CONFIG_TLS_INTERNAL_CLIENT */
+ return -1;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+ const struct tls_connection_params *params)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ struct tls_global *global = tls_ctx;
+ struct tlsv1_credentials *cred;
+
+ /* Currently, global parameters are only set when running in server
+ * mode. */
+ global->server = 1;
+ tlsv1_cred_free(global->server_cred);
+ global->server_cred = cred = tlsv1_cred_alloc();
+ if (cred == NULL)
+ return -1;
+
+ if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob,
+ params->ca_cert_blob_len, params->ca_path)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
+ "certificates");
+ return -1;
+ }
+
+ if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob,
+ params->client_cert_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to configure server "
+ "certificate");
+ return -1;
+ }
+
+ if (tlsv1_set_private_key(cred, params->private_key,
+ params->private_key_passwd,
+ params->private_key_blob,
+ params->private_key_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+ return -1;
+ }
+
+ if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
+ params->dh_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
+ return -1;
+ }
+
+ return 0;
+#else /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+ struct tls_global *global = tls_ctx;
+ global->check_crl = check_crl;
+ return 0;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+ int verify_peer)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_set_verify(conn->server, verify_peer);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
+ int tls_ia)
+{
+ return -1;
+}
+
+
+int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
+ struct tls_keys *keys)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_get_keys(conn->client, keys);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_get_keys(conn->server, keys);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+ const char *label, int server_random_first,
+ u8 *out, size_t out_len)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client) {
+ return tlsv1_client_prf(conn->client, label,
+ server_random_first,
+ out, out_len);
+ }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server) {
+ return tlsv1_server_prf(conn->server, label,
+ server_random_first,
+ out, out_len);
+ }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ u8 *res, *ad;
+ size_t res_len, ad_len;
+ struct wpabuf *out;
+
+ if (conn->client == NULL)
+ return NULL;
+
+ ad = NULL;
+ res = tlsv1_client_handshake(conn->client,
+ in_data ? wpabuf_head(in_data) : NULL,
+ in_data ? wpabuf_len(in_data) : 0,
+ &res_len, &ad, &ad_len);
+ if (res == NULL)
+ return NULL;
+ out = wpabuf_alloc_ext_data(res, res_len);
+ if (out == NULL) {
+ os_free(res);
+ os_free(ad);
+ return NULL;
+ }
+ if (appl_data) {
+ if (ad) {
+ *appl_data = wpabuf_alloc_ext_data(ad, ad_len);
+ if (*appl_data == NULL)
+ os_free(ad);
+ } else
+ *appl_data = NULL;
+ } else
+ os_free(ad);
+
+ return out;
+#else /* CONFIG_TLS_INTERNAL_CLIENT */
+ return NULL;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ u8 *res;
+ size_t res_len;
+ struct wpabuf *out;
+
+ if (conn->server == NULL)
+ return NULL;
+
+ if (appl_data)
+ *appl_data = NULL;
+
+ res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data),
+ wpabuf_len(in_data), &res_len);
+ if (res == NULL && tlsv1_server_established(conn->server))
+ return wpabuf_alloc(0);
+ if (res == NULL)
+ return NULL;
+ out = wpabuf_alloc_ext_data(res, res_len);
+ if (out == NULL) {
+ os_free(res);
+ return NULL;
+ }
+
+ return out;
+#else /* CONFIG_TLS_INTERNAL_SERVER */
+ return NULL;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client) {
+ struct wpabuf *buf;
+ int res;
+ buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+ if (buf == NULL)
+ return NULL;
+ res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data),
+ wpabuf_len(in_data),
+ wpabuf_mhead(buf),
+ wpabuf_size(buf));
+ if (res < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+ return buf;
+ }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server) {
+ struct wpabuf *buf;
+ int res;
+ buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+ if (buf == NULL)
+ return NULL;
+ res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data),
+ wpabuf_len(in_data),
+ wpabuf_mhead(buf),
+ wpabuf_size(buf));
+ if (res < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+ return buf;
+ }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return NULL;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client) {
+ struct wpabuf *buf;
+ int res;
+ buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+ if (buf == NULL)
+ return NULL;
+ res = tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
+ wpabuf_len(in_data),
+ wpabuf_mhead(buf),
+ wpabuf_size(buf));
+ if (res < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+ return buf;
+ }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server) {
+ struct wpabuf *buf;
+ int res;
+ buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+ if (buf == NULL)
+ return NULL;
+ res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data),
+ wpabuf_len(in_data),
+ wpabuf_mhead(buf),
+ wpabuf_size(buf));
+ if (res < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+ return buf;
+ }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return NULL;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_resumed(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_resumed(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+ u8 *ciphers)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_set_cipher_list(conn->client, ciphers);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_set_cipher_list(conn->server, ciphers);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ if (conn == NULL)
+ return -1;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_get_cipher(conn->client, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_get_cipher(conn->server, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client) {
+ return tlsv1_client_hello_ext(conn->client, ext_type,
+ data, data_len);
+ }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+ return -1;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_get_keyblock_size(void *tls_ctx,
+ struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_get_keyblock_size(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_get_keyblock_size(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+ return 0;
+}
+
+
+struct wpabuf * tls_connection_ia_send_phase_finished(
+ void *tls_ctx, struct tls_connection *conn, int final)
+{
+ return NULL;
+}
+
+
+int tls_connection_ia_final_phase_finished(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+int tls_connection_ia_permute_inner_secret(void *tls_ctx,
+ struct tls_connection *conn,
+ const u8 *key, size_t key_len)
+{
+ return -1;
+}
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+ struct tls_connection *conn,
+ tls_session_ticket_cb cb,
+ void *ctx)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client) {
+ tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx);
+ return 0;
+ }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server) {
+ tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx);
+ return 0;
+ }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+ return -1;
+}
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
new file mode 100644
index 0000000..0c836bb
--- /dev/null
+++ b/src/crypto/tls_none.c
@@ -0,0 +1,229 @@
+/*
+ * SSL/TLS interface functions for no TLS case
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "tls.h"
+
+void * tls_init(const struct tls_config *conf)
+{
+ return (void *) 1;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+ return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+ return NULL;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params)
+{
+ return -1;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+ const struct tls_connection_params *params)
+{
+ return -1;
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+ return -1;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+ int verify_peer)
+{
+ return -1;
+}
+
+
+int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
+ int tls_ia)
+{
+ return -1;
+}
+
+
+int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
+ struct tls_keys *keys)
+{
+ return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+ const char *label, int server_random_first,
+ u8 *out, size_t out_len)
+{
+ return -1;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return NULL;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return NULL;
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ return NULL;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ return NULL;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+ u8 *ciphers)
+{
+ return -1;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ return -1;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len)
+{
+ return -1;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_get_keyblock_size(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+ return 0;
+}
+
+
+struct wpabuf * tls_connection_ia_send_phase_finished(
+ void *tls_ctx, struct tls_connection *conn, int final)
+{
+ return NULL;
+}
+
+
+int tls_connection_ia_final_phase_finished(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+int tls_connection_ia_permute_inner_secret(void *tls_ctx,
+ struct tls_connection *conn,
+ const u8 *key, size_t key_len)
+{
+ return -1;
+}
diff --git a/src/crypto/tls_nss.c b/src/crypto/tls_nss.c
new file mode 100644
index 0000000..ad834b6
--- /dev/null
+++ b/src/crypto/tls_nss.c
@@ -0,0 +1,680 @@
+/*
+ * SSL/TLS interface functions for NSS
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <nspr/prtypes.h>
+#include <nspr/plarenas.h>
+#include <nspr/plhash.h>
+#include <nspr/prio.h>
+#include <nspr/prclist.h>
+#include <nspr/prlock.h>
+#include <nspr/prinit.h>
+#include <nspr/prerror.h>
+#include <nspr/prmem.h>
+#include <nss/nss.h>
+#include <nss/nssilckt.h>
+#include <nss/ssl.h>
+#include <nss/pk11func.h>
+#include <nss/secerr.h>
+
+#include "common.h"
+#include "tls.h"
+
+static int tls_nss_ref_count = 0;
+
+static PRDescIdentity nss_layer_id;
+
+
+struct tls_connection {
+ PRFileDesc *fd;
+
+ int established;
+ int verify_peer;
+ u8 *push_buf, *pull_buf, *pull_buf_offset;
+ size_t push_buf_len, pull_buf_len;
+};
+
+
+static PRStatus nss_io_close(PRFileDesc *fd)
+{
+ wpa_printf(MSG_DEBUG, "NSS: I/O close");
+ return PR_SUCCESS;
+}
+
+
+static PRInt32 nss_io_read(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+ wpa_printf(MSG_DEBUG, "NSS: I/O read(%d)", amount);
+ return PR_FAILURE;
+}
+
+
+static PRInt32 nss_io_write(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+ wpa_printf(MSG_DEBUG, "NSS: I/O write(%d)", amount);
+ return PR_FAILURE;
+}
+
+
+static PRInt32 nss_io_writev(PRFileDesc *fd, const PRIOVec *iov,
+ PRInt32 iov_size, PRIntervalTime timeout)
+{
+ wpa_printf(MSG_DEBUG, "NSS: I/O writev(%d)", iov_size);
+ return PR_FAILURE;
+}
+
+
+static PRInt32 nss_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ struct tls_connection *conn = (struct tls_connection *) fd->secret;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "NSS: I/O recv(%d)", amount);
+
+ if (conn->pull_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "NSS: No data available to be read yet");
+ return PR_FAILURE;
+ }
+
+ end = conn->pull_buf + conn->pull_buf_len;
+ if (end - conn->pull_buf_offset < amount)
+ amount = end - conn->pull_buf_offset;
+ os_memcpy(buf, conn->pull_buf_offset, amount);
+ conn->pull_buf_offset += amount;
+ if (conn->pull_buf_offset == end) {
+ wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
+ os_free(conn->pull_buf);
+ conn->pull_buf = conn->pull_buf_offset = NULL;
+ conn->pull_buf_len = 0;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
+ __func__,
+ (unsigned long) (end - conn->pull_buf_offset));
+ }
+ return amount;
+}
+
+
+static PRInt32 nss_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, PRIntervalTime timeout)
+{
+ struct tls_connection *conn = (struct tls_connection *) fd->secret;
+ u8 *nbuf;
+
+ wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
+ wpa_hexdump(MSG_MSGDUMP, "NSS: I/O send data", buf, amount);
+
+ nbuf = os_realloc(conn->push_buf, conn->push_buf_len + amount);
+ if (nbuf == NULL) {
+ wpa_printf(MSG_ERROR, "NSS: Failed to allocate memory for the "
+ "data to be sent");
+ return PR_FAILURE;
+ }
+ os_memcpy(nbuf + conn->push_buf_len, buf, amount);
+ conn->push_buf = nbuf;
+ conn->push_buf_len += amount;
+
+ return amount;
+}
+
+
+static PRInt32 nss_io_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+ PRIntn flags, PRNetAddr *addr,
+ PRIntervalTime timeout)
+{
+ wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
+ return PR_FAILURE;
+}
+
+
+static PRInt32 nss_io_sendto(PRFileDesc *fd, const void *buf, PRInt32 amount,
+ PRIntn flags, const PRNetAddr *addr,
+ PRIntervalTime timeout)
+{
+ wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
+ return PR_FAILURE;
+}
+
+
+static PRStatus nss_io_getpeername(PRFileDesc *fd, PRNetAddr *addr)
+{
+ wpa_printf(MSG_DEBUG, "NSS: I/O getpeername");
+
+ /*
+ * It Looks like NSS only supports IPv4 and IPv6 TCP sockets. Provide a
+ * fake IPv4 address to work around this even though we are not really
+ * using TCP.
+ */
+ os_memset(addr, 0, sizeof(*addr));
+ addr->inet.family = PR_AF_INET;
+
+ return PR_SUCCESS;
+}
+
+
+static PRStatus nss_io_getsocketoption(PRFileDesc *fd,
+ PRSocketOptionData *data)
+{
+ switch (data->option) {
+ case PR_SockOpt_Nonblocking:
+ wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(Nonblocking)");
+ data->value.non_blocking = PR_TRUE;
+ return PR_SUCCESS;
+ default:
+ wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(%d)",
+ data->option);
+ return PR_FAILURE;
+ }
+}
+
+
+static const PRIOMethods nss_io = {
+ PR_DESC_LAYERED,
+ nss_io_close,
+ nss_io_read,
+ nss_io_write,
+ NULL /* available */,
+ NULL /* available64 */,
+ NULL /* fsync */,
+ NULL /* fseek */,
+ NULL /* fseek64 */,
+ NULL /* fileinfo */,
+ NULL /* fileinfo64 */,
+ nss_io_writev,
+ NULL /* connect */,
+ NULL /* accept */,
+ NULL /* bind */,
+ NULL /* listen */,
+ NULL /* shutdown */,
+ nss_io_recv,
+ nss_io_send,
+ nss_io_recvfrom,
+ nss_io_sendto,
+ NULL /* poll */,
+ NULL /* acceptread */,
+ NULL /* transmitfile */,
+ NULL /* getsockname */,
+ nss_io_getpeername,
+ NULL /* reserved_fn_6 */,
+ NULL /* reserved_fn_5 */,
+ nss_io_getsocketoption,
+ NULL /* setsocketoption */,
+ NULL /* sendfile */,
+ NULL /* connectcontinue */,
+ NULL /* reserved_fn_3 */,
+ NULL /* reserved_fn_2 */,
+ NULL /* reserved_fn_1 */,
+ NULL /* reserved_fn_0 */
+};
+
+
+static char * nss_password_cb(PK11SlotInfo *slot, PRBool retry, void *arg)
+{
+ wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__);
+ return NULL;
+}
+
+
+void * tls_init(const struct tls_config *conf)
+{
+ char *dir;
+
+ tls_nss_ref_count++;
+ if (tls_nss_ref_count > 1)
+ return (void *) 1;
+
+ PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+
+ nss_layer_id = PR_GetUniqueIdentity("wpa_supplicant");
+
+ PK11_SetPasswordFunc(nss_password_cb);
+
+ dir = getenv("SSL_DIR");
+ if (dir) {
+ if (NSS_Init(dir) != SECSuccess) {
+ wpa_printf(MSG_ERROR, "NSS: NSS_Init(cert_dir=%s) "
+ "failed", dir);
+ return NULL;
+ }
+ } else {
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ wpa_printf(MSG_ERROR, "NSS: NSS_NoDB_Init(NULL) "
+ "failed");
+ return NULL;
+ }
+ }
+
+ if (SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, PR_FALSE) !=
+ SECSuccess ||
+ SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_FALSE) != SECSuccess ||
+ SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE) != SECSuccess ||
+ SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE) != SECSuccess) {
+ wpa_printf(MSG_ERROR, "NSS: SSL_OptionSetDefault failed");
+ return NULL;
+ }
+
+ if (NSS_SetDomesticPolicy() != SECSuccess) {
+ wpa_printf(MSG_ERROR, "NSS: NSS_SetDomesticPolicy() failed");
+ return NULL;
+ }
+
+ return (void *) 1;
+}
+
+void tls_deinit(void *ssl_ctx)
+{
+ tls_nss_ref_count--;
+ if (tls_nss_ref_count == 0) {
+ if (NSS_Shutdown() != SECSuccess)
+ wpa_printf(MSG_ERROR, "NSS: NSS_Shutdown() failed");
+ }
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+ return 0;
+}
+
+
+static SECStatus nss_bad_cert_cb(void *arg, PRFileDesc *fd)
+{
+ struct tls_connection *conn = arg;
+ SECStatus res = SECSuccess;
+ PRErrorCode err;
+ CERTCertificate *cert;
+ char *subject, *issuer;
+
+ err = PR_GetError();
+ if (IS_SEC_ERROR(err))
+ wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (sec err "
+ "%d)", err - SEC_ERROR_BASE);
+ else
+ wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (err %d)",
+ err);
+ cert = SSL_PeerCertificate(fd);
+ subject = CERT_NameToAscii(&cert->subject);
+ issuer = CERT_NameToAscii(&cert->issuer);
+ wpa_printf(MSG_DEBUG, "NSS: Peer certificate subject='%s' issuer='%s'",
+ subject, issuer);
+ CERT_DestroyCertificate(cert);
+ PR_Free(subject);
+ PR_Free(issuer);
+ if (conn->verify_peer)
+ res = SECFailure;
+
+ return res;
+}
+
+
+static void nss_handshake_cb(PRFileDesc *fd, void *client_data)
+{
+ struct tls_connection *conn = client_data;
+ wpa_printf(MSG_DEBUG, "NSS: Handshake completed");
+ conn->established = 1;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+ struct tls_connection *conn;
+
+ conn = os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
+
+ conn->fd = PR_CreateIOLayerStub(nss_layer_id, &nss_io);
+ if (conn->fd == NULL) {
+ os_free(conn);
+ return NULL;
+ }
+ conn->fd->secret = (void *) conn;
+
+ conn->fd = SSL_ImportFD(NULL, conn->fd);
+ if (conn->fd == NULL) {
+ os_free(conn);
+ return NULL;
+ }
+
+ if (SSL_OptionSet(conn->fd, SSL_SECURITY, PR_TRUE) != SECSuccess ||
+ SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) !=
+ SECSuccess ||
+ SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) !=
+ SECSuccess ||
+ SSL_OptionSet(conn->fd, SSL_ENABLE_TLS, PR_TRUE) != SECSuccess ||
+ SSL_BadCertHook(conn->fd, nss_bad_cert_cb, conn) != SECSuccess ||
+ SSL_HandshakeCallback(conn->fd, nss_handshake_cb, conn) !=
+ SECSuccess) {
+ wpa_printf(MSG_ERROR, "NSS: Failed to set options");
+ PR_Close(conn->fd);
+ os_free(conn);
+ return NULL;
+ }
+
+ SSL_ResetHandshake(conn->fd, PR_FALSE);
+
+ return conn;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+ PR_Close(conn->fd);
+ os_free(conn->push_buf);
+ os_free(conn->pull_buf);
+ os_free(conn);
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+ return conn->established;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params)
+{
+ wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__);
+ return 0;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+ const struct tls_connection_params *params)
+{
+ return -1;
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+ return -1;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+ int verify_peer)
+{
+ conn->verify_peer = verify_peer;
+ return 0;
+}
+
+
+int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
+ int tls_ia)
+{
+ return -1;
+}
+
+
+int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
+ struct tls_keys *keys)
+{
+ /* NSS does not export master secret or client/server random. */
+ return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+ const char *label, int server_random_first,
+ u8 *out, size_t out_len)
+{
+ if (conn == NULL || server_random_first) {
+ wpa_printf(MSG_INFO, "NSS: Unsupported PRF request "
+ "(server_random_first=%d)",
+ server_random_first);
+ return -1;
+ }
+
+ if (SSL_ExportKeyingMaterial(conn->fd, label, NULL, 0, out, out_len) !=
+ SECSuccess) {
+ wpa_printf(MSG_INFO, "NSS: Failed to use TLS extractor "
+ "(label='%s' out_len=%d", label, (int) out_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ struct wpabuf *out_data;
+
+ wpa_printf(MSG_DEBUG, "NSS: handshake: in_len=%u",
+ in_data ? (unsigned int) wpabuf_len(in_data) : 0);
+
+ if (appl_data)
+ *appl_data = NULL;
+
+ if (in_data && wpabuf_len(in_data) > 0) {
+ if (conn->pull_buf) {
+ wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+ "pull_buf", __func__,
+ (unsigned long) conn->pull_buf_len);
+ os_free(conn->pull_buf);
+ }
+ conn->pull_buf = os_malloc(wpabuf_len(in_data));
+ if (conn->pull_buf == NULL)
+ return NULL;
+ os_memcpy(conn->pull_buf, wpabuf_head(in_data),
+ wpabuf_len(in_data));
+ conn->pull_buf_offset = conn->pull_buf;
+ conn->pull_buf_len = wpabuf_len(in_data);
+ }
+
+ SSL_ForceHandshake(conn->fd);
+
+ if (conn->established && conn->push_buf == NULL) {
+ /* Need to return something to get final TLS ACK. */
+ conn->push_buf = os_malloc(1);
+ }
+
+ if (conn->push_buf == NULL)
+ return NULL;
+ out_data = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len);
+ if (out_data == NULL)
+ os_free(conn->push_buf);
+ conn->push_buf = NULL;
+ conn->push_buf_len = 0;
+ return out_data;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return NULL;
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ PRInt32 res;
+ struct wpabuf *buf;
+
+ wpa_printf(MSG_DEBUG, "NSS: encrypt %d bytes",
+ (int) wpabuf_len(in_data));
+ res = PR_Send(conn->fd, wpabuf_head(in_data), wpabuf_len(in_data), 0,
+ 0);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "NSS: Encryption failed");
+ return NULL;
+ }
+ if (conn->push_buf == NULL)
+ return NULL;
+ buf = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len);
+ if (buf == NULL)
+ os_free(conn->push_buf);
+ conn->push_buf = NULL;
+ conn->push_buf_len = 0;
+ return buf;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ PRInt32 res;
+ struct wpabuf *out;
+
+ wpa_printf(MSG_DEBUG, "NSS: decrypt %d bytes",
+ (int) wpabuf_len(in_data));
+ if (conn->pull_buf) {
+ wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+ "pull_buf", __func__,
+ (unsigned long) conn->pull_buf_len);
+ os_free(conn->pull_buf);
+ }
+ conn->pull_buf = os_malloc(wpabuf_len(in_data));
+ if (conn->pull_buf == NULL)
+ return NULL;
+ os_memcpy(conn->pull_buf, wpabuf_head(in_data), wpabuf_len(in_data));
+ conn->pull_buf_offset = conn->pull_buf;
+ conn->pull_buf_len = wpabuf_len(in_data);
+
+ /*
+ * Even though we try to disable TLS compression, it is possible that
+ * this cannot be done with all TLS libraries. Add extra buffer space
+ * to handle the possibility of the decrypted data being longer than
+ * input data.
+ */
+ out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+ if (out == NULL)
+ return NULL;
+
+ res = PR_Recv(conn->fd, wpabuf_mhead(out), wpabuf_size(out), 0, 0);
+ wpa_printf(MSG_DEBUG, "NSS: PR_Recv: %d", res);
+ if (res < 0) {
+ wpabuf_free(out);
+ return NULL;
+ }
+ wpabuf_put(out, res);
+
+ return out;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+ u8 *ciphers)
+{
+ return -1;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ return -1;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len)
+{
+ return -1;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_get_keyblock_size(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+ return 0;
+}
+
+
+struct wpabuf * tls_connection_ia_send_phase_finished(
+ void *tls_ctx, struct tls_connection *conn, int final)
+{
+ return NULL;
+}
+
+
+int tls_connection_ia_final_phase_finished(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+int tls_connection_ia_permute_inner_secret(void *tls_ctx,
+ struct tls_connection *conn,
+ const u8 *key, size_t key_len)
+{
+ return -1;
+}
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+ struct tls_connection *conn,
+ tls_session_ticket_cb cb,
+ void *ctx)
+{
+ return -1;
+}
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
new file mode 100644
index 0000000..bf92a11
--- /dev/null
+++ b/src/crypto/tls_openssl.c
@@ -0,0 +1,2992 @@
+/*
+ * SSL/TLS interface functions for OpenSSL
+ * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#ifndef CONFIG_SMARTCARD
+#ifndef OPENSSL_NO_ENGINE
+#define OPENSSL_NO_ENGINE
+#endif
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509v3.h>
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif /* OPENSSL_NO_ENGINE */
+
+#ifdef ANDROID
+#include <openssl/pem.h>
+#include "keystore_get.h"
+#endif /* ANDROID */
+
+#include "common.h"
+#include "crypto.h"
+#include "tls.h"
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+#define OPENSSL_d2i_TYPE const unsigned char **
+#else
+#define OPENSSL_d2i_TYPE unsigned char **
+#endif
+
+#ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT
+#ifdef SSL_OP_NO_TICKET
+/*
+ * Session ticket override patch was merged into OpenSSL 0.9.9 tree on
+ * 2008-11-15. This version uses a bit different API compared to the old patch.
+ */
+#define CONFIG_OPENSSL_TICKET_OVERRIDE
+#endif
+#endif
+
+static int tls_openssl_ref_count = 0;
+
+struct tls_global {
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data);
+ void *cb_ctx;
+};
+
+static struct tls_global *tls_global = NULL;
+
+
+struct tls_connection {
+ SSL *ssl;
+ BIO *ssl_in, *ssl_out;
+#ifndef OPENSSL_NO_ENGINE
+ ENGINE *engine; /* functional reference to the engine */
+ EVP_PKEY *private_key; /* the private key if using engine */
+#endif /* OPENSSL_NO_ENGINE */
+ char *subject_match, *altsubject_match;
+ int read_alerts, write_alerts, failed;
+
+ tls_session_ticket_cb session_ticket_cb;
+ void *session_ticket_cb_ctx;
+
+ /* SessionTicket received from OpenSSL hello_extension_cb (server) */
+ u8 *session_ticket;
+ size_t session_ticket_len;
+
+ unsigned int ca_cert_verify:1;
+ unsigned int cert_probe:1;
+ unsigned int server_cert_only:1;
+
+ u8 srv_cert_hash[32];
+};
+
+
+#ifdef CONFIG_NO_STDOUT_DEBUG
+
+static void _tls_show_errors(void)
+{
+ unsigned long err;
+
+ while ((err = ERR_get_error())) {
+ /* Just ignore the errors, since stdout is disabled */
+ }
+}
+#define tls_show_errors(l, f, t) _tls_show_errors()
+
+#else /* CONFIG_NO_STDOUT_DEBUG */
+
+static void tls_show_errors(int level, const char *func, const char *txt)
+{
+ unsigned long err;
+
+ wpa_printf(level, "OpenSSL: %s - %s %s",
+ func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+ while ((err = ERR_get_error())) {
+ wpa_printf(MSG_INFO, "OpenSSL: pending error: %s",
+ ERR_error_string(err, NULL));
+ }
+}
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+
+/* Windows CryptoAPI and access to certificate stores */
+#include <wincrypt.h>
+
+#ifdef __MINGW32_VERSION
+/*
+ * MinGW does not yet include all the needed definitions for CryptoAPI, so
+ * define here whatever extra is needed.
+ */
+#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16)
+#define CERT_STORE_READONLY_FLAG 0x00008000
+#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
+
+#endif /* __MINGW32_VERSION */
+
+
+struct cryptoapi_rsa_data {
+ const CERT_CONTEXT *cert;
+ HCRYPTPROV crypt_prov;
+ DWORD key_spec;
+ BOOL free_crypt_prov;
+};
+
+
+static void cryptoapi_error(const char *msg)
+{
+ wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u",
+ msg, (unsigned int) GetLastError());
+}
+
+
+static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from,
+ unsigned char *to, RSA *rsa, int padding)
+{
+ wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+ return 0;
+}
+
+
+static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from,
+ unsigned char *to, RSA *rsa, int padding)
+{
+ wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+ return 0;
+}
+
+
+static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from,
+ unsigned char *to, RSA *rsa, int padding)
+{
+ struct cryptoapi_rsa_data *priv =
+ (struct cryptoapi_rsa_data *) rsa->meth->app_data;
+ HCRYPTHASH hash;
+ DWORD hash_size, len, i;
+ unsigned char *buf = NULL;
+ int ret = 0;
+
+ if (priv == NULL) {
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+ ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ if (padding != RSA_PKCS1_PADDING) {
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+ RSA_R_UNKNOWN_PADDING_TYPE);
+ return 0;
+ }
+
+ if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) {
+ wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported",
+ __func__);
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+ RSA_R_INVALID_MESSAGE_LENGTH);
+ return 0;
+ }
+
+ if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash))
+ {
+ cryptoapi_error("CryptCreateHash failed");
+ return 0;
+ }
+
+ len = sizeof(hash_size);
+ if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len,
+ 0)) {
+ cryptoapi_error("CryptGetHashParam failed");
+ goto err;
+ }
+
+ if ((int) hash_size != flen) {
+ wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)",
+ (unsigned) hash_size, flen);
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+ RSA_R_INVALID_MESSAGE_LENGTH);
+ goto err;
+ }
+ if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) {
+ cryptoapi_error("CryptSetHashParam failed");
+ goto err;
+ }
+
+ len = RSA_size(rsa);
+ buf = os_malloc(len);
+ if (buf == NULL) {
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) {
+ cryptoapi_error("CryptSignHash failed");
+ goto err;
+ }
+
+ for (i = 0; i < len; i++)
+ to[i] = buf[len - i - 1];
+ ret = len;
+
+err:
+ os_free(buf);
+ CryptDestroyHash(hash);
+
+ return ret;
+}
+
+
+static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from,
+ unsigned char *to, RSA *rsa, int padding)
+{
+ wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+ return 0;
+}
+
+
+static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv)
+{
+ if (priv == NULL)
+ return;
+ if (priv->crypt_prov && priv->free_crypt_prov)
+ CryptReleaseContext(priv->crypt_prov, 0);
+ if (priv->cert)
+ CertFreeCertificateContext(priv->cert);
+ os_free(priv);
+}
+
+
+static int cryptoapi_finish(RSA *rsa)
+{
+ cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data);
+ os_free((void *) rsa->meth);
+ rsa->meth = NULL;
+ return 1;
+}
+
+
+static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store)
+{
+ HCERTSTORE cs;
+ const CERT_CONTEXT *ret = NULL;
+
+ cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0,
+ store | CERT_STORE_OPEN_EXISTING_FLAG |
+ CERT_STORE_READONLY_FLAG, L"MY");
+ if (cs == NULL) {
+ cryptoapi_error("Failed to open 'My system store'");
+ return NULL;
+ }
+
+ if (strncmp(name, "cert://", 7) == 0) {
+ unsigned short wbuf[255];
+ MultiByteToWideChar(CP_ACP, 0, name + 7, -1, wbuf, 255);
+ ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING |
+ PKCS_7_ASN_ENCODING,
+ 0, CERT_FIND_SUBJECT_STR,
+ wbuf, NULL);
+ } else if (strncmp(name, "hash://", 7) == 0) {
+ CRYPT_HASH_BLOB blob;
+ int len;
+ const char *hash = name + 7;
+ unsigned char *buf;
+
+ len = os_strlen(hash) / 2;
+ buf = os_malloc(len);
+ if (buf && hexstr2bin(hash, buf, len) == 0) {
+ blob.cbData = len;
+ blob.pbData = buf;
+ ret = CertFindCertificateInStore(cs,
+ X509_ASN_ENCODING |
+ PKCS_7_ASN_ENCODING,
+ 0, CERT_FIND_HASH,
+ &blob, NULL);
+ }
+ os_free(buf);
+ }
+
+ CertCloseStore(cs, 0);
+
+ return ret;
+}
+
+
+static int tls_cryptoapi_cert(SSL *ssl, const char *name)
+{
+ X509 *cert = NULL;
+ RSA *rsa = NULL, *pub_rsa;
+ struct cryptoapi_rsa_data *priv;
+ RSA_METHOD *rsa_meth;
+
+ if (name == NULL ||
+ (strncmp(name, "cert://", 7) != 0 &&
+ strncmp(name, "hash://", 7) != 0))
+ return -1;
+
+ priv = os_zalloc(sizeof(*priv));
+ rsa_meth = os_zalloc(sizeof(*rsa_meth));
+ if (priv == NULL || rsa_meth == NULL) {
+ wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory "
+ "for CryptoAPI RSA method");
+ os_free(priv);
+ os_free(rsa_meth);
+ return -1;
+ }
+
+ priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER);
+ if (priv->cert == NULL) {
+ priv->cert = cryptoapi_find_cert(
+ name, CERT_SYSTEM_STORE_LOCAL_MACHINE);
+ }
+ if (priv->cert == NULL) {
+ wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate "
+ "'%s'", name);
+ goto err;
+ }
+
+ cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded,
+ priv->cert->cbCertEncoded);
+ if (cert == NULL) {
+ wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER "
+ "encoding");
+ goto err;
+ }
+
+ if (!CryptAcquireCertificatePrivateKey(priv->cert,
+ CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
+ NULL, &priv->crypt_prov,
+ &priv->key_spec,
+ &priv->free_crypt_prov)) {
+ cryptoapi_error("Failed to acquire a private key for the "
+ "certificate");
+ goto err;
+ }
+
+ rsa_meth->name = "Microsoft CryptoAPI RSA Method";
+ rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc;
+ rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec;
+ rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc;
+ rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec;
+ rsa_meth->finish = cryptoapi_finish;
+ rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK;
+ rsa_meth->app_data = (char *) priv;
+
+ rsa = RSA_new();
+ if (rsa == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,
+ ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ if (!SSL_use_certificate(ssl, cert)) {
+ RSA_free(rsa);
+ rsa = NULL;
+ goto err;
+ }
+ pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
+ X509_free(cert);
+ cert = NULL;
+
+ rsa->n = BN_dup(pub_rsa->n);
+ rsa->e = BN_dup(pub_rsa->e);
+ if (!RSA_set_method(rsa, rsa_meth))
+ goto err;
+
+ if (!SSL_use_RSAPrivateKey(ssl, rsa))
+ goto err;
+ RSA_free(rsa);
+
+ return 0;
+
+err:
+ if (cert)
+ X509_free(cert);
+ if (rsa)
+ RSA_free(rsa);
+ else {
+ os_free(rsa_meth);
+ cryptoapi_free_data(priv);
+ }
+ return -1;
+}
+
+
+static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name)
+{
+ HCERTSTORE cs;
+ PCCERT_CONTEXT ctx = NULL;
+ X509 *cert;
+ char buf[128];
+ const char *store;
+#ifdef UNICODE
+ WCHAR *wstore;
+#endif /* UNICODE */
+
+ if (name == NULL || strncmp(name, "cert_store://", 13) != 0)
+ return -1;
+
+ store = name + 13;
+#ifdef UNICODE
+ wstore = os_malloc((os_strlen(store) + 1) * sizeof(WCHAR));
+ if (wstore == NULL)
+ return -1;
+ wsprintf(wstore, L"%S", store);
+ cs = CertOpenSystemStore(0, wstore);
+ os_free(wstore);
+#else /* UNICODE */
+ cs = CertOpenSystemStore(0, store);
+#endif /* UNICODE */
+ if (cs == NULL) {
+ wpa_printf(MSG_DEBUG, "%s: failed to open system cert store "
+ "'%s': error=%d", __func__, store,
+ (int) GetLastError());
+ return -1;
+ }
+
+ while ((ctx = CertEnumCertificatesInStore(cs, ctx))) {
+ cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded,
+ ctx->cbCertEncoded);
+ if (cert == NULL) {
+ wpa_printf(MSG_INFO, "CryptoAPI: Could not process "
+ "X509 DER encoding for CA cert");
+ continue;
+ }
+
+ X509_NAME_oneline(X509_get_subject_name(cert), buf,
+ sizeof(buf));
+ wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for "
+ "system certificate store: subject='%s'", buf);
+
+ if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to add ca_cert to OpenSSL "
+ "certificate store");
+ }
+
+ X509_free(cert);
+ }
+
+ if (!CertCloseStore(cs, 0)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to close system cert store "
+ "'%s': error=%d", __func__, name + 13,
+ (int) GetLastError());
+ }
+
+ return 0;
+}
+
+
+#else /* CONFIG_NATIVE_WINDOWS */
+
+static int tls_cryptoapi_cert(SSL *ssl, const char *name)
+{
+ return -1;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static void ssl_info_cb(const SSL *ssl, int where, int ret)
+{
+ const char *str;
+ int w;
+
+ wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret);
+ w = where & ~SSL_ST_MASK;
+ if (w & SSL_ST_CONNECT)
+ str = "SSL_connect";
+ else if (w & SSL_ST_ACCEPT)
+ str = "SSL_accept";
+ else
+ str = "undefined";
+
+ if (where & SSL_CB_LOOP) {
+ wpa_printf(MSG_DEBUG, "SSL: %s:%s",
+ str, SSL_state_string_long(ssl));
+ } else if (where & SSL_CB_ALERT) {
+ wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s",
+ where & SSL_CB_READ ?
+ "read (remote end reported an error)" :
+ "write (local SSL3 detected an error)",
+ SSL_alert_type_string_long(ret),
+ SSL_alert_desc_string_long(ret));
+ if ((ret >> 8) == SSL3_AL_FATAL) {
+ struct tls_connection *conn =
+ SSL_get_app_data((SSL *) ssl);
+ if (where & SSL_CB_READ)
+ conn->read_alerts++;
+ else
+ conn->write_alerts++;
+ }
+ } else if (where & SSL_CB_EXIT && ret <= 0) {
+ wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s",
+ str, ret == 0 ? "failed" : "error",
+ SSL_state_string_long(ssl));
+ }
+}
+
+
+#ifndef OPENSSL_NO_ENGINE
+/**
+ * tls_engine_load_dynamic_generic - load any openssl engine
+ * @pre: an array of commands and values that load an engine initialized
+ * in the engine specific function
+ * @post: an array of commands and values that initialize an already loaded
+ * engine (or %NULL if not required)
+ * @id: the engine id of the engine to load (only required if post is not %NULL
+ *
+ * This function is a generic function that loads any openssl engine.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+static int tls_engine_load_dynamic_generic(const char *pre[],
+ const char *post[], const char *id)
+{
+ ENGINE *engine;
+ const char *dynamic_id = "dynamic";
+
+ engine = ENGINE_by_id(id);
+ if (engine) {
+ ENGINE_free(engine);
+ wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already "
+ "available", id);
+ return 0;
+ }
+ ERR_clear_error();
+
+ engine = ENGINE_by_id(dynamic_id);
+ if (engine == NULL) {
+ wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
+ dynamic_id,
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ /* Perform the pre commands. This will load the engine. */
+ while (pre && pre[0]) {
+ wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]);
+ if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) {
+ wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: "
+ "%s %s [%s]", pre[0], pre[1],
+ ERR_error_string(ERR_get_error(), NULL));
+ ENGINE_free(engine);
+ return -1;
+ }
+ pre += 2;
+ }
+
+ /*
+ * Free the reference to the "dynamic" engine. The loaded engine can
+ * now be looked up using ENGINE_by_id().
+ */
+ ENGINE_free(engine);
+
+ engine = ENGINE_by_id(id);
+ if (engine == NULL) {
+ wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
+ id, ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ while (post && post[0]) {
+ wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]);
+ if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) {
+ wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:"
+ " %s %s [%s]", post[0], post[1],
+ ERR_error_string(ERR_get_error(), NULL));
+ ENGINE_remove(engine);
+ ENGINE_free(engine);
+ return -1;
+ }
+ post += 2;
+ }
+ ENGINE_free(engine);
+
+ return 0;
+}
+
+
+/**
+ * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc
+ * @pkcs11_so_path: pksc11_so_path from the configuration
+ * @pcks11_module_path: pkcs11_module_path from the configuration
+ */
+static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
+ const char *pkcs11_module_path)
+{
+ char *engine_id = "pkcs11";
+ const char *pre_cmd[] = {
+ "SO_PATH", NULL /* pkcs11_so_path */,
+ "ID", NULL /* engine_id */,
+ "LIST_ADD", "1",
+ /* "NO_VCHECK", "1", */
+ "LOAD", NULL,
+ NULL, NULL
+ };
+ const char *post_cmd[] = {
+ "MODULE_PATH", NULL /* pkcs11_module_path */,
+ NULL, NULL
+ };
+
+ if (!pkcs11_so_path || !pkcs11_module_path)
+ return 0;
+
+ pre_cmd[1] = pkcs11_so_path;
+ pre_cmd[3] = engine_id;
+ post_cmd[1] = pkcs11_module_path;
+
+ wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s",
+ pkcs11_so_path);
+
+ return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id);
+}
+
+
+/**
+ * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc
+ * @opensc_so_path: opensc_so_path from the configuration
+ */
+static int tls_engine_load_dynamic_opensc(const char *opensc_so_path)
+{
+ char *engine_id = "opensc";
+ const char *pre_cmd[] = {
+ "SO_PATH", NULL /* opensc_so_path */,
+ "ID", NULL /* engine_id */,
+ "LIST_ADD", "1",
+ "LOAD", NULL,
+ NULL, NULL
+ };
+
+ if (!opensc_so_path)
+ return 0;
+
+ pre_cmd[1] = opensc_so_path;
+ pre_cmd[3] = engine_id;
+
+ wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s",
+ opensc_so_path);
+
+ return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id);
+}
+#endif /* OPENSSL_NO_ENGINE */
+
+
+void * tls_init(const struct tls_config *conf)
+{
+ SSL_CTX *ssl;
+
+ if (tls_openssl_ref_count == 0) {
+ tls_global = os_zalloc(sizeof(*tls_global));
+ if (tls_global == NULL)
+ return NULL;
+ if (conf) {
+ tls_global->event_cb = conf->event_cb;
+ tls_global->cb_ctx = conf->cb_ctx;
+ }
+
+#ifdef CONFIG_FIPS
+#ifdef OPENSSL_FIPS
+ if (conf && conf->fips_mode) {
+ if (!FIPS_mode_set(1)) {
+ wpa_printf(MSG_ERROR, "Failed to enable FIPS "
+ "mode");
+ ERR_load_crypto_strings();
+ ERR_print_errors_fp(stderr);
+ return NULL;
+ } else
+ wpa_printf(MSG_INFO, "Running in FIPS mode");
+ }
+#else /* OPENSSL_FIPS */
+ if (conf && conf->fips_mode) {
+ wpa_printf(MSG_ERROR, "FIPS mode requested, but not "
+ "supported");
+ return NULL;
+ }
+#endif /* OPENSSL_FIPS */
+#endif /* CONFIG_FIPS */
+ SSL_load_error_strings();
+ SSL_library_init();
+#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 */
+ }
+ tls_openssl_ref_count++;
+
+ ssl = SSL_CTX_new(TLSv1_method());
+ if (ssl == NULL)
+ return NULL;
+
+ SSL_CTX_set_info_callback(ssl, ssl_info_cb);
+
+#ifndef OPENSSL_NO_ENGINE
+ if (conf &&
+ (conf->opensc_engine_path || conf->pkcs11_engine_path ||
+ conf->pkcs11_module_path)) {
+ wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
+ ERR_load_ENGINE_strings();
+ ENGINE_load_dynamic();
+
+ if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
+ tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
+ conf->pkcs11_module_path)) {
+ tls_deinit(ssl);
+ return NULL;
+ }
+ }
+#endif /* OPENSSL_NO_ENGINE */
+
+ return ssl;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+ SSL_CTX *ssl = ssl_ctx;
+ SSL_CTX_free(ssl);
+
+ tls_openssl_ref_count--;
+ if (tls_openssl_ref_count == 0) {
+#ifndef OPENSSL_NO_ENGINE
+ ENGINE_cleanup();
+#endif /* OPENSSL_NO_ENGINE */
+ CRYPTO_cleanup_all_ex_data();
+ ERR_remove_state(0);
+ ERR_free_strings();
+ EVP_cleanup();
+ os_free(tls_global);
+ tls_global = NULL;
+ }
+}
+
+
+static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
+ const char *pin, const char *key_id,
+ const char *cert_id, const char *ca_cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+ int ret = -1;
+ if (engine_id == NULL) {
+ wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set");
+ return -1;
+ }
+ if (pin == NULL) {
+ wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set");
+ return -1;
+ }
+ if (key_id == NULL) {
+ wpa_printf(MSG_ERROR, "ENGINE: Key Id not set");
+ return -1;
+ }
+
+ ERR_clear_error();
+ conn->engine = ENGINE_by_id(engine_id);
+ if (!conn->engine) {
+ wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]",
+ engine_id, ERR_error_string(ERR_get_error(), NULL));
+ goto err;
+ }
+ if (ENGINE_init(conn->engine) != 1) {
+ wpa_printf(MSG_ERROR, "ENGINE: engine init failed "
+ "(engine: %s) [%s]", engine_id,
+ ERR_error_string(ERR_get_error(), NULL));
+ goto err;
+ }
+ wpa_printf(MSG_DEBUG, "ENGINE: engine initialized");
+
+ if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) {
+ wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto err;
+ }
+ /* load private key first in-case PIN is required for cert */
+ conn->private_key = ENGINE_load_private_key(conn->engine,
+ key_id, NULL, NULL);
+ if (!conn->private_key) {
+ wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id"
+ " '%s' [%s]", key_id,
+ ERR_error_string(ERR_get_error(), NULL));
+ ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+ goto err;
+ }
+
+ /* handle a certificate and/or CA certificate */
+ if (cert_id || ca_cert_id) {
+ const char *cmd_name = "LOAD_CERT_CTRL";
+
+ /* test if the engine supports a LOAD_CERT_CTRL */
+ if (!ENGINE_ctrl(conn->engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
+ 0, (void *)cmd_name, NULL)) {
+ wpa_printf(MSG_ERROR, "ENGINE: engine does not support"
+ " loading certificates");
+ ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ if (conn->engine) {
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ }
+
+ if (conn->private_key) {
+ EVP_PKEY_free(conn->private_key);
+ conn->private_key = NULL;
+ }
+
+ return ret;
+#else /* OPENSSL_NO_ENGINE */
+ return 0;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static void tls_engine_deinit(struct tls_connection *conn)
+{
+#ifndef OPENSSL_NO_ENGINE
+ wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
+ if (conn->private_key) {
+ EVP_PKEY_free(conn->private_key);
+ conn->private_key = NULL;
+ }
+ if (conn->engine) {
+ ENGINE_finish(conn->engine);
+ conn->engine = NULL;
+ }
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+int tls_get_errors(void *ssl_ctx)
+{
+ int count = 0;
+ unsigned long err;
+
+ while ((err = ERR_get_error())) {
+ wpa_printf(MSG_INFO, "TLS - SSL error: %s",
+ ERR_error_string(err, NULL));
+ count++;
+ }
+
+ return count;
+}
+
+struct tls_connection * tls_connection_init(void *ssl_ctx)
+{
+ SSL_CTX *ssl = ssl_ctx;
+ struct tls_connection *conn;
+ long options;
+
+ conn = os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
+ conn->ssl = SSL_new(ssl);
+ if (conn->ssl == NULL) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to initialize new SSL connection");
+ os_free(conn);
+ return NULL;
+ }
+
+ SSL_set_app_data(conn->ssl, conn);
+ options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+ SSL_OP_SINGLE_DH_USE;
+#ifdef SSL_OP_NO_COMPRESSION
+ options |= SSL_OP_NO_COMPRESSION;
+#endif /* SSL_OP_NO_COMPRESSION */
+ SSL_set_options(conn->ssl, options);
+
+ conn->ssl_in = BIO_new(BIO_s_mem());
+ if (!conn->ssl_in) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to create a new BIO for ssl_in");
+ SSL_free(conn->ssl);
+ os_free(conn);
+ return NULL;
+ }
+
+ conn->ssl_out = BIO_new(BIO_s_mem());
+ if (!conn->ssl_out) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to create a new BIO for ssl_out");
+ SSL_free(conn->ssl);
+ BIO_free(conn->ssl_in);
+ os_free(conn);
+ return NULL;
+ }
+
+ SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out);
+
+ return conn;
+}
+
+
+void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return;
+ SSL_free(conn->ssl);
+ tls_engine_deinit(conn);
+ os_free(conn->subject_match);
+ os_free(conn->altsubject_match);
+ os_free(conn->session_ticket);
+ os_free(conn);
+}
+
+
+int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
+{
+ return conn ? SSL_is_init_finished(conn->ssl) : 0;
+}
+
+
+int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+
+ /* Shutdown previous TLS connection without notifying the peer
+ * because the connection was already terminated in practice
+ * and "close notify" shutdown alert would confuse AS. */
+ SSL_set_quiet_shutdown(conn->ssl, 1);
+ SSL_shutdown(conn->ssl);
+ return 0;
+}
+
+
+static int tls_match_altsubject_component(X509 *cert, int type,
+ const char *value, size_t len)
+{
+ GENERAL_NAME *gen;
+ void *ext;
+ int i, found = 0;
+
+ ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+ for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+ gen = sk_GENERAL_NAME_value(ext, i);
+ if (gen->type != type)
+ continue;
+ if (os_strlen((char *) gen->d.ia5->data) == len &&
+ os_memcmp(value, gen->d.ia5->data, len) == 0)
+ found++;
+ }
+
+ return found;
+}
+
+
+static int tls_match_altsubject(X509 *cert, const char *match)
+{
+ int type;
+ const char *pos, *end;
+ size_t len;
+
+ pos = match;
+ do {
+ if (os_strncmp(pos, "EMAIL:", 6) == 0) {
+ type = GEN_EMAIL;
+ pos += 6;
+ } else if (os_strncmp(pos, "DNS:", 4) == 0) {
+ type = GEN_DNS;
+ pos += 4;
+ } else if (os_strncmp(pos, "URI:", 4) == 0) {
+ type = GEN_URI;
+ pos += 4;
+ } else {
+ wpa_printf(MSG_INFO, "TLS: Invalid altSubjectName "
+ "match '%s'", pos);
+ return 0;
+ }
+ end = os_strchr(pos, ';');
+ while (end) {
+ if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
+ os_strncmp(end + 1, "DNS:", 4) == 0 ||
+ os_strncmp(end + 1, "URI:", 4) == 0)
+ break;
+ end = os_strchr(end + 1, ';');
+ }
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ if (tls_match_altsubject_component(cert, type, pos, len) > 0)
+ return 1;
+ pos = end + 1;
+ } while (end);
+
+ return 0;
+}
+
+
+static enum tls_fail_reason openssl_tls_fail_reason(int err)
+{
+ switch (err) {
+ case X509_V_ERR_CERT_REVOKED:
+ return TLS_FAIL_REVOKED;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ return TLS_FAIL_NOT_YET_VALID;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ return TLS_FAIL_EXPIRED;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ case X509_V_ERR_INVALID_CA:
+ return TLS_FAIL_UNTRUSTED;
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ case X509_V_ERR_CERT_UNTRUSTED:
+ case X509_V_ERR_CERT_REJECTED:
+ return TLS_FAIL_BAD_CERTIFICATE;
+ default:
+ return TLS_FAIL_UNSPECIFIED;
+ }
+}
+
+
+static struct wpabuf * get_x509_cert(X509 *cert)
+{
+ struct wpabuf *buf;
+ u8 *tmp;
+
+ int cert_len = i2d_X509(cert, NULL);
+ if (cert_len <= 0)
+ return NULL;
+
+ buf = wpabuf_alloc(cert_len);
+ if (buf == NULL)
+ return NULL;
+
+ tmp = wpabuf_put(buf, cert_len);
+ i2d_X509(cert, &tmp);
+ return buf;
+}
+
+
+static void openssl_tls_fail_event(struct tls_connection *conn,
+ X509 *err_cert, int err, int depth,
+ const char *subject, const char *err_str,
+ enum tls_fail_reason reason)
+{
+ union tls_event_data ev;
+ struct wpabuf *cert = NULL;
+
+ if (tls_global->event_cb == NULL)
+ return;
+
+ cert = get_x509_cert(err_cert);
+ os_memset(&ev, 0, sizeof(ev));
+ ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
+ reason : openssl_tls_fail_reason(err);
+ ev.cert_fail.depth = depth;
+ ev.cert_fail.subject = subject;
+ ev.cert_fail.reason_txt = err_str;
+ ev.cert_fail.cert = cert;
+ tls_global->event_cb(tls_global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+ wpabuf_free(cert);
+}
+
+
+static void openssl_tls_cert_event(struct tls_connection *conn,
+ X509 *err_cert, int depth,
+ const char *subject)
+{
+ struct wpabuf *cert = NULL;
+ union tls_event_data ev;
+#ifdef CONFIG_SHA256
+ u8 hash[32];
+#endif /* CONFIG_SHA256 */
+
+ if (tls_global->event_cb == NULL)
+ return;
+
+ os_memset(&ev, 0, sizeof(ev));
+ if (conn->cert_probe) {
+ cert = get_x509_cert(err_cert);
+ ev.peer_cert.cert = cert;
+ }
+#ifdef CONFIG_SHA256
+ if (cert) {
+ const u8 *addr[1];
+ size_t len[1];
+ addr[0] = wpabuf_head(cert);
+ len[0] = wpabuf_len(cert);
+ if (sha256_vector(1, addr, len, hash) == 0) {
+ ev.peer_cert.hash = hash;
+ ev.peer_cert.hash_len = sizeof(hash);
+ }
+ }
+#endif /* CONFIG_SHA256 */
+ ev.peer_cert.depth = depth;
+ ev.peer_cert.subject = subject;
+ tls_global->event_cb(tls_global->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+ wpabuf_free(cert);
+}
+
+
+static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+ char buf[256];
+ X509 *err_cert;
+ int err, depth;
+ SSL *ssl;
+ struct tls_connection *conn;
+ char *match, *altmatch;
+ const char *err_str;
+
+ err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+ err = X509_STORE_CTX_get_error(x509_ctx);
+ depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+ ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+ X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
+
+ conn = SSL_get_app_data(ssl);
+ if (conn == NULL)
+ return 0;
+ match = conn->subject_match;
+ altmatch = conn->altsubject_match;
+
+ if (!preverify_ok && !conn->ca_cert_verify)
+ preverify_ok = 1;
+ if (!preverify_ok && depth > 0 && conn->server_cert_only)
+ preverify_ok = 1;
+
+ err_str = X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
+ if (preverify_ok && depth == 0 && conn->server_cert_only) {
+ struct wpabuf *cert;
+ cert = get_x509_cert(err_cert);
+ if (!cert) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch "
+ "server certificate data");
+ preverify_ok = 0;
+ } else {
+ u8 hash[32];
+ const u8 *addr[1];
+ size_t len[1];
+ addr[0] = wpabuf_head(cert);
+ len[0] = wpabuf_len(cert);
+ if (sha256_vector(1, addr, len, hash) < 0 ||
+ os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
+ err_str = "Server certificate mismatch";
+ err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+ preverify_ok = 0;
+ }
+ wpabuf_free(cert);
+ }
+ }
+#endif /* CONFIG_SHA256 */
+
+ if (!preverify_ok) {
+ wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
+ " error %d (%s) depth %d for '%s'", err, err_str,
+ depth, buf);
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ err_str, TLS_FAIL_UNSPECIFIED);
+ return preverify_ok;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d "
+ "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
+ preverify_ok, err, err_str,
+ conn->ca_cert_verify, depth, buf);
+ if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+ wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
+ "match with '%s'", buf, match);
+ preverify_ok = 0;
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Subject mismatch",
+ TLS_FAIL_SUBJECT_MISMATCH);
+ } else if (depth == 0 && altmatch &&
+ !tls_match_altsubject(err_cert, altmatch)) {
+ wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
+ "'%s' not found", altmatch);
+ preverify_ok = 0;
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "AltSubject mismatch",
+ TLS_FAIL_ALTSUBJECT_MISMATCH);
+ } else
+ openssl_tls_cert_event(conn, err_cert, depth, buf);
+
+ if (conn->cert_probe && preverify_ok && depth == 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate "
+ "on probe-only run");
+ preverify_ok = 0;
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Server certificate chain probe",
+ TLS_FAIL_SERVER_CHAIN_PROBE);
+ }
+
+ return preverify_ok;
+}
+
+
+#ifndef OPENSSL_NO_STDIO
+static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert)
+{
+ SSL_CTX *ssl_ctx = _ssl_ctx;
+ X509_LOOKUP *lookup;
+ int ret = 0;
+
+ lookup = X509_STORE_add_lookup(ssl_ctx->cert_store,
+ X509_LOOKUP_file());
+ if (lookup == NULL) {
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed add lookup for X509 store");
+ return -1;
+ }
+
+ if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) {
+ unsigned long err = ERR_peek_error();
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed load CA in DER format");
+ if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+ ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
+ "cert already in hash table error",
+ __func__);
+ } else
+ ret = -1;
+ }
+
+ return ret;
+}
+#endif /* OPENSSL_NO_STDIO */
+
+
+#ifdef ANDROID
+static BIO * BIO_from_keystore(const char *key)
+{
+ BIO *bio = NULL;
+ char value[KEYSTORE_MESSAGE_SIZE];
+ int length = keystore_get(key, strlen(key), value);
+ if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
+ BIO_write(bio, value, length);
+ return bio;
+}
+#endif /* ANDROID */
+
+
+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)
+{
+ os_free(conn->subject_match);
+ conn->subject_match = NULL;
+ if (subject_match) {
+ conn->subject_match = os_strdup(subject_match);
+ if (conn->subject_match == NULL)
+ return -1;
+ }
+
+ os_free(conn->altsubject_match);
+ conn->altsubject_match = NULL;
+ if (altsubject_match) {
+ conn->altsubject_match = os_strdup(altsubject_match);
+ if (conn->altsubject_match == NULL)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+ int verify_peer)
+{
+ static int counter = 0;
+
+ if (conn == NULL)
+ return -1;
+
+ if (verify_peer) {
+ conn->ca_cert_verify = 1;
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
+ SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
+ } else {
+ conn->ca_cert_verify = 0;
+ SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+ }
+
+ SSL_set_accept_state(conn->ssl);
+
+ /*
+ * Set session id context in order to avoid fatal errors when client
+ * tries to resume a session. However, set the context to a unique
+ * value in order to effectively disable session resumption for now
+ * since not all areas of the server code are ready for it (e.g.,
+ * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS
+ * handshake).
+ */
+ counter++;
+ SSL_set_session_id_context(conn->ssl,
+ (const unsigned char *) &counter,
+ sizeof(counter));
+
+ return 0;
+}
+
+
+static int tls_connection_client_cert(struct tls_connection *conn,
+ const char *client_cert,
+ const u8 *client_cert_blob,
+ size_t client_cert_blob_len)
+{
+ if (client_cert == NULL && client_cert_blob == NULL)
+ return 0;
+
+ if (client_cert_blob &&
+ SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob,
+ client_cert_blob_len) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> "
+ "OK");
+ return 0;
+ } else if (client_cert_blob) {
+ tls_show_errors(MSG_DEBUG, __func__,
+ "SSL_use_certificate_ASN1 failed");
+ }
+
+ if (client_cert == NULL)
+ return -1;
+
+#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_file(ssl_ctx, client_cert,
+ SSL_FILETYPE_PEM) != 1) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to load client certificate");
+ return -1;
+ }
+ return 0;
+#else /* OPENSSL_NO_STDIO */
+ if (client_cert == NULL)
+ return 0;
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+ return -1;
+#endif /* OPENSSL_NO_STDIO */
+}
+
+
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
+{
+ if (password == NULL) {
+ return 0;
+ }
+ os_strlcpy(buf, (char *) password, size);
+ return os_strlen(buf);
+}
+
+
+#ifdef PKCS12_FUNCS
+static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12,
+ const char *passwd)
+{
+ EVP_PKEY *pkey;
+ X509 *cert;
+ STACK_OF(X509) *certs;
+ int res = 0;
+ char buf[256];
+
+ pkey = NULL;
+ cert = NULL;
+ certs = NULL;
+ if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) {
+ tls_show_errors(MSG_DEBUG, __func__,
+ "Failed to parse PKCS12 file");
+ PKCS12_free(p12);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data");
+
+ if (cert) {
+ X509_NAME_oneline(X509_get_subject_name(cert), buf,
+ sizeof(buf));
+ wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: "
+ "subject='%s'", buf);
+ if (ssl) {
+ if (SSL_use_certificate(ssl, cert) != 1)
+ res = -1;
+ } else {
+ if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1)
+ res = -1;
+ }
+ X509_free(cert);
+ }
+
+ if (pkey) {
+ wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12");
+ if (ssl) {
+ if (SSL_use_PrivateKey(ssl, pkey) != 1)
+ res = -1;
+ } else {
+ if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1)
+ res = -1;
+ }
+ EVP_PKEY_free(pkey);
+ }
+
+ if (certs) {
+ while ((cert = sk_X509_pop(certs)) != NULL) {
+ X509_NAME_oneline(X509_get_subject_name(cert), buf,
+ sizeof(buf));
+ wpa_printf(MSG_DEBUG, "TLS: additional certificate"
+ " from PKCS12: subject='%s'", buf);
+ /*
+ * There is no SSL equivalent for the chain cert - so
+ * always add it to the context...
+ */
+ if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) {
+ res = -1;
+ break;
+ }
+ }
+ sk_X509_free(certs);
+ }
+
+ PKCS12_free(p12);
+
+ if (res < 0)
+ tls_get_errors(ssl_ctx);
+
+ return res;
+}
+#endif /* PKCS12_FUNCS */
+
+
+static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key,
+ const char *passwd)
+{
+#ifdef PKCS12_FUNCS
+ FILE *f;
+ PKCS12 *p12;
+
+ f = fopen(private_key, "rb");
+ if (f == NULL)
+ return -1;
+
+ p12 = d2i_PKCS12_fp(f, NULL);
+ fclose(f);
+
+ if (p12 == NULL) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to use PKCS#12 file");
+ return -1;
+ }
+
+ return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
+
+#else /* PKCS12_FUNCS */
+ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read "
+ "p12/pfx files");
+ return -1;
+#endif /* PKCS12_FUNCS */
+}
+
+
+static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl,
+ const u8 *blob, size_t len, const char *passwd)
+{
+#ifdef PKCS12_FUNCS
+ PKCS12 *p12;
+
+ p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len);
+ if (p12 == NULL) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to use PKCS#12 blob");
+ return -1;
+ }
+
+ return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
+
+#else /* PKCS12_FUNCS */
+ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse "
+ "p12/pfx blobs");
+ return -1;
+#endif /* PKCS12_FUNCS */
+}
+
+
+#ifndef OPENSSL_NO_ENGINE
+static int tls_engine_get_cert(struct tls_connection *conn,
+ const char *cert_id,
+ X509 **cert)
+{
+ /* this runs after the private key is loaded so no PIN is required */
+ struct {
+ const char *cert_id;
+ X509 *cert;
+ } params;
+ params.cert_id = cert_id;
+ params.cert = NULL;
+
+ if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL",
+ 0, &params, NULL, 1)) {
+ wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id"
+ " '%s' [%s]", cert_id,
+ ERR_error_string(ERR_get_error(), NULL));
+ return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+ }
+ if (!params.cert) {
+ wpa_printf(MSG_ERROR, "ENGINE: did not properly cert with id"
+ " '%s'", cert_id);
+ return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+ }
+ *cert = params.cert;
+ return 0;
+}
+#endif /* OPENSSL_NO_ENGINE */
+
+
+static int tls_connection_engine_client_cert(struct tls_connection *conn,
+ const char *cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+ X509 *cert;
+
+ if (tls_engine_get_cert(conn, cert_id, &cert))
+ return -1;
+
+ if (!SSL_use_certificate(conn->ssl, cert)) {
+ tls_show_errors(MSG_ERROR, __func__,
+ "SSL_use_certificate failed");
+ X509_free(cert);
+ return -1;
+ }
+ X509_free(cert);
+ wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> "
+ "OK");
+ return 0;
+
+#else /* OPENSSL_NO_ENGINE */
+ return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static int tls_connection_engine_ca_cert(void *_ssl_ctx,
+ struct tls_connection *conn,
+ const char *ca_cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+ X509 *cert;
+ SSL_CTX *ssl_ctx = _ssl_ctx;
+
+ if (tls_engine_get_cert(conn, ca_cert_id, &cert))
+ return -1;
+
+ /* start off the same as tls_connection_ca_cert */
+ X509_STORE_free(ssl_ctx->cert_store);
+ ssl_ctx->cert_store = X509_STORE_new();
+ if (ssl_ctx->cert_store == NULL) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
+ "certificate store", __func__);
+ X509_free(cert);
+ return -1;
+ }
+ if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+ unsigned long err = ERR_peek_error();
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to add CA certificate from engine "
+ "to certificate store");
+ if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+ ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring cert"
+ " already in hash table error",
+ __func__);
+ } else {
+ X509_free(cert);
+ return -1;
+ }
+ }
+ X509_free(cert);
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine "
+ "to certificate store", __func__);
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+ return 0;
+
+#else /* OPENSSL_NO_ENGINE */
+ return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static int tls_connection_engine_private_key(struct tls_connection *conn)
+{
+#ifndef OPENSSL_NO_ENGINE
+ if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) {
+ tls_show_errors(MSG_ERROR, __func__,
+ "ENGINE: cannot use private key for TLS");
+ return -1;
+ }
+ if (!SSL_check_private_key(conn->ssl)) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Private key failed verification");
+ return -1;
+ }
+ return 0;
+#else /* OPENSSL_NO_ENGINE */
+ wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but "
+ "engine support was not compiled in");
+ return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static int tls_connection_private_key(void *_ssl_ctx,
+ struct tls_connection *conn,
+ const char *private_key,
+ const char *private_key_passwd,
+ const u8 *private_key_blob,
+ size_t private_key_blob_len)
+{
+ SSL_CTX *ssl_ctx = _ssl_ctx;
+ char *passwd;
+ int ok;
+
+ if (private_key == NULL && private_key_blob == NULL)
+ return 0;
+
+ if (private_key_passwd) {
+ passwd = os_strdup(private_key_passwd);
+ if (passwd == NULL)
+ return -1;
+ } else
+ passwd = NULL;
+
+ SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
+
+ ok = 0;
+ while (private_key_blob) {
+ if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl,
+ (u8 *) private_key_blob,
+ private_key_blob_len) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
+ "ASN1(EVP_PKEY_RSA) --> OK");
+ ok = 1;
+ break;
+ }
+
+ 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;
+ }
+
+#ifdef ANDROID
+ if (!ok && private_key &&
+ os_strncmp("keystore://", private_key, 11) == 0) {
+ BIO *bio = BIO_from_keystore(&private_key[11]);
+ EVP_PKEY *pkey = NULL;
+ if (bio) {
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ }
+ if (pkey) {
+ if (SSL_use_PrivateKey(conn->ssl, pkey) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Private key "
+ "from keystore");
+ ok = 1;
+ }
+ EVP_PKEY_free(pkey);
+ }
+ }
+#endif /* ANDROID */
+
+ 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)
+{
+ SSL *ssl;
+
+ if (conn == NULL || keys == NULL)
+ return -1;
+ ssl = conn->ssl;
+ if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
+ return -1;
+
+ os_memset(keys, 0, sizeof(*keys));
+ keys->master_key = ssl->session->master_key;
+ keys->master_key_len = ssl->session->master_key_length;
+ keys->client_random = ssl->s3->client_random;
+ keys->client_random_len = SSL3_RANDOM_SIZE;
+ keys->server_random = ssl->s3->server_random;
+ keys->server_random_len = SSL3_RANDOM_SIZE;
+
+ return 0;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+ const char *label, int server_random_first,
+ u8 *out, size_t out_len)
+{
+ return -1;
+}
+
+
+static struct wpabuf *
+openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
+ int server)
+{
+ int res;
+ struct wpabuf *out_data;
+
+ /*
+ * Give TLS handshake data from the server (if available) to OpenSSL
+ * for processing.
+ */
+ if (in_data &&
+ BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data))
+ < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Handshake failed - BIO_write");
+ return NULL;
+ }
+
+ /* Initiate TLS handshake or continue the existing handshake */
+ if (server)
+ res = SSL_accept(conn->ssl);
+ else
+ res = SSL_connect(conn->ssl);
+ if (res != 1) {
+ int err = SSL_get_error(conn->ssl, res);
+ if (err == SSL_ERROR_WANT_READ)
+ wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want "
+ "more data");
+ else if (err == SSL_ERROR_WANT_WRITE)
+ wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to "
+ "write");
+ else {
+ tls_show_errors(MSG_INFO, __func__, "SSL_connect");
+ conn->failed++;
+ }
+ }
+
+ /* Get the TLS handshake data to be sent to the server */
+ res = BIO_ctrl_pending(conn->ssl_out);
+ wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
+ out_data = wpabuf_alloc(res);
+ if (out_data == NULL) {
+ wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for "
+ "handshake output (%d bytes)", res);
+ if (BIO_reset(conn->ssl_out) < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "BIO_reset failed");
+ }
+ return NULL;
+ }
+ res = res == 0 ? 0 : BIO_read(conn->ssl_out, wpabuf_mhead(out_data),
+ res);
+ if (res < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Handshake failed - BIO_read");
+ if (BIO_reset(conn->ssl_out) < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "BIO_reset failed");
+ }
+ wpabuf_free(out_data);
+ return NULL;
+ }
+ wpabuf_put(out_data, res);
+
+ return out_data;
+}
+
+
+static struct wpabuf *
+openssl_get_appl_data(struct tls_connection *conn, size_t max_len)
+{
+ struct wpabuf *appl_data;
+ int res;
+
+ appl_data = wpabuf_alloc(max_len + 100);
+ if (appl_data == NULL)
+ return NULL;
+
+ res = SSL_read(conn->ssl, wpabuf_mhead(appl_data),
+ wpabuf_size(appl_data));
+ if (res < 0) {
+ int err = SSL_get_error(conn->ssl, res);
+ if (err == SSL_ERROR_WANT_READ ||
+ err == SSL_ERROR_WANT_WRITE) {
+ wpa_printf(MSG_DEBUG, "SSL: No Application Data "
+ "included");
+ } else {
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to read possible "
+ "Application Data");
+ }
+ wpabuf_free(appl_data);
+ return NULL;
+ }
+
+ wpabuf_put(appl_data, res);
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application Data in Finished "
+ "message", appl_data);
+
+ return appl_data;
+}
+
+
+static struct wpabuf *
+openssl_connection_handshake(struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data, int server)
+{
+ struct wpabuf *out_data;
+
+ if (appl_data)
+ *appl_data = NULL;
+
+ out_data = openssl_handshake(conn, in_data, server);
+ if (out_data == NULL)
+ return NULL;
+
+ if (SSL_is_init_finished(conn->ssl) && appl_data && in_data)
+ *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data));
+
+ return out_data;
+}
+
+
+struct wpabuf *
+tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return openssl_connection_handshake(conn, in_data, appl_data, 0);
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return openssl_connection_handshake(conn, in_data, appl_data, 1);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ int res;
+ struct wpabuf *buf;
+
+ if (conn == NULL)
+ return NULL;
+
+ /* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */
+ if ((res = BIO_reset(conn->ssl_in)) < 0 ||
+ (res = BIO_reset(conn->ssl_out)) < 0) {
+ tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
+ return NULL;
+ }
+ res = SSL_write(conn->ssl, wpabuf_head(in_data), wpabuf_len(in_data));
+ if (res < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Encryption failed - SSL_write");
+ return NULL;
+ }
+
+ /* Read encrypted data to be sent to the server */
+ buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+ if (buf == NULL)
+ return NULL;
+ res = BIO_read(conn->ssl_out, wpabuf_mhead(buf), wpabuf_size(buf));
+ if (res < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Encryption failed - BIO_read");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+
+ return buf;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ int res;
+ struct wpabuf *buf;
+
+ /* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */
+ res = BIO_write(conn->ssl_in, wpabuf_head(in_data),
+ wpabuf_len(in_data));
+ if (res < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Decryption failed - BIO_write");
+ return NULL;
+ }
+ if (BIO_reset(conn->ssl_out) < 0) {
+ tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
+ return NULL;
+ }
+
+ /* Read decrypted data for further processing */
+ /*
+ * Even though we try to disable TLS compression, it is possible that
+ * this cannot be done with all TLS libraries. Add extra buffer space
+ * to handle the possibility of the decrypted data being longer than
+ * input data.
+ */
+ buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+ if (buf == NULL)
+ return NULL;
+ res = SSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
+ if (res < 0) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Decryption failed - SSL_read");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ wpabuf_put(buf, res);
+
+ return buf;
+}
+
+
+int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+{
+ return conn ? conn->ssl->hit : 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+ u8 *ciphers)
+{
+ char buf[100], *pos, *end;
+ u8 *c;
+ int ret;
+
+ if (conn == NULL || conn->ssl == NULL || ciphers == NULL)
+ return -1;
+
+ buf[0] = '\0';
+ pos = buf;
+ end = pos + sizeof(buf);
+
+ c = ciphers;
+ while (*c != TLS_CIPHER_NONE) {
+ const char *suite;
+
+ switch (*c) {
+ case TLS_CIPHER_RC4_SHA:
+ suite = "RC4-SHA";
+ break;
+ case TLS_CIPHER_AES128_SHA:
+ suite = "AES128-SHA";
+ break;
+ case TLS_CIPHER_RSA_DHE_AES128_SHA:
+ suite = "DHE-RSA-AES128-SHA";
+ break;
+ case TLS_CIPHER_ANON_DH_AES128_SHA:
+ suite = "ADH-AES128-SHA";
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "TLS: Unsupported "
+ "cipher selection: %d", *c);
+ return -1;
+ }
+ ret = os_snprintf(pos, end - pos, ":%s", suite);
+ if (ret < 0 || ret >= end - pos)
+ break;
+ pos += ret;
+
+ c++;
+ }
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
+
+ if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
+ tls_show_errors(MSG_INFO, __func__,
+ "Cipher suite configuration failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ const char *name;
+ if (conn == NULL || conn->ssl == NULL)
+ return -1;
+
+ name = SSL_get_cipher(conn->ssl);
+ if (name == NULL)
+ return -1;
+
+ os_strlcpy(buf, name, buflen);
+ return 0;
+}
+
+
+int tls_connection_enable_workaround(void *ssl_ctx,
+ struct tls_connection *conn)
+{
+ SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+
+ return 0;
+}
+
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+/* ClientHello TLS extensions require a patch to openssl, so this function is
+ * commented out unless explicitly needed for EAP-FAST in order to be able to
+ * build this file with unmodified openssl. */
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len)
+{
+ if (conn == NULL || conn->ssl == NULL || ext_type != 35)
+ return -1;
+
+#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
+ if (SSL_set_session_ticket_ext(conn->ssl, (void *) data,
+ data_len) != 1)
+ return -1;
+#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+ if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data,
+ data_len) != 1)
+ return -1;
+#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+
+ return 0;
+}
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+
+int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+ return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+ return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+ return conn->write_alerts;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params)
+{
+ int ret;
+ unsigned long err;
+
+ if (conn == NULL)
+ return -1;
+
+ while ((err = ERR_get_error())) {
+ wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
+ __func__, ERR_error_string(err, NULL));
+ }
+
+ if (params->engine) {
+ wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine");
+ ret = tls_engine_init(conn, params->engine_id, params->pin,
+ params->key_id, params->cert_id,
+ params->ca_cert_id);
+ if (ret)
+ return ret;
+ }
+ if (tls_connection_set_subject_match(conn,
+ params->subject_match,
+ params->altsubject_match))
+ return -1;
+
+ if (params->engine && params->ca_cert_id) {
+ if (tls_connection_engine_ca_cert(tls_ctx, conn,
+ params->ca_cert_id))
+ return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+ } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
+ params->ca_cert_blob,
+ params->ca_cert_blob_len,
+ params->ca_path))
+ return -1;
+
+ if (params->engine && params->cert_id) {
+ if (tls_connection_engine_client_cert(conn, params->cert_id))
+ return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+ } else if (tls_connection_client_cert(conn, params->client_cert,
+ params->client_cert_blob,
+ params->client_cert_blob_len))
+ return -1;
+
+ if (params->engine && params->key_id) {
+ wpa_printf(MSG_DEBUG, "TLS: Using private key from engine");
+ if (tls_connection_engine_private_key(conn))
+ return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+ } else if (tls_connection_private_key(tls_ctx, conn,
+ params->private_key,
+ params->private_key_passwd,
+ params->private_key_blob,
+ params->private_key_blob_len)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'",
+ params->private_key);
+ return -1;
+ }
+
+ if (tls_connection_dh(conn, params->dh_file)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
+ params->dh_file);
+ return -1;
+ }
+
+ tls_get_errors(tls_ctx);
+
+ return 0;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+ const struct tls_connection_params *params)
+{
+ SSL_CTX *ssl_ctx = tls_ctx;
+ unsigned long err;
+
+ while ((err = ERR_get_error())) {
+ wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
+ __func__, ERR_error_string(err, NULL));
+ }
+
+ if (tls_global_ca_cert(ssl_ctx, params->ca_cert))
+ return -1;
+
+ if (tls_global_client_cert(ssl_ctx, params->client_cert))
+ return -1;
+
+ if (tls_global_private_key(ssl_ctx, params->private_key,
+ params->private_key_passwd))
+ return -1;
+
+ if (tls_global_dh(ssl_ctx, params->dh_file)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
+ params->dh_file);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int tls_connection_get_keyblock_size(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ const EVP_CIPHER *c;
+ const EVP_MD *h;
+
+ if (conn == NULL || conn->ssl == NULL ||
+ conn->ssl->enc_read_ctx == NULL ||
+ conn->ssl->enc_read_ctx->cipher == NULL ||
+ conn->ssl->read_hash == NULL)
+ return -1;
+
+ c = conn->ssl->enc_read_ctx->cipher;
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+ h = EVP_MD_CTX_md(conn->ssl->read_hash);
+#else
+ h = conn->ssl->read_hash;
+#endif
+
+ return 2 * (EVP_CIPHER_key_length(c) +
+ EVP_MD_size(h) +
+ EVP_CIPHER_iv_length(c));
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+ return 0;
+}
+
+
+int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
+ int tls_ia)
+{
+ return -1;
+}
+
+
+struct wpabuf * tls_connection_ia_send_phase_finished(
+ void *tls_ctx, struct tls_connection *conn, int final)
+{
+ return NULL;
+}
+
+
+int tls_connection_ia_final_phase_finished(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+int tls_connection_ia_permute_inner_secret(void *tls_ctx,
+ struct tls_connection *conn,
+ const u8 *key, size_t key_len)
+{
+ return -1;
+}
+
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+/* Pre-shared secred requires a patch to openssl, so this function is
+ * commented out unless explicitly needed for EAP-FAST in order to be able to
+ * build this file with unmodified openssl. */
+
+static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
+ STACK_OF(SSL_CIPHER) *peer_ciphers,
+ SSL_CIPHER **cipher, void *arg)
+{
+ struct tls_connection *conn = arg;
+ int ret;
+
+ if (conn == NULL || conn->session_ticket_cb == NULL)
+ return 0;
+
+ ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+ conn->session_ticket,
+ conn->session_ticket_len,
+ s->s3->client_random,
+ s->s3->server_random, secret);
+ os_free(conn->session_ticket);
+ conn->session_ticket = NULL;
+
+ if (ret <= 0)
+ return 0;
+
+ *secret_len = SSL_MAX_MASTER_KEY_LENGTH;
+ return 1;
+}
+
+
+#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
+static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
+ int len, void *arg)
+{
+ struct tls_connection *conn = arg;
+
+ if (conn == NULL || conn->session_ticket_cb == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s: length=%d", __func__, len);
+
+ os_free(conn->session_ticket);
+ conn->session_ticket = NULL;
+
+ wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
+ "extension", data, len);
+
+ conn->session_ticket = os_malloc(len);
+ if (conn->session_ticket == NULL)
+ return 0;
+
+ os_memcpy(conn->session_ticket, data, len);
+ conn->session_ticket_len = len;
+
+ return 1;
+}
+#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+#ifdef SSL_OP_NO_TICKET
+static void tls_hello_ext_cb(SSL *s, int client_server, int type,
+ unsigned char *data, int len, void *arg)
+{
+ struct tls_connection *conn = arg;
+
+ if (conn == NULL || conn->session_ticket_cb == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__,
+ type, len);
+
+ if (type == TLSEXT_TYPE_session_ticket && !client_server) {
+ os_free(conn->session_ticket);
+ conn->session_ticket = NULL;
+
+ wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
+ "extension", data, len);
+ conn->session_ticket = os_malloc(len);
+ if (conn->session_ticket == NULL)
+ return;
+
+ os_memcpy(conn->session_ticket, data, len);
+ conn->session_ticket_len = len;
+ }
+}
+#else /* SSL_OP_NO_TICKET */
+static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg)
+{
+ struct tls_connection *conn = arg;
+
+ if (conn == NULL || conn->session_ticket_cb == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__,
+ ext->type, ext->length);
+
+ os_free(conn->session_ticket);
+ conn->session_ticket = NULL;
+
+ if (ext->type == 35) {
+ wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
+ "extension", ext->data, ext->length);
+ conn->session_ticket = os_malloc(ext->length);
+ if (conn->session_ticket == NULL)
+ return SSL_AD_INTERNAL_ERROR;
+
+ os_memcpy(conn->session_ticket, ext->data, ext->length);
+ conn->session_ticket_len = ext->length;
+ }
+
+ return 0;
+}
+#endif /* SSL_OP_NO_TICKET */
+#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+ struct tls_connection *conn,
+ tls_session_ticket_cb cb,
+ void *ctx)
+{
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+ conn->session_ticket_cb = cb;
+ conn->session_ticket_cb_ctx = ctx;
+
+ if (cb) {
+ if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
+ conn) != 1)
+ return -1;
+#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
+ SSL_set_session_ticket_ext_cb(conn->ssl,
+ tls_session_ticket_ext_cb, conn);
+#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+#ifdef SSL_OP_NO_TICKET
+ SSL_set_tlsext_debug_callback(conn->ssl, tls_hello_ext_cb);
+ SSL_set_tlsext_debug_arg(conn->ssl, conn);
+#else /* SSL_OP_NO_TICKET */
+ if (SSL_set_hello_extension_cb(conn->ssl, tls_hello_ext_cb,
+ conn) != 1)
+ return -1;
+#endif /* SSL_OP_NO_TICKET */
+#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+ } else {
+ if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
+ return -1;
+#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
+ SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL);
+#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+#ifdef SSL_OP_NO_TICKET
+ SSL_set_tlsext_debug_callback(conn->ssl, NULL);
+ SSL_set_tlsext_debug_arg(conn->ssl, conn);
+#else /* SSL_OP_NO_TICKET */
+ if (SSL_set_hello_extension_cb(conn->ssl, NULL, NULL) != 1)
+ return -1;
+#endif /* SSL_OP_NO_TICKET */
+#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+ }
+
+ return 0;
+#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+ return -1;
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+}
diff --git a/src/crypto/tls_schannel.c b/src/crypto/tls_schannel.c
new file mode 100644
index 0000000..4a94e99
--- /dev/null
+++ b/src/crypto/tls_schannel.c
@@ -0,0 +1,767 @@
+/*
+ * SSL/TLS interface functions for Microsoft Schannel
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+/*
+ * FIX: Go through all SSPI functions and verify what needs to be freed
+ * FIX: session resumption
+ * TODO: add support for server cert chain validation
+ * TODO: add support for CA cert validation
+ * TODO: add support for EAP-TLS (client cert/key conf)
+ */
+
+#include "includes.h"
+#include <windows.h>
+#include <wincrypt.h>
+#include <schannel.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include <sspi.h>
+
+#include "common.h"
+#include "tls.h"
+
+
+struct tls_global {
+ HMODULE hsecurity;
+ PSecurityFunctionTable sspi;
+ HCERTSTORE my_cert_store;
+};
+
+struct tls_connection {
+ int established, start;
+ int failed, read_alerts, write_alerts;
+
+ SCHANNEL_CRED schannel_cred;
+ CredHandle creds;
+ CtxtHandle context;
+
+ u8 eap_tls_prf[128];
+ int eap_tls_prf_set;
+};
+
+
+static int schannel_load_lib(struct tls_global *global)
+{
+ INIT_SECURITY_INTERFACE pInitSecurityInterface;
+
+ global->hsecurity = LoadLibrary(TEXT("Secur32.dll"));
+ if (global->hsecurity == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Could not load Secur32.dll - 0x%x",
+ __func__, (unsigned int) GetLastError());
+ return -1;
+ }
+
+ pInitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress(
+ global->hsecurity, "InitSecurityInterfaceA");
+ if (pInitSecurityInterface == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Could not find "
+ "InitSecurityInterfaceA from Secur32.dll",
+ __func__);
+ FreeLibrary(global->hsecurity);
+ global->hsecurity = NULL;
+ return -1;
+ }
+
+ global->sspi = pInitSecurityInterface();
+ if (global->sspi == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Could not read security "
+ "interface - 0x%x",
+ __func__, (unsigned int) GetLastError());
+ FreeLibrary(global->hsecurity);
+ global->hsecurity = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void * tls_init(const struct tls_config *conf)
+{
+ struct tls_global *global;
+
+ global = os_zalloc(sizeof(*global));
+ if (global == NULL)
+ return NULL;
+ if (schannel_load_lib(global)) {
+ os_free(global);
+ return NULL;
+ }
+ return global;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+ struct tls_global *global = ssl_ctx;
+
+ if (global->my_cert_store)
+ CertCloseStore(global->my_cert_store, 0);
+ FreeLibrary(global->hsecurity);
+ os_free(global);
+}
+
+
+int tls_get_errors(void *ssl_ctx)
+{
+ return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *ssl_ctx)
+{
+ struct tls_connection *conn;
+
+ conn = os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
+ conn->start = 1;
+
+ return conn;
+}
+
+
+void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return;
+
+ os_free(conn);
+}
+
+
+int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
+{
+ return conn ? conn->established : 0;
+}
+
+
+int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+{
+ struct tls_global *global = ssl_ctx;
+ if (conn == NULL)
+ return -1;
+
+ conn->eap_tls_prf_set = 0;
+ conn->established = conn->failed = 0;
+ conn->read_alerts = conn->write_alerts = 0;
+ global->sspi->DeleteSecurityContext(&conn->context);
+ /* FIX: what else needs to be reseted? */
+
+ return 0;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+ const struct tls_connection_params *params)
+{
+ return -1;
+}
+
+
+int tls_global_set_verify(void *ssl_ctx, int check_crl)
+{
+ return -1;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+ int verify_peer)
+{
+ return -1;
+}
+
+
+int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
+ struct tls_keys *keys)
+{
+ /* Schannel does not export master secret or client/server random. */
+ return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+ const char *label, int server_random_first,
+ u8 *out, size_t out_len)
+{
+ /*
+ * Cannot get master_key from Schannel, but EapKeyBlock can be used to
+ * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and
+ * EAP-TTLS cannot use this, though, since they are using different
+ * labels. The only option could be to implement TLSv1 completely here
+ * and just use Schannel or CryptoAPI for low-level crypto
+ * functionality..
+ */
+
+ if (conn == NULL || !conn->eap_tls_prf_set || server_random_first ||
+ os_strcmp(label, "client EAP encryption") != 0 ||
+ out_len > sizeof(conn->eap_tls_prf))
+ return -1;
+
+ os_memcpy(out, conn->eap_tls_prf, out_len);
+
+ return 0;
+}
+
+
+static struct wpabuf * tls_conn_hs_clienthello(struct tls_global *global,
+ struct tls_connection *conn)
+{
+ DWORD sspi_flags, sspi_flags_out;
+ SecBufferDesc outbuf;
+ SecBuffer outbufs[1];
+ SECURITY_STATUS status;
+ TimeStamp ts_expiry;
+
+ sspi_flags = ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_MANUAL_CRED_VALIDATION;
+
+ wpa_printf(MSG_DEBUG, "%s: Generating ClientHello", __func__);
+
+ outbufs[0].pvBuffer = NULL;
+ outbufs[0].BufferType = SECBUFFER_TOKEN;
+ outbufs[0].cbBuffer = 0;
+
+ outbuf.cBuffers = 1;
+ outbuf.pBuffers = outbufs;
+ outbuf.ulVersion = SECBUFFER_VERSION;
+
+#ifdef UNICODE
+ status = global->sspi->InitializeSecurityContextW(
+ &conn->creds, NULL, NULL /* server name */, sspi_flags, 0,
+ SECURITY_NATIVE_DREP, NULL, 0, &conn->context,
+ &outbuf, &sspi_flags_out, &ts_expiry);
+#else /* UNICODE */
+ status = global->sspi->InitializeSecurityContextA(
+ &conn->creds, NULL, NULL /* server name */, sspi_flags, 0,
+ SECURITY_NATIVE_DREP, NULL, 0, &conn->context,
+ &outbuf, &sspi_flags_out, &ts_expiry);
+#endif /* UNICODE */
+ if (status != SEC_I_CONTINUE_NEEDED) {
+ wpa_printf(MSG_ERROR, "%s: InitializeSecurityContextA "
+ "failed - 0x%x",
+ __func__, (unsigned int) status);
+ return NULL;
+ }
+
+ if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) {
+ struct wpabuf *buf;
+ wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello",
+ outbufs[0].pvBuffer, outbufs[0].cbBuffer);
+ conn->start = 0;
+ buf = wpabuf_alloc_copy(outbufs[0].pvBuffer,
+ outbufs[0].cbBuffer);
+ if (buf == NULL)
+ return NULL;
+ global->sspi->FreeContextBuffer(outbufs[0].pvBuffer);
+ return buf;
+ }
+
+ wpa_printf(MSG_ERROR, "SChannel: Failed to generate ClientHello");
+
+ return NULL;
+}
+
+
+#ifndef SECPKG_ATTR_EAP_KEY_BLOCK
+#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b
+
+typedef struct _SecPkgContext_EapKeyBlock {
+ BYTE rgbKeys[128];
+ BYTE rgbIVs[64];
+} SecPkgContext_EapKeyBlock, *PSecPkgContext_EapKeyBlock;
+#endif /* !SECPKG_ATTR_EAP_KEY_BLOCK */
+
+static int tls_get_eap(struct tls_global *global, struct tls_connection *conn)
+{
+ SECURITY_STATUS status;
+ SecPkgContext_EapKeyBlock kb;
+
+ /* Note: Windows NT and Windows Me/98/95 do not support getting
+ * EapKeyBlock */
+
+ status = global->sspi->QueryContextAttributes(
+ &conn->context, SECPKG_ATTR_EAP_KEY_BLOCK, &kb);
+ if (status != SEC_E_OK) {
+ wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes("
+ "SECPKG_ATTR_EAP_KEY_BLOCK) failed (%d)",
+ __func__, (int) status);
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbKeys",
+ kb.rgbKeys, sizeof(kb.rgbKeys));
+ wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbIVs",
+ kb.rgbIVs, sizeof(kb.rgbIVs));
+
+ os_memcpy(conn->eap_tls_prf, kb.rgbKeys, sizeof(kb.rgbKeys));
+ conn->eap_tls_prf_set = 1;
+ return 0;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ struct tls_global *global = tls_ctx;
+ DWORD sspi_flags, sspi_flags_out;
+ SecBufferDesc inbuf, outbuf;
+ SecBuffer inbufs[2], outbufs[1];
+ SECURITY_STATUS status;
+ TimeStamp ts_expiry;
+ struct wpabuf *out_buf = NULL;
+
+ if (appl_data)
+ *appl_data = NULL;
+
+ if (conn->start)
+ return tls_conn_hs_clienthello(global, conn);
+
+ wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process",
+ (int) wpabuf_len(in_data));
+
+ sspi_flags = ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_MANUAL_CRED_VALIDATION;
+
+ /* Input buffer for Schannel */
+ inbufs[0].pvBuffer = (u8 *) wpabuf_head(in_data);
+ inbufs[0].cbBuffer = wpabuf_len(in_data);
+ inbufs[0].BufferType = SECBUFFER_TOKEN;
+
+ /* Place for leftover data from Schannel */
+ inbufs[1].pvBuffer = NULL;
+ inbufs[1].cbBuffer = 0;
+ inbufs[1].BufferType = SECBUFFER_EMPTY;
+
+ inbuf.cBuffers = 2;
+ inbuf.pBuffers = inbufs;
+ inbuf.ulVersion = SECBUFFER_VERSION;
+
+ /* Output buffer for Schannel */
+ outbufs[0].pvBuffer = NULL;
+ outbufs[0].cbBuffer = 0;
+ outbufs[0].BufferType = SECBUFFER_TOKEN;
+
+ outbuf.cBuffers = 1;
+ outbuf.pBuffers = outbufs;
+ outbuf.ulVersion = SECBUFFER_VERSION;
+
+#ifdef UNICODE
+ status = global->sspi->InitializeSecurityContextW(
+ &conn->creds, &conn->context, NULL, sspi_flags, 0,
+ SECURITY_NATIVE_DREP, &inbuf, 0, NULL,
+ &outbuf, &sspi_flags_out, &ts_expiry);
+#else /* UNICODE */
+ status = global->sspi->InitializeSecurityContextA(
+ &conn->creds, &conn->context, NULL, sspi_flags, 0,
+ SECURITY_NATIVE_DREP, &inbuf, 0, NULL,
+ &outbuf, &sspi_flags_out, &ts_expiry);
+#endif /* UNICODE */
+
+ wpa_printf(MSG_MSGDUMP, "Schannel: InitializeSecurityContext -> "
+ "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d "
+ "intype[1]=%d outlen[0]=%d",
+ (int) status, (int) inbufs[0].cbBuffer,
+ (int) inbufs[0].BufferType, (int) inbufs[1].cbBuffer,
+ (int) inbufs[1].BufferType,
+ (int) outbufs[0].cbBuffer);
+ if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED ||
+ (FAILED(status) && (sspi_flags_out & ISC_RET_EXTENDED_ERROR))) {
+ if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) {
+ wpa_hexdump(MSG_MSGDUMP, "SChannel - output",
+ outbufs[0].pvBuffer, outbufs[0].cbBuffer);
+ out_buf = wpabuf_alloc_copy(outbufs[0].pvBuffer,
+ outbufs[0].cbBuffer);
+ global->sspi->FreeContextBuffer(outbufs[0].pvBuffer);
+ outbufs[0].pvBuffer = NULL;
+ if (out_buf == NULL)
+ return NULL;
+ }
+ }
+
+ switch (status) {
+ case SEC_E_INCOMPLETE_MESSAGE:
+ wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INCOMPLETE_MESSAGE");
+ break;
+ case SEC_I_CONTINUE_NEEDED:
+ wpa_printf(MSG_DEBUG, "Schannel: SEC_I_CONTINUE_NEEDED");
+ break;
+ case SEC_E_OK:
+ /* TODO: verify server certificate chain */
+ wpa_printf(MSG_DEBUG, "Schannel: SEC_E_OK - Handshake "
+ "completed successfully");
+ conn->established = 1;
+ tls_get_eap(global, conn);
+
+ /* Need to return something to get final TLS ACK. */
+ if (out_buf == NULL)
+ out_buf = wpabuf_alloc(0);
+
+ if (inbufs[1].BufferType == SECBUFFER_EXTRA) {
+ wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted "
+ "application data",
+ inbufs[1].pvBuffer, inbufs[1].cbBuffer);
+ if (appl_data) {
+ *appl_data = wpabuf_alloc_copy(
+ outbufs[1].pvBuffer,
+ outbufs[1].cbBuffer);
+ }
+ global->sspi->FreeContextBuffer(inbufs[1].pvBuffer);
+ inbufs[1].pvBuffer = NULL;
+ }
+ break;
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ wpa_printf(MSG_DEBUG,
+ "Schannel: SEC_I_INCOMPLETE_CREDENTIALS");
+ break;
+ case SEC_E_WRONG_PRINCIPAL:
+ wpa_printf(MSG_DEBUG, "Schannel: SEC_E_WRONG_PRINCIPAL");
+ break;
+ case SEC_E_INTERNAL_ERROR:
+ wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INTERNAL_ERROR");
+ break;
+ }
+
+ if (FAILED(status)) {
+ wpa_printf(MSG_DEBUG, "Schannel: Handshake failed "
+ "(out_buf=%p)", out_buf);
+ conn->failed++;
+ global->sspi->DeleteSecurityContext(&conn->context);
+ return out_buf;
+ }
+
+ if (inbufs[1].BufferType == SECBUFFER_EXTRA) {
+ /* TODO: Can this happen? What to do with this data? */
+ wpa_hexdump(MSG_MSGDUMP, "SChannel - Leftover data",
+ inbufs[1].pvBuffer, inbufs[1].cbBuffer);
+ global->sspi->FreeContextBuffer(inbufs[1].pvBuffer);
+ inbufs[1].pvBuffer = NULL;
+ }
+
+ return out_buf;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return NULL;
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ struct tls_global *global = tls_ctx;
+ SECURITY_STATUS status;
+ SecBufferDesc buf;
+ SecBuffer bufs[4];
+ SecPkgContext_StreamSizes sizes;
+ int i;
+ struct wpabuf *out;
+
+ status = global->sspi->QueryContextAttributes(&conn->context,
+ SECPKG_ATTR_STREAM_SIZES,
+ &sizes);
+ if (status != SEC_E_OK) {
+ wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed",
+ __func__);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u",
+ __func__,
+ (unsigned int) sizes.cbHeader,
+ (unsigned int) sizes.cbTrailer);
+
+ out = wpabuf_alloc(sizes.cbHeader + wpabuf_len(in_data) +
+ sizes.cbTrailer);
+
+ os_memset(&bufs, 0, sizeof(bufs));
+ bufs[0].pvBuffer = wpabuf_put(out, sizes.cbHeader);
+ bufs[0].cbBuffer = sizes.cbHeader;
+ bufs[0].BufferType = SECBUFFER_STREAM_HEADER;
+
+ bufs[1].pvBuffer = wpabuf_put(out, 0);
+ wpabuf_put_buf(out, in_data);
+ bufs[1].cbBuffer = wpabuf_len(in_data);
+ bufs[1].BufferType = SECBUFFER_DATA;
+
+ bufs[2].pvBuffer = wpabuf_put(out, sizes.cbTrailer);
+ bufs[2].cbBuffer = sizes.cbTrailer;
+ bufs[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+ buf.ulVersion = SECBUFFER_VERSION;
+ buf.cBuffers = 3;
+ buf.pBuffers = bufs;
+
+ status = global->sspi->EncryptMessage(&conn->context, 0, &buf, 0);
+
+ wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage -> "
+ "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d "
+ "len[2]=%d type[2]=%d",
+ (int) status,
+ (int) bufs[0].cbBuffer, (int) bufs[0].BufferType,
+ (int) bufs[1].cbBuffer, (int) bufs[1].BufferType,
+ (int) bufs[2].cbBuffer, (int) bufs[2].BufferType);
+ wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: "
+ "out_data=%p bufs %p %p %p",
+ wpabuf_head(out), bufs[0].pvBuffer, bufs[1].pvBuffer,
+ bufs[2].pvBuffer);
+
+ for (i = 0; i < 3; i++) {
+ if (bufs[i].pvBuffer && bufs[i].BufferType != SECBUFFER_EMPTY)
+ {
+ wpa_hexdump(MSG_MSGDUMP, "SChannel: bufs",
+ bufs[i].pvBuffer, bufs[i].cbBuffer);
+ }
+ }
+
+ if (status == SEC_E_OK) {
+ wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__);
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "Schannel: Encrypted data "
+ "from EncryptMessage", out);
+ return out;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: Failed - status=%d",
+ __func__, (int) status);
+ wpabuf_free(out);
+ return NULL;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
+{
+ struct tls_global *global = tls_ctx;
+ SECURITY_STATUS status;
+ SecBufferDesc buf;
+ SecBuffer bufs[4];
+ int i;
+ struct wpabuf *out, *tmp;
+
+ wpa_hexdump_buf(MSG_MSGDUMP,
+ "Schannel: Encrypted data to DecryptMessage", in_data);
+ os_memset(&bufs, 0, sizeof(bufs));
+ tmp = wpabuf_dup(in_data);
+ if (tmp == NULL)
+ return NULL;
+ bufs[0].pvBuffer = wpabuf_mhead(tmp);
+ bufs[0].cbBuffer = wpabuf_len(in_data);
+ bufs[0].BufferType = SECBUFFER_DATA;
+
+ bufs[1].BufferType = SECBUFFER_EMPTY;
+ bufs[2].BufferType = SECBUFFER_EMPTY;
+ bufs[3].BufferType = SECBUFFER_EMPTY;
+
+ buf.ulVersion = SECBUFFER_VERSION;
+ buf.cBuffers = 4;
+ buf.pBuffers = bufs;
+
+ status = global->sspi->DecryptMessage(&conn->context, &buf, 0,
+ NULL);
+ wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage -> "
+ "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d "
+ "len[2]=%d type[2]=%d len[3]=%d type[3]=%d",
+ (int) status,
+ (int) bufs[0].cbBuffer, (int) bufs[0].BufferType,
+ (int) bufs[1].cbBuffer, (int) bufs[1].BufferType,
+ (int) bufs[2].cbBuffer, (int) bufs[2].BufferType,
+ (int) bufs[3].cbBuffer, (int) bufs[3].BufferType);
+ wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: "
+ "out_data=%p bufs %p %p %p %p",
+ wpabuf_head(tmp), bufs[0].pvBuffer, bufs[1].pvBuffer,
+ bufs[2].pvBuffer, bufs[3].pvBuffer);
+
+ switch (status) {
+ case SEC_E_INCOMPLETE_MESSAGE:
+ wpa_printf(MSG_DEBUG, "%s: SEC_E_INCOMPLETE_MESSAGE",
+ __func__);
+ break;
+ case SEC_E_OK:
+ wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__);
+ for (i = 0; i < 4; i++) {
+ if (bufs[i].BufferType == SECBUFFER_DATA)
+ break;
+ }
+ if (i == 4) {
+ wpa_printf(MSG_DEBUG, "%s: No output data from "
+ "DecryptMessage", __func__);
+ wpabuf_free(tmp);
+ return NULL;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from "
+ "DecryptMessage",
+ bufs[i].pvBuffer, bufs[i].cbBuffer);
+ out = wpabuf_alloc_copy(bufs[i].pvBuffer, bufs[i].cbBuffer);
+ wpabuf_free(tmp);
+ return out;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: Failed - status=%d",
+ __func__, (int) status);
+ wpabuf_free(tmp);
+ return NULL;
+}
+
+
+int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+ u8 *ciphers)
+{
+ return -1;
+}
+
+
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ return -1;
+}
+
+
+int tls_connection_enable_workaround(void *ssl_ctx,
+ struct tls_connection *conn)
+{
+ return 0;
+}
+
+
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+ int ext_type, const u8 *data,
+ size_t data_len)
+{
+ return -1;
+}
+
+
+int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+ return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+ return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+ if (conn == NULL)
+ return -1;
+ return conn->write_alerts;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+ const struct tls_connection_params *params)
+{
+ struct tls_global *global = tls_ctx;
+ ALG_ID algs[1];
+ SECURITY_STATUS status;
+ TimeStamp ts_expiry;
+
+ if (conn == NULL)
+ return -1;
+
+ if (global->my_cert_store == NULL &&
+ (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) ==
+ NULL) {
+ wpa_printf(MSG_ERROR, "%s: CertOpenSystemStore failed - 0x%x",
+ __func__, (unsigned int) GetLastError());
+ return -1;
+ }
+
+ os_memset(&conn->schannel_cred, 0, sizeof(conn->schannel_cred));
+ conn->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
+ conn->schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1;
+ algs[0] = CALG_RSA_KEYX;
+ conn->schannel_cred.cSupportedAlgs = 1;
+ conn->schannel_cred.palgSupportedAlgs = algs;
+ conn->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
+#ifdef UNICODE
+ status = global->sspi->AcquireCredentialsHandleW(
+ NULL, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL,
+ &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry);
+#else /* UNICODE */
+ status = global->sspi->AcquireCredentialsHandleA(
+ NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL,
+ &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry);
+#endif /* UNICODE */
+ if (status != SEC_E_OK) {
+ wpa_printf(MSG_DEBUG, "%s: AcquireCredentialsHandleA failed - "
+ "0x%x", __func__, (unsigned int) status);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+ return 0;
+}
+
+
+int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
+ int tls_ia)
+{
+ return -1;
+}
+
+
+struct wpabuf * tls_connection_ia_send_phase_finished(
+ void *tls_ctx, struct tls_connection *conn, int final);
+{
+ return NULL;
+}
+
+
+int tls_connection_ia_final_phase_finished(void *tls_ctx,
+ struct tls_connection *conn)
+{
+ return -1;
+}
+
+
+int tls_connection_ia_permute_inner_secret(void *tls_ctx,
+ struct tls_connection *conn,
+ const u8 *key, size_t key_len)
+{
+ return -1;
+}
diff --git a/src/drivers/.gitignore b/src/drivers/.gitignore
new file mode 100644
index 0000000..1d9e0e6
--- /dev/null
+++ b/src/drivers/.gitignore
@@ -0,0 +1,2 @@
+build.wpa_supplicant
+build.hostapd
diff --git a/src/drivers/Apple80211.h b/src/drivers/Apple80211.h
new file mode 100644
index 0000000..2a612e7
--- /dev/null
+++ b/src/drivers/Apple80211.h
@@ -0,0 +1,156 @@
+#ifndef APPLE80211_H
+#define APPLE80211_H
+
+/*
+ * Apple80211 framework definitions
+ * This is an undocumented interface and the definitions here are based on
+ * information from MacStumbler (http://www.macstumbler.com/Apple80211.h) and
+ * whatever related information can be found with google and experiments ;-).
+ */
+
+typedef struct __WirelessRef *WirelessRef;
+typedef SInt32 WirelessError;
+#define errWirelessNoError 0
+
+typedef struct WirelessInfo {
+ UInt16 link_qual;
+ UInt16 comms_qual;
+ UInt16 signal;
+ UInt16 noise;
+ UInt16 port_stat;
+ UInt16 client_mode;
+ UInt16 res1;
+ UInt16 power;
+ UInt16 res2;
+ UInt8 bssID[6];
+ UInt8 ssid[34];
+} WirelessInfo;
+
+typedef struct WirelessInfo2 {
+ /* TODO - these are probably not in correct order or complete */
+ WirelessInfo info1;
+ UInt8 macAddress[6];
+} WirelessInfo2;
+
+typedef struct WirelessNetworkInfo {
+ UInt16 channel;
+ UInt16 noise;
+ UInt16 signal;
+ UInt8 bssid[6];
+ UInt16 beacon_int;
+ UInt16 capability;
+ UInt16 ssid_len;
+ UInt8 ssid[32];
+} WirelessNetworkInfo;
+
+typedef int wirelessKeyType; /* TODO */
+
+int WirelessIsAvailable(void);
+WirelessError WirelessAttach(WirelessRef *ref, UInt32 res);
+WirelessError WirelessDetach(WirelessRef ref);
+WirelessError WirelessPrivate(WirelessRef ref, void *in_ptr, int in_bytes,
+ void *out_ptr, int out_bytes);
+WirelessError WirelessSetEnabled(WirelessRef ref, UInt8 enabled);
+WirelessError WirelessGetEnabled(WirelessRef ref, UInt8 *enabled);
+WirelessError WirelessSetPower(WirelessRef ref, UInt8 power);
+WirelessError WirelessGetPower(WirelessRef ref, UInt8 *power);
+WirelessError WirelessGetInfo(WirelessRef ref, WirelessInfo *info);
+WirelessError WirelessGetInfo2(WirelessRef ref, WirelessInfo2 *info);
+WirelessError WirelessScan(WirelessRef ref, CFArrayRef *results,
+ UInt32 strip_dups);
+WirelessError WirelessScanSplit(WirelessRef ref, CFArrayRef *ap_results,
+ CFArrayRef *ibss_results, UInt32 strip_dups);
+WirelessError WirelessDirectedScan(WirelessRef ref, CFArrayRef *results,
+ UInt32 strip_dups, CFStringRef ssid);
+WirelessError WirelessDirectedScan2(WirelessRef ref, CFDataRef ssid,
+ UInt32 strip_dups, CFArrayRef *results);
+WirelessError WirelessJoin(WirelessRef ref, CFStringRef ssid);
+WirelessError WirelessJoinWEP(WirelessRef ref, CFStringRef ssid,
+ CFStringRef passwd);
+WirelessError WirelessJoin8021x(WirelessRef ref, CFStringRef ssid);
+/*
+ * Set WEP key
+ * ref: wireless reference from WirelessAttach()
+ * type: ?
+ * key_idx: 0..3
+ * key_len: 13 for WEP-104 or 0 for clearing the key
+ * key: Pointer to the key or %NULL if key_len = 0
+ */
+WirelessError WirelessSetKey(WirelessRef ref, wirelessKeyType type,
+ int key_idx, int key_len,
+ const unsigned char *key);
+/*
+ * Set WPA key (e.g., PMK for 4-way handshake)
+ * ref: wireless reference from WirelessAttach()
+ * type: 0..4; 1 = PMK
+ * key_len: 16, 32, or 0
+ * key: Pointer to the key or %NULL if key_len = 0
+ */
+WirelessError WirelessSetWPAKey(WirelessRef ref, wirelessKeyType type,
+ int key_len, const unsigned char *key);
+WirelessError WirelessAssociate(WirelessRef ref, int type, CFDataRef ssid,
+ CFStringRef key);
+WirelessError WirelessAssociate2(WirelessRef ref, CFDictionaryRef scan_res,
+ CFStringRef key);
+WirelessError WirelessDisassociate(WirelessRef ref);
+
+/*
+ * Get a copy of scan results for the given SSID
+ * The returned dictionary includes following entries:
+ * beaconInterval: CFNumber(kCFNumberSInt32Type)
+ * SSID: CFData buffer of the SSID
+ * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 = WPA2
+ * name: Name of the network (SSID string)
+ * BSSID: CFData buffer of the BSSID
+ * channel: CFNumber(kCFNumberSInt32Type)
+ * signal: CFNumber(kCFNumberSInt32Type)
+ * appleIE: CFData
+ * WPSNOPINRequired: CFBoolean
+ * noise: CFNumber(kCFNumberSInt32Type)
+ * capability: CFNumber(kCFNumberSInt32Type)
+ * uniCipher: CFArray of CFNumber(kCFNumberSInt32Type)
+ * appleIE_Version: CFNumber(kCFNumberSInt32Type)
+ * appleIE_Robust: CFBoolean
+ * WPSConfigured: CFBoolean
+ * scanWasDirected: CFBoolean
+ * appleIE_Product: CFNumber(kCFNumberSInt32Type)
+ * authModes: CFArray of CFNumber(kCFNumberSInt32Type)
+ * multiCipher: CFNumber(kCFNumberSInt32Type)
+ */
+CFDictionaryRef WirelessSafeDirectedScanCopy(WirelessRef ref, CFDataRef ssid);
+
+/*
+ * Get information about the current association
+ * The returned dictionary includes following entries:
+ * keyData: CFData buffer of the key (e.g., 32-octet PSK)
+ * multiCipher: CFNumber(kCFNumberSInt32Type); 0 = none, 5 = CCMP?
+ * channel: CFNumber(kCFNumberSInt32Type)
+ * isIBSS: CFBoolean
+ * authMode: CFNumber(kCFNumberSInt32Type); 2 = WPA-Personal; 3 = open,
+ * 129 = WPA2-Enterprise
+ * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 == WPA2
+ * SSID: CFData buffer of the SSID
+ * cipherMode: CFNumber(kCFNumberSInt32Type); 0 = none, 4 = CCMP?
+ */
+CFDictionaryRef WirelessGetAssociationInfo(WirelessRef ref);
+
+WirelessError WirelessConfigure(WirelessRef ref);
+
+/*
+ * Get ASP information
+ * The returned dictionary includes following entries:
+ * Version: version number (e.g., 3.0)
+ * Channel: channel (e.g., 1)
+ * Vendor: vendor (e.g., 2)
+ */
+CFDictionaryRef WirelessGetInfoASP(void);
+
+/*
+ * Get a copy of the interface dictionary
+ * The returned dictionary has a key,value pairs for wireless interfaces.
+ * The key is the interface name and the value is the driver identifier, e.g.,
+ * en1: com.apple.driver.AirPort.Atheros
+ */
+CFDictionaryRef WirelessCopyInterfaceDict(void);
+
+#endif /* APPLE80211_H */
diff --git a/src/drivers/Makefile b/src/drivers/Makefile
new file mode 100644
index 0000000..07600e5
--- /dev/null
+++ b/src/drivers/Makefile
@@ -0,0 +1,9 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+ rm -f build.wpa_supplicant build.hostapd
+
+install:
+ @echo Nothing to be made.
diff --git a/src/drivers/MobileApple80211.c b/src/drivers/MobileApple80211.c
new file mode 100644
index 0000000..ce004fe
--- /dev/null
+++ b/src/drivers/MobileApple80211.c
@@ -0,0 +1,189 @@
+#include "includes.h"
+#include <dlfcn.h>
+
+#include "common.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include "MobileApple80211.h"
+
+/*
+ * Code for dynamically loading Apple80211 functions from Aeropuerto to avoid
+ * having to link with full Preferences.framework.
+ */
+
+static void *aeropuerto = NULL;
+
+
+int _Apple80211Initialized(void)
+{
+ return aeropuerto ? 1 : 0;
+}
+
+
+static int (*__Apple80211Open)(Apple80211Ref *ctx) = NULL;
+
+int Apple80211Open(Apple80211Ref *ctx)
+{
+ return __Apple80211Open(ctx);
+}
+
+
+static int (*__Apple80211Close)(Apple80211Ref ctx) = NULL;
+
+int Apple80211Close(Apple80211Ref ctx)
+{
+ return __Apple80211Close(ctx);
+}
+
+
+static int (*__Apple80211GetIfListCopy)(Apple80211Ref handle, CFArrayRef *list)
+ = NULL;
+
+int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list)
+{
+ return __Apple80211GetIfListCopy(handle, list);
+}
+
+
+static int (*__Apple80211BindToInterface)(Apple80211Ref handle,
+ CFStringRef interface) = NULL;
+
+int Apple80211BindToInterface(Apple80211Ref handle,
+ CFStringRef interface)
+{
+ return __Apple80211BindToInterface(handle, interface);
+}
+
+
+static int (*__Apple80211GetInterfaceNameCopy)(Apple80211Ref handle,
+ CFStringRef *name) = NULL;
+
+int Apple80211GetInterfaceNameCopy(Apple80211Ref handle,
+ CFStringRef *name)
+{
+ return __Apple80211GetInterfaceNameCopy(handle, name);
+}
+
+
+static int (*__Apple80211GetInfoCopy)(Apple80211Ref handle,
+ CFDictionaryRef *info) = NULL;
+
+int Apple80211GetInfoCopy(Apple80211Ref handle,
+ CFDictionaryRef *info)
+{
+ return __Apple80211GetInfoCopy(handle, info);
+}
+
+
+static int (*__Apple80211GetPower)(Apple80211Ref handle, char *pwr) = NULL;
+
+int Apple80211GetPower(Apple80211Ref handle, char *pwr)
+{
+ return __Apple80211GetPower(handle, pwr);
+}
+
+
+static int (*__Apple80211SetPower)(Apple80211Ref handle, char pwr) = NULL;
+
+int Apple80211SetPower(Apple80211Ref handle, char pwr)
+{
+ return __Apple80211SetPower(handle, pwr);
+}
+
+
+static int (*__Apple80211Scan)(Apple80211Ref handle, CFArrayRef *list,
+ CFDictionaryRef parameters) = NULL;
+
+int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list,
+ CFDictionaryRef parameters)
+{
+ return __Apple80211Scan(handle, list, parameters);
+}
+
+
+static int (*__Apple80211Associate)(Apple80211Ref handle, CFDictionaryRef bss,
+ CFStringRef password) = NULL;
+
+int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss,
+ CFStringRef password)
+{
+ return __Apple80211Associate(handle, bss, password);
+}
+
+
+static int (*__Apple80211AssociateAndCopyInfo)(Apple80211Ref handle,
+ CFDictionaryRef bss,
+ CFStringRef password,
+ CFDictionaryRef *info) =
+ NULL;
+
+int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss,
+ CFStringRef password, CFDictionaryRef *info)
+{
+ return __Apple80211AssociateAndCopyInfo(handle, bss, password, info);
+}
+
+
+static int (*__Apple80211CopyValue)(Apple80211Ref handle, int field,
+ CFDictionaryRef arg2, void *value) = NULL;
+
+int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2,
+ void *value)
+{
+ return __Apple80211CopyValue(handle, field, arg2, value);
+}
+
+
+#define DLSYM(s) \
+do { \
+ __ ## s = dlsym(aeropuerto, #s); \
+ if (__ ## s == NULL) { \
+ wpa_printf(MSG_ERROR, "MobileApple80211: Could not resolve " \
+ "symbol '" #s "' (%s)", dlerror()); \
+ err = 1; \
+ } \
+} while (0)
+
+
+__attribute__ ((constructor))
+void _Apple80211_constructor(void)
+{
+ const char *fname = "/System/Library/SystemConfiguration/"
+ "Aeropuerto.bundle/Aeropuerto";
+ int err = 0;
+
+ aeropuerto = dlopen(fname, RTLD_LAZY);
+ if (!aeropuerto) {
+ wpa_printf(MSG_ERROR, "MobileApple80211: Failed to open %s "
+ "for symbols", fname);
+ return;
+ }
+
+ DLSYM(Apple80211Open);
+ DLSYM(Apple80211Close);
+ DLSYM(Apple80211GetIfListCopy);
+ DLSYM(Apple80211BindToInterface);
+ DLSYM(Apple80211GetInterfaceNameCopy);
+ DLSYM(Apple80211GetInfoCopy);
+ DLSYM(Apple80211GetPower);
+ DLSYM(Apple80211SetPower);
+ DLSYM(Apple80211Scan);
+ DLSYM(Apple80211Associate);
+ DLSYM(Apple80211AssociateAndCopyInfo);
+ DLSYM(Apple80211CopyValue);
+
+ if (err) {
+ dlclose(aeropuerto);
+ aeropuerto = NULL;
+ }
+}
+
+
+__attribute__ ((destructor))
+void _Apple80211_destructor(void)
+{
+ if (aeropuerto) {
+ dlclose(aeropuerto);
+ aeropuerto = NULL;
+ }
+}
diff --git a/src/drivers/MobileApple80211.h b/src/drivers/MobileApple80211.h
new file mode 100644
index 0000000..64d439d
--- /dev/null
+++ b/src/drivers/MobileApple80211.h
@@ -0,0 +1,43 @@
+#ifndef MOBILEAPPLE80211_H
+#define MOBILEAPPLE80211_H
+
+/*
+ * MobileApple80211 interface for iPhone/iPod touch
+ * These functions are available from Aeropuerto.
+ */
+
+struct Apple80211;
+typedef struct Apple80211 *Apple80211Ref;
+
+int Apple80211Open(Apple80211Ref *ctx);
+int Apple80211Close(Apple80211Ref ctx);
+int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list);
+int Apple80211BindToInterface(Apple80211Ref handle,
+ CFStringRef interface);
+int Apple80211GetInterfaceNameCopy(Apple80211Ref handle,
+ CFStringRef *name);
+int Apple80211GetInfoCopy(Apple80211Ref handle,
+ CFDictionaryRef *info);
+int Apple80211GetPower(Apple80211Ref handle, char *pwr);
+int Apple80211SetPower(Apple80211Ref handle, char pwr);
+
+/* parameters can be NULL; returns scan results in CFArrayRef *list;
+ * caller will need to free with CFRelease() */
+int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list,
+ CFDictionaryRef parameters);
+
+int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss,
+ CFStringRef password);
+int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss,
+ CFStringRef password,
+ CFDictionaryRef *info);
+
+enum {
+ APPLE80211_VALUE_SSID = 1,
+ APPLE80211_VALUE_BSSID = 9
+};
+
+int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2,
+ void *value);
+
+#endif /* MOBILEAPPLE80211_H */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
new file mode 100644
index 0000000..8efd697
--- /dev/null
+++ b/src/drivers/driver.h
@@ -0,0 +1,3230 @@
+/*
+ * Driver interface definition
+ * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file defines a driver interface used by both %wpa_supplicant and
+ * hostapd. The first part of the file defines data structures used in various
+ * driver operations. This is followed by the struct wpa_driver_ops that each
+ * driver wrapper will beed to define with callback functions for requesting
+ * driver operations. After this, there are definitions for driver event
+ * reporting with wpa_supplicant_event() and some convenience helper functions
+ * that can be used to report events.
+ */
+
+#ifndef DRIVER_H
+#define DRIVER_H
+
+#define WPA_SUPPLICANT_DRIVER_VERSION 4
+
+#include "common/defs.h"
+
+#define HOSTAPD_CHAN_DISABLED 0x00000001
+#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002
+#define HOSTAPD_CHAN_NO_IBSS 0x00000004
+#define HOSTAPD_CHAN_RADAR 0x00000008
+#define HOSTAPD_CHAN_HT40PLUS 0x00000010
+#define HOSTAPD_CHAN_HT40MINUS 0x00000020
+#define HOSTAPD_CHAN_HT40 0x00000040
+
+/**
+ * struct hostapd_channel_data - Channel information
+ */
+struct hostapd_channel_data {
+ /**
+ * chan - Channel number (IEEE 802.11)
+ */
+ short chan;
+
+ /**
+ * freq - Frequency in MHz
+ */
+ short freq;
+
+ /**
+ * flag - Channel flags (HOSTAPD_CHAN_*)
+ */
+ int flag;
+
+ /**
+ * max_tx_power - maximum transmit power in dBm
+ */
+ u8 max_tx_power;
+};
+
+/**
+ * struct hostapd_hw_modes - Supported hardware mode information
+ */
+struct hostapd_hw_modes {
+ /**
+ * mode - Hardware mode
+ */
+ enum hostapd_hw_mode mode;
+
+ /**
+ * num_channels - Number of entries in the channels array
+ */
+ int num_channels;
+
+ /**
+ * channels - Array of supported channels
+ */
+ struct hostapd_channel_data *channels;
+
+ /**
+ * num_rates - Number of entries in the rates array
+ */
+ int num_rates;
+
+ /**
+ * rates - Array of supported rates in 100 kbps units
+ */
+ int *rates;
+
+ /**
+ * ht_capab - HT (IEEE 802.11n) capabilities
+ */
+ u16 ht_capab;
+
+ /**
+ * mcs_set - MCS (IEEE 802.11n) rate parameters
+ */
+ u8 mcs_set[16];
+
+ /**
+ * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters
+ */
+ u8 a_mpdu_params;
+};
+
+
+#define IEEE80211_MODE_INFRA 0
+#define IEEE80211_MODE_IBSS 1
+#define IEEE80211_MODE_AP 2
+
+#define IEEE80211_CAP_ESS 0x0001
+#define IEEE80211_CAP_IBSS 0x0002
+#define IEEE80211_CAP_PRIVACY 0x0010
+
+#define WPA_SCAN_QUAL_INVALID BIT(0)
+#define WPA_SCAN_NOISE_INVALID BIT(1)
+#define WPA_SCAN_LEVEL_INVALID BIT(2)
+#define WPA_SCAN_LEVEL_DBM BIT(3)
+#define WPA_SCAN_AUTHENTICATED BIT(4)
+#define WPA_SCAN_ASSOCIATED BIT(5)
+
+/**
+ * struct wpa_scan_res - Scan result for an BSS/IBSS
+ * @flags: information flags about the BSS/IBSS (WPA_SCAN_*)
+ * @bssid: BSSID
+ * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
+ * @beacon_int: beacon interval in TUs (host byte order)
+ * @caps: capability information field in host byte order
+ * @qual: signal quality
+ * @noise: noise level
+ * @level: signal level
+ * @tsf: Timestamp
+ * @age: Age of the information in milliseconds (i.e., how many milliseconds
+ * ago the last Beacon or Probe Response frame was received)
+ * @ie_len: length of the following IE field in octets
+ * @beacon_ie_len: length of the following Beacon IE field in octets
+ *
+ * This structure is used as a generic format for scan results from the
+ * driver. Each driver interface implementation is responsible for converting
+ * the driver or OS specific scan results into this format.
+ *
+ * If the driver does not support reporting all IEs, the IE data structure is
+ * constructed of the IEs that are available. This field will also need to
+ * include SSID in IE format. All drivers are encouraged to be extended to
+ * report all IEs to make it easier to support future additions.
+ */
+struct wpa_scan_res {
+ unsigned int flags;
+ u8 bssid[ETH_ALEN];
+ int freq;
+ u16 beacon_int;
+ u16 caps;
+ int qual;
+ int noise;
+ int level;
+ u64 tsf;
+ unsigned int age;
+ size_t ie_len;
+ size_t beacon_ie_len;
+ /*
+ * Followed by ie_len octets of IEs from Probe Response frame (or if
+ * the driver does not indicate source of IEs, these may also be from
+ * Beacon frame). After the first set of IEs, another set of IEs may
+ * follow (with beacon_ie_len octets of data) if the driver provides
+ * both IE sets.
+ */
+};
+
+/**
+ * struct wpa_scan_results - Scan results
+ * @res: Array of pointers to allocated variable length scan result entries
+ * @num: Number of entries in the scan result array
+ */
+struct wpa_scan_results {
+ struct wpa_scan_res **res;
+ size_t num;
+};
+
+/**
+ * struct wpa_interface_info - Network interface information
+ * @next: Pointer to the next interface or NULL if this is the last one
+ * @ifname: Interface name that can be used with init() or init2()
+ * @desc: Human readable adapter description (e.g., vendor/model) or NULL if
+ * not available
+ * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one
+ * is not an allocated copy, i.e., get_interfaces() caller will not free
+ * this)
+ */
+struct wpa_interface_info {
+ struct wpa_interface_info *next;
+ char *ifname;
+ char *desc;
+ const char *drv_name;
+};
+
+#define WPAS_MAX_SCAN_SSIDS 4
+
+/**
+ * struct wpa_driver_scan_params - Scan parameters
+ * Data for struct wpa_driver_ops::scan2().
+ */
+struct wpa_driver_scan_params {
+ /**
+ * ssids - SSIDs to scan for
+ */
+ struct wpa_driver_scan_ssid {
+ /**
+ * ssid - specific SSID to scan for (ProbeReq)
+ * %NULL or zero-length SSID is used to indicate active scan
+ * with wildcard SSID.
+ */
+ const u8 *ssid;
+ /**
+ * ssid_len: Length of the SSID in octets
+ */
+ size_t ssid_len;
+ } ssids[WPAS_MAX_SCAN_SSIDS];
+
+ /**
+ * num_ssids - Number of entries in ssids array
+ * Zero indicates a request for a passive scan.
+ */
+ size_t num_ssids;
+
+ /**
+ * extra_ies - Extra IE(s) to add into Probe Request or %NULL
+ */
+ const u8 *extra_ies;
+
+ /**
+ * extra_ies_len - Length of extra_ies in octets
+ */
+ size_t extra_ies_len;
+
+ /**
+ * freqs - Array of frequencies to scan or %NULL for all frequencies
+ *
+ * The frequency is set in MHz. The array is zero-terminated.
+ */
+ int *freqs;
+
+ /**
+ * filter_ssids - Filter for reporting SSIDs
+ *
+ * This optional parameter can be used to request the driver wrapper to
+ * filter scan results to include only the specified SSIDs. %NULL
+ * indicates that no filtering is to be done. This can be used to
+ * reduce memory needs for scan results in environments that have large
+ * number of APs with different SSIDs.
+ *
+ * The driver wrapper is allowed to take this allocated buffer into its
+ * own use by setting the pointer to %NULL. In that case, the driver
+ * wrapper is responsible for freeing the buffer with os_free() once it
+ * is not needed anymore.
+ */
+ struct wpa_driver_scan_filter {
+ u8 ssid[32];
+ size_t ssid_len;
+ } *filter_ssids;
+
+ /**
+ * num_filter_ssids - Number of entries in filter_ssids array
+ */
+ size_t num_filter_ssids;
+};
+
+/**
+ * struct wpa_driver_auth_params - Authentication parameters
+ * Data for struct wpa_driver_ops::authenticate().
+ */
+struct wpa_driver_auth_params {
+ int freq;
+ const u8 *bssid;
+ const u8 *ssid;
+ size_t ssid_len;
+ int auth_alg;
+ const u8 *ie;
+ size_t ie_len;
+ const u8 *wep_key[4];
+ size_t wep_key_len[4];
+ int wep_tx_keyidx;
+ int local_state_change;
+};
+
+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;
+
+ /**
+ * wpa_ie - WPA information element for (Re)Association Request
+ * WPA information element to be included in (Re)Association
+ * Request (including information element id and length). Use
+ * of this WPA IE is optional. If the driver generates the WPA
+ * IE, it can use pairwise_suite, group_suite, and
+ * key_mgmt_suite to select proper algorithms. In this case,
+ * the driver has to notify wpa_supplicant about the used WPA
+ * IE by generating an event that the interface code will
+ * convert into EVENT_ASSOCINFO data (see below).
+ *
+ * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE
+ * instead. The driver can determine which version is used by
+ * looking at the first byte of the IE (0xdd for WPA, 0x30 for
+ * WPA2/RSN).
+ *
+ * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE.
+ */
+ const u8 *wpa_ie;
+
+ /**
+ * wpa_ie_len - length of the wpa_ie
+ */
+ size_t wpa_ie_len;
+
+ /**
+ * pairwise_suite - Selected pairwise cipher suite
+ *
+ * This is usually ignored if @wpa_ie is used.
+ */
+ enum wpa_cipher pairwise_suite;
+
+ /**
+ * group_suite - Selected group cipher suite
+ *
+ * This is usually ignored if @wpa_ie is used.
+ */
+ enum wpa_cipher group_suite;
+
+ /**
+ * key_mgmt_suite - Selected key management suite
+ *
+ * This is usually ignored if @wpa_ie is used.
+ */
+ enum wpa_key_mgmt key_mgmt_suite;
+
+ /**
+ * auth_alg - Allowed authentication algorithms
+ * Bit field of WPA_AUTH_ALG_*
+ */
+ int auth_alg;
+
+ /**
+ * mode - Operation mode (infra/ibss) IEEE80211_MODE_*
+ */
+ int mode;
+
+ /**
+ * wep_key - WEP keys for static WEP configuration
+ */
+ const u8 *wep_key[4];
+
+ /**
+ * wep_key_len - WEP key length for static WEP configuration
+ */
+ size_t wep_key_len[4];
+
+ /**
+ * wep_tx_keyidx - WEP TX key index for static WEP configuration
+ */
+ int wep_tx_keyidx;
+
+ /**
+ * mgmt_frame_protection - IEEE 802.11w management frame protection
+ */
+ enum mfp_options mgmt_frame_protection;
+
+ /**
+ * ft_ies - IEEE 802.11r / FT information elements
+ * If the supplicant is using IEEE 802.11r (FT) and has the needed keys
+ * for fast transition, this parameter is set to include the IEs that
+ * are to be sent in the next FT Authentication Request message.
+ * update_ft_ies() handler is called to update the IEs for further
+ * FT messages in the sequence.
+ *
+ * The driver should use these IEs only if the target AP is advertising
+ * the same mobility domain as the one included in the MDIE here.
+ *
+ * In ap_scan=2 mode, the driver can use these IEs when moving to a new
+ * AP after the initial association. These IEs can only be used if the
+ * target AP is advertising support for FT and is using the same MDIE
+ * and SSID as the current AP.
+ *
+ * The driver is responsible for reporting the FT IEs received from the
+ * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE
+ * type. update_ft_ies() handler will then be called with the FT IEs to
+ * include in the next frame in the authentication sequence.
+ */
+ const u8 *ft_ies;
+
+ /**
+ * ft_ies_len - Length of ft_ies in bytes
+ */
+ size_t ft_ies_len;
+
+ /**
+ * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies)
+ *
+ * This value is provided to allow the driver interface easier access
+ * to the current mobility domain. This value is set to %NULL if no
+ * mobility domain is currently active.
+ */
+ const u8 *ft_md;
+
+ /**
+ * passphrase - RSN passphrase for PSK
+ *
+ * This value is made available only for WPA/WPA2-Personal (PSK) and
+ * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
+ * the 8..63 character ASCII passphrase, if available. Please note that
+ * this can be %NULL if passphrase was not used to generate the PSK. In
+ * that case, the psk field must be used to fetch the PSK.
+ */
+ const char *passphrase;
+
+ /**
+ * psk - RSN PSK (alternative for passphrase for PSK)
+ *
+ * This value is made available only for WPA/WPA2-Personal (PSK) and
+ * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
+ * the 32-octet (256-bit) PSK, if available. The driver wrapper should
+ * be prepared to handle %NULL value as an error.
+ */
+ const u8 *psk;
+
+ /**
+ * drop_unencrypted - Enable/disable unencrypted frame filtering
+ *
+ * Configure the driver to drop all non-EAPOL frames (both receive and
+ * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must
+ * still be allowed for key negotiation.
+ */
+ int drop_unencrypted;
+
+ /**
+ * prev_bssid - Previously used BSSID in this ESS
+ *
+ * When not %NULL, this is a request to use reassociation instead of
+ * association.
+ */
+ const u8 *prev_bssid;
+
+ /**
+ * 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;
+};
+
+/**
+ * struct wpa_driver_capa - Driver capability information
+ */
+struct wpa_driver_capa {
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA 0x00000001
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2 0x00000002
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK 0x00000004
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK 0x00000008
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT 0x00000020
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK 0x00000040
+ unsigned int key_mgmt;
+
+#define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001
+#define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002
+#define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004
+#define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008
+ unsigned int enc;
+
+#define WPA_DRIVER_AUTH_OPEN 0x00000001
+#define WPA_DRIVER_AUTH_SHARED 0x00000002
+#define WPA_DRIVER_AUTH_LEAP 0x00000004
+ unsigned int auth;
+
+/* Driver generated WPA/RSN IE */
+#define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001
+/* Driver needs static WEP key setup after association command */
+#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002
+#define WPA_DRIVER_FLAGS_USER_SPACE_MLME 0x00000004
+/* Driver takes care of RSN 4-way handshake internally; PMK is configured with
+ * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */
+#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008
+#define WPA_DRIVER_FLAGS_WIRED 0x00000010
+/* Driver provides separate commands for authentication and association (SME in
+ * wpa_supplicant). */
+#define WPA_DRIVER_FLAGS_SME 0x00000020
+/* Driver supports AP mode */
+#define WPA_DRIVER_FLAGS_AP 0x00000040
+/* Driver needs static WEP key setup after association has been completed */
+#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080
+/* 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 Device, GO, or P2P Client */
+#define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800
+/* Driver supports concurrent operations on multiple channels */
+#define WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT 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
+ unsigned int flags;
+
+ int max_scan_ssids;
+
+ /**
+ * 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;
+};
+
+
+struct hostapd_data;
+
+struct hostap_sta_driver_data {
+ unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes;
+ unsigned long current_tx_rate;
+ unsigned long inactive_msec;
+ unsigned long flags;
+ unsigned long num_ps_buf_frames;
+ unsigned long tx_retry_failed;
+ unsigned long tx_retry_count;
+ int last_rssi;
+ int last_ack_rssi;
+};
+
+struct hostapd_sta_add_params {
+ const u8 *addr;
+ u16 aid;
+ u16 capability;
+ const u8 *supp_rates;
+ size_t supp_rates_len;
+ u16 listen_interval;
+ const struct ieee80211_ht_capabilities *ht_capabilities;
+};
+
+struct hostapd_freq_params {
+ int mode;
+ int freq;
+ int channel;
+ int ht_enabled;
+ int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled,
+ * secondary channel below primary, 1 = HT40
+ * enabled, secondary channel above primary */
+};
+
+enum wpa_driver_if_type {
+ /**
+ * WPA_IF_STATION - Station mode interface
+ */
+ WPA_IF_STATION,
+
+ /**
+ * WPA_IF_AP_VLAN - AP mode VLAN interface
+ *
+ * This interface shares its address and Beacon frame with the main
+ * BSS.
+ */
+ WPA_IF_AP_VLAN,
+
+ /**
+ * WPA_IF_AP_BSS - AP mode BSS interface
+ *
+ * This interface has its own address and Beacon frame.
+ */
+ WPA_IF_AP_BSS,
+
+ /**
+ * 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
+};
+
+struct wpa_init_params {
+ const u8 *bssid;
+ const char *ifname;
+ const u8 *ssid;
+ size_t ssid_len;
+ const char *test_socket;
+ int use_pae_group_addr;
+ char **bridge;
+ size_t num_bridge;
+
+ u8 *own_addr; /* buffer for writing own MAC address */
+};
+
+
+struct wpa_bss_params {
+ /** Interface name (for multi-SSID/VLAN support) */
+ const char *ifname;
+ /** Whether IEEE 802.1X or WPA/WPA2 is enabled */
+ int enabled;
+
+ int wpa;
+ int ieee802_1x;
+ int wpa_group;
+ int wpa_pairwise;
+ int wpa_key_mgmt;
+ int rsn_preauth;
+ 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)
+
+/**
+ * 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
+};
+
+/**
+ * struct wpa_signal_info - Information about channel signal quality
+ */
+struct wpa_signal_info {
+ u32 frequency;
+ int above_threshold;
+ int current_signal;
+ int current_noise;
+ int current_txrate;
+};
+
+/**
+ * struct wpa_driver_ops - Driver interface API definition
+ *
+ * This structure defines the API that each driver interface needs to implement
+ * for core wpa_supplicant code. All driver specific functionality is captured
+ * in this wrapper.
+ */
+struct wpa_driver_ops {
+ /** Name of the driver interface */
+ const char *name;
+ /** One line description of the driver interface */
+ const char *desc;
+
+ /**
+ * get_bssid - Get the current BSSID
+ * @priv: private driver interface data
+ * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Query kernel driver for the current BSSID and copy it to bssid.
+ * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not
+ * associated.
+ */
+ int (*get_bssid)(void *priv, u8 *bssid);
+
+ /**
+ * get_ssid - Get the current SSID
+ * @priv: private driver interface data
+ * @ssid: buffer for SSID (at least 32 bytes)
+ *
+ * Returns: Length of the SSID on success, -1 on failure
+ *
+ * Query kernel driver for the current SSID and copy it to ssid.
+ * Returning zero is recommended if the STA is not associated.
+ *
+ * Note: SSID is an array of octets, i.e., it is not nul terminated and
+ * can, at least in theory, contain control characters (including nul)
+ * and as such, should be processed as binary data, not a printable
+ * string.
+ */
+ int (*get_ssid)(void *priv, u8 *ssid);
+
+ /**
+ * set_key - Configure encryption key
+ * @ifname: Interface name (for multi-SSID/VLAN support)
+ * @priv: private driver interface data
+ * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
+ * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK);
+ * %WPA_ALG_NONE clears the key.
+ * @addr: Address of the peer STA (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: 6 octets, IGTK: 6 octets
+ * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
+ * 8-byte Rx Mic Key
+ * @key_len: length of the key buffer in octets (WEP: 5 or 13,
+ * TKIP: 32, CCMP: 16, IGTK: 16)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Configure the given key for the kernel driver. If the driver
+ * supports separate individual keys (4 default keys + 1 individual),
+ * addr can be used to determine whether the key is default or
+ * individual. If only 4 keys are supported, the default key with key
+ * index 0 is used as the individual key. STA must be configured to use
+ * it as the default Tx key (set_tx is set) and accept Rx for all the
+ * key indexes. In most cases, WPA uses only key indexes 1 and 2 for
+ * broadcast keys, so key index 0 is available for this kind of
+ * configuration.
+ *
+ * Please note that TKIP keys include separate TX and RX MIC keys and
+ * some drivers may expect them in different order than wpa_supplicant
+ * is using. If the TX/RX keys are swapped, all TKIP encrypted packets
+ * will 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);
+
+ /**
+ * disassociate - Request driver to disassociate
+ * @priv: private driver interface data
+ * @addr: peer address (BSSID of the AP)
+ * @reason_code: 16-bit reason code to be sent in the disassociation
+ * frame
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*disassociate)(void *priv, const u8 *addr, int reason_code);
+
+ /**
+ * associate - Request driver to associate
+ * @priv: private driver interface data
+ * @params: association parameters
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*associate)(void *priv,
+ struct wpa_driver_associate_params *params);
+
+ /**
+ * add_pmkid - Add PMKSA cache entry to the driver
+ * @priv: private driver interface data
+ * @bssid: BSSID for the PMKSA cache entry
+ * @pmkid: PMKID for the PMKSA cache entry
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when a new PMK is received, as a result of
+ * either normal authentication or RSN pre-authentication.
+ *
+ * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+ * associate(), add_pmkid() can be used to add new PMKSA cache entries
+ * in the driver. If the driver uses wpa_ie from wpa_supplicant, this
+ * driver_ops function does not need to be implemented. Likewise, if
+ * the driver does not support WPA, this function is not needed.
+ */
+ int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+
+ /**
+ * remove_pmkid - Remove PMKSA cache entry to the driver
+ * @priv: private driver interface data
+ * @bssid: BSSID for the PMKSA cache entry
+ * @pmkid: PMKID for the PMKSA cache entry
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when the supplicant drops a PMKSA cache
+ * entry for any reason.
+ *
+ * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+ * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+ * between the driver and wpa_supplicant. If the driver uses wpa_ie
+ * from wpa_supplicant, this driver_ops function does not need to be
+ * implemented. Likewise, if the driver does not support WPA, this
+ * function is not needed.
+ */
+ int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+
+ /**
+ * flush_pmkid - Flush PMKSA cache
+ * @priv: private driver interface data
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when the supplicant drops all PMKSA cache
+ * entries for any reason.
+ *
+ * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+ * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+ * between the driver and wpa_supplicant. If the driver uses wpa_ie
+ * from wpa_supplicant, this driver_ops function does not need to be
+ * implemented. Likewise, if the driver does not support WPA, this
+ * function is not needed.
+ */
+ int (*flush_pmkid)(void *priv);
+
+ /**
+ * get_capa - Get driver capabilities
+ * @priv: private driver interface data
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get driver/firmware/hardware capabilities.
+ */
+ int (*get_capa)(void *priv, struct wpa_driver_capa *capa);
+
+ /**
+ * poll - Poll driver for association information
+ * @priv: private driver interface data
+ *
+ * This is an option callback that can be used when the driver does not
+ * provide event mechanism for association events. This is called when
+ * receiving WPA EAPOL-Key messages that require association
+ * information. The driver interface is supposed to generate associnfo
+ * event before returning from this callback function. In addition, the
+ * driver interface should generate an association event after having
+ * sent out associnfo.
+ */
+ void (*poll)(void *priv);
+
+ /**
+ * get_ifname - Get interface name
+ * @priv: private driver interface data
+ *
+ * Returns: Pointer to the interface name. This can differ from the
+ * interface name used in init() call. Init() is called first.
+ *
+ * This optional function can be used to allow the driver interface to
+ * replace the interface name with something else, e.g., based on an
+ * interface mapping from a more descriptive name.
+ */
+ const char * (*get_ifname)(void *priv);
+
+ /**
+ * get_mac_addr - Get own MAC address
+ * @priv: private driver interface data
+ *
+ * Returns: Pointer to own MAC address or %NULL on failure
+ *
+ * This optional function can be used to get the own MAC address of the
+ * device from the driver interface code. This is only needed if the
+ * l2_packet implementation for the OS does not provide easy access to
+ * a MAC address. */
+ const u8 * (*get_mac_addr)(void *priv);
+
+ /**
+ * send_eapol - Optional function for sending EAPOL packets
+ * @priv: private driver interface data
+ * @dest: Destination MAC address
+ * @proto: Ethertype
+ * @data: EAPOL packet starting with IEEE 802.1X header
+ * @data_len: Size of the EAPOL packet
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This optional function can be used to override l2_packet operations
+ * with driver specific functionality. If this function pointer is set,
+ * l2_packet module is not used at all and the driver interface code is
+ * responsible for receiving and sending all EAPOL packets. The
+ * received EAPOL packets are sent to core code with EVENT_EAPOL_RX
+ * event. The driver interface is required to implement get_mac_addr()
+ * handler if send_eapol() is used.
+ */
+ int (*send_eapol)(void *priv, const u8 *dest, u16 proto,
+ const u8 *data, size_t data_len);
+
+ /**
+ * set_operstate - Sets device operating state to DORMANT or UP
+ * @priv: private driver interface data
+ * @state: 0 = dormant, 1 = up
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function that can be used on operating systems
+ * that support a concept of controlling network device state from user
+ * space applications. This function, if set, gets called with
+ * state = 1 when authentication has been completed and with state = 0
+ * when connection is lost.
+ */
+ int (*set_operstate)(void *priv, int state);
+
+ /**
+ * mlme_setprotection - MLME-SETPROTECTION.request primitive
+ * @priv: Private driver interface data
+ * @addr: Address of the station for which to set protection (may be
+ * %NULL for group keys)
+ * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_*
+ * @key_type: MLME_SETPROTECTION_KEY_TYPE_*
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function that can be used to set the driver to
+ * require protection for Tx and/or Rx frames. This uses the layer
+ * interface defined in IEEE 802.11i-2004 clause 10.3.22.1
+ * (MLME-SETPROTECTION.request). Many drivers do not use explicit
+ * set protection operation; instead, they set protection implicitly
+ * based on configured keys.
+ */
+ int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type,
+ int key_type);
+
+ /**
+ * get_hw_feature_data - Get hardware support data (channels and rates)
+ * @priv: Private driver interface data
+ * @num_modes: Variable for returning the number of returned modes
+ * flags: Variable for returning hardware feature flags
+ * Returns: Pointer to allocated hardware data on success or %NULL on
+ * failure. Caller is responsible for freeing this.
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to %wpa_supplicant or hostapd.
+ */
+ struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
+ u16 *num_modes,
+ u16 *flags);
+
+ /**
+ * set_channel - Set channel
+ * @priv: Private driver interface data
+ * @phymode: HOSTAPD_MODE_IEEE80211B, ..
+ * @chan: IEEE 802.11 channel number
+ * @freq: Frequency of the channel in MHz
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to wpa_supplicant.
+ */
+ int (*set_channel)(void *priv, enum hostapd_hw_mode phymode, int chan,
+ int freq);
+
+ /**
+ * set_ssid - Set SSID
+ * @priv: Private driver interface data
+ * @ssid: SSID
+ * @ssid_len: SSID length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to wpa_supplicant.
+ */
+ int (*set_ssid)(void *priv, const u8 *ssid, size_t ssid_len);
+
+ /**
+ * set_bssid - Set BSSID
+ * @priv: Private driver interface data
+ * @bssid: BSSID
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to wpa_supplicant.
+ */
+ int (*set_bssid)(void *priv, const u8 *bssid);
+
+ /**
+ * send_mlme - Send management frame from MLME
+ * @priv: Private driver interface data
+ * @data: IEEE 802.11 management frame with IEEE 802.11 header
+ * @data_len: Size of the management frame
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to wpa_supplicant.
+ */
+ int (*send_mlme)(void *priv, const u8 *data, size_t data_len);
+
+ /**
+ * mlme_add_sta - Add a STA entry into the driver/netstack
+ * @priv: Private driver interface data
+ * @addr: MAC address of the STA (e.g., BSSID of the AP)
+ * @supp_rates: Supported rate set (from (Re)AssocResp); in IEEE 802.11
+ * format (one octet per rate, 1 = 0.5 Mbps)
+ * @supp_rates_len: Number of entries in supp_rates
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to wpa_supplicant. When the MLME code
+ * completes association with an AP, this function is called to
+ * configure the driver/netstack with a STA entry for data frame
+ * processing (TX rate control, encryption/decryption).
+ */
+ int (*mlme_add_sta)(void *priv, const u8 *addr, const u8 *supp_rates,
+ size_t supp_rates_len);
+
+ /**
+ * mlme_remove_sta - Remove a STA entry from the driver/netstack
+ * @priv: Private driver interface data
+ * @addr: MAC address of the STA (e.g., BSSID of the AP)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to wpa_supplicant.
+ */
+ int (*mlme_remove_sta)(void *priv, const u8 *addr);
+
+ /**
+ * update_ft_ies - Update FT (IEEE 802.11r) IEs
+ * @priv: Private driver interface data
+ * @md: Mobility domain (2 octets) (also included inside ies)
+ * @ies: FT IEs (MDIE, FTIE, ...) or %NULL to remove IEs
+ * @ies_len: Length of FT IEs in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * The supplicant uses this callback to let the driver know that keying
+ * material for FT is available and that the driver can use the
+ * provided IEs in the next message in FT authentication sequence.
+ *
+ * This function is only needed for driver that support IEEE 802.11r
+ * (Fast BSS Transition).
+ */
+ int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies,
+ size_t ies_len);
+
+ /**
+ * send_ft_action - Send FT Action frame (IEEE 802.11r)
+ * @priv: Private driver interface data
+ * @action: Action field value
+ * @target_ap: Target AP address
+ * @ies: FT IEs (MDIE, FTIE, ...) (FT Request action frame body)
+ * @ies_len: Length of FT IEs in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * The supplicant uses this callback to request the driver to transmit
+ * an FT Action frame (action category 6) for over-the-DS fast BSS
+ * transition.
+ */
+ int (*send_ft_action)(void *priv, u8 action, const u8 *target_ap,
+ const u8 *ies, size_t ies_len);
+
+ /**
+ * get_scan_results2 - Fetch the latest scan results
+ * @priv: private driver interface data
+ *
+ * Returns: Allocated buffer of scan results (caller is responsible for
+ * freeing the data structure) on success, NULL on failure
+ */
+ struct wpa_scan_results * (*get_scan_results2)(void *priv);
+
+ /**
+ * set_country - Set country
+ * @priv: Private driver interface data
+ * @alpha2: country to which to switch to
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is for drivers which support some form
+ * of setting a regulatory domain.
+ */
+ int (*set_country)(void *priv, const char *alpha2);
+
+ /**
+ * global_init - Global driver initialization
+ * Returns: Pointer to private data (global), %NULL on failure
+ *
+ * This optional function is called to initialize the driver wrapper
+ * for global data, i.e., data that applies to all interfaces. If this
+ * function is implemented, global_deinit() will also need to be
+ * implemented to free the private data. The driver will also likely
+ * use init2() function instead of init() to get the pointer to global
+ * data available to per-interface initializer.
+ */
+ void * (*global_init)(void);
+
+ /**
+ * global_deinit - Global driver deinitialization
+ * @priv: private driver global data from global_init()
+ *
+ * Terminate any global driver related functionality and free the
+ * global data structure.
+ */
+ void (*global_deinit)(void *priv);
+
+ /**
+ * init2 - Initialize driver interface (with global data)
+ * @ctx: context to be used when calling wpa_supplicant functions,
+ * e.g., wpa_supplicant_event()
+ * @ifname: interface name, e.g., wlan0
+ * @global_priv: private driver global data from global_init()
+ * Returns: Pointer to private data, %NULL on failure
+ *
+ * This function can be used instead of init() if the driver wrapper
+ * uses global data.
+ */
+ void * (*init2)(void *ctx, const char *ifname, void *global_priv);
+
+ /**
+ * get_interfaces - Get information about available interfaces
+ * @global_priv: private driver global data from global_init()
+ * Returns: Allocated buffer of interface information (caller is
+ * responsible for freeing the data structure) on success, NULL on
+ * failure
+ */
+ struct wpa_interface_info * (*get_interfaces)(void *global_priv);
+
+ /**
+ * scan2 - Request the driver to initiate scan
+ * @priv: private driver interface data
+ * @params: Scan parameters
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Once the scan results are ready, the driver should report scan
+ * results event for wpa_supplicant which will eventually request the
+ * results with wpa_driver_get_scan_results2().
+ */
+ int (*scan2)(void *priv, struct wpa_driver_scan_params *params);
+
+ /**
+ * authenticate - Request driver to authenticate
+ * @priv: private driver interface data
+ * @params: authentication parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function that can be used with drivers that
+ * support separate authentication and association steps, i.e., when
+ * wpa_supplicant can act as the SME. If not implemented, associate()
+ * function is expected to take care of IEEE 802.11 authentication,
+ * too.
+ */
+ int (*authenticate)(void *priv,
+ struct wpa_driver_auth_params *params);
+
+ /**
+ * set_beacon - Set Beacon frame template
+ * @priv: Private driver interface data
+ * @head: Beacon head from IEEE 802.11 header to IEs before TIM IE
+ * @head_len: Length of the head buffer in octets
+ * @tail: Beacon tail following TIM IE
+ * @tail_len: Length of the tail buffer in octets
+ * @dtim_period: DTIM period
+ * @beacon_int: Beacon interval
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to configure Beacon template for the driver in
+ * AP mode. The driver is responsible for building the full Beacon
+ * frame by concatenating the head part with TIM IE generated by the
+ * driver/firmware and finishing with the tail part.
+ */
+ int (*set_beacon)(void *priv, const u8 *head, size_t head_len,
+ const u8 *tail, size_t tail_len, int dtim_period,
+ int beacon_int);
+
+ /**
+ * hapd_init - Initialize driver interface (hostapd only)
+ * @hapd: Pointer to hostapd context
+ * @params: Configuration for the driver wrapper
+ * Returns: Pointer to private data, %NULL on failure
+ *
+ * This function is used instead of init() or init2() when the driver
+ * wrapper is used withh hostapd.
+ */
+ void * (*hapd_init)(struct hostapd_data *hapd,
+ struct wpa_init_params *params);
+
+ /**
+ * hapd_deinit - Deinitialize driver interface (hostapd only)
+ * @priv: Private driver interface data from hapd_init()
+ */
+ void (*hapd_deinit)(void *priv);
+
+ /**
+ * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only)
+ * @priv: Private driver interface data
+ * @params: BSS parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function to configure the kernel driver to
+ * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This
+ * can be left undefined (set to %NULL) if IEEE 802.1X support is
+ * always enabled and the driver uses set_beacon() to set WPA/RSN IE
+ * for Beacon frames.
+ */
+ int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params);
+
+ /**
+ * set_privacy - Enable/disable privacy (AP only)
+ * @priv: Private driver interface data
+ * @enabled: 1 = privacy enabled, 0 = disabled
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function to configure privacy field in the
+ * kernel driver for Beacon frames. This can be left undefined (set to
+ * %NULL) if the driver uses the Beacon template from set_beacon().
+ */
+ int (*set_privacy)(void *priv, int enabled);
+
+ /**
+ * get_seqnum - Fetch the current TSC/packet number (AP only)
+ * @ifname: The interface name (main or virtual)
+ * @priv: Private driver interface data
+ * @addr: MAC address of the station or %NULL for group keys
+ * @idx: Key index
+ * @seq: Buffer for returning the latest used TSC/packet number
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to fetch the last used TSC/packet number for
+ * a TKIP, CCMP, or BIP/IGTK key. It is mainly used with group keys, so
+ * there is no strict requirement on implementing support for unicast
+ * keys (i.e., addr != %NULL).
+ */
+ int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr,
+ int idx, u8 *seq);
+
+ /**
+ * flush - Flush all association stations (AP only)
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function requests the driver to disassociate all associated
+ * stations. This function does not need to be implemented if the
+ * driver does not process association frames internally.
+ */
+ int (*flush)(void *priv);
+
+ /**
+ * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
+ * @priv: Private driver interface data
+ * @elem: Information elements
+ * @elem_len: Length of the elem buffer in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function to add information elements in the
+ * kernel driver for Beacon and Probe Response frames. This can be left
+ * undefined (set to %NULL) if the driver uses the Beacon template from
+ * set_beacon().
+ */
+ int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
+
+ /**
+ * read_sta_data - Fetch station data (AP only)
+ * @priv: Private driver interface data
+ * @data: Buffer for returning station information
+ * @addr: MAC address of the station
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data,
+ const u8 *addr);
+
+ /**
+ * hapd_send_eapol - Send an EAPOL packet (AP only)
+ * @priv: private driver interface data
+ * @addr: Destination MAC address
+ * @data: EAPOL packet starting with IEEE 802.1X header
+ * @data_len: Length of the EAPOL packet in octets
+ * @encrypt: Whether the frame should be encrypted
+ * @own_addr: Source MAC address
+ * @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_beacon() and does not reply to Probe Request
+ * frames.
+ */
+ int (*hapd_get_ssid)(void *priv, u8 *buf, int len);
+
+ /**
+ * hapd_set_ssid - Set SSID (AP only)
+ * @priv: Private driver interface data
+ * @buf: SSID
+ * @len: Length of the SSID in octets
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*hapd_set_ssid)(void *priv, const u8 *buf, int len);
+
+ /**
+ * hapd_set_countermeasures - Enable/disable TKIP countermeasures (AP)
+ * @priv: Private driver interface data
+ * @enabled: 1 = countermeasures enabled, 0 = disabled
+ * Returns: 0 on success, -1 on failure
+ *
+ * This need not be implemented if the driver does not take care of
+ * association processing.
+ */
+ int (*hapd_set_countermeasures)(void *priv, int enabled);
+
+ /**
+ * sta_add - Add a station entry
+ * @priv: Private driver interface data
+ * @params: Station parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to add a station entry to the driver once the
+ * station has completed association. This is only used if the driver
+ * does not take care of association processing.
+ */
+ int (*sta_add)(void *priv, struct hostapd_sta_add_params *params);
+
+ /**
+ * get_inact_sec - Get station inactivity duration (AP only)
+ * @priv: Private driver interface data
+ * @addr: Station address
+ * Returns: Number of seconds station has been inactive, -1 on failure
+ */
+ int (*get_inact_sec)(void *priv, const u8 *addr);
+
+ /**
+ * sta_clear_stats - Clear station statistics (AP only)
+ * @priv: Private driver interface data
+ * @addr: Station address
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*sta_clear_stats)(void *priv, const u8 *addr);
+
+ /**
+ * set_freq - Set channel/frequency (AP only)
+ * @priv: Private driver interface data
+ * @freq: Channel parameters
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_freq)(void *priv, struct hostapd_freq_params *freq);
+
+ /**
+ * set_rts - Set RTS threshold
+ * @priv: Private driver interface data
+ * @rts: RTS threshold in octets
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_rts)(void *priv, int rts);
+
+ /**
+ * set_frag - Set fragmentation threshold
+ * @priv: Private driver interface data
+ * @frag: Fragmentation threshold in octets
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_frag)(void *priv, int frag);
+
+ /**
+ * sta_set_flags - Set station flags (AP only)
+ * @priv: Private driver interface data
+ * @addr: Station address
+ * @total_flags: Bitmap of all WPA_STA_* flags currently set
+ * @flags_or: Bitmap of WPA_STA_* flags to add
+ * @flags_and: Bitmap of WPA_STA_* flags to us as a mask
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*sta_set_flags)(void *priv, const u8 *addr,
+ int total_flags, int flags_or, int flags_and);
+
+ /**
+ * set_rate_sets - Set supported and basic rate sets (AP only)
+ * @priv: Private driver interface data
+ * @supp_rates: -1 terminated array of supported rates in 100 kbps
+ * @basic_rates: -1 terminated array of basic rates in 100 kbps
+ * @mode: hardware mode (HOSTAPD_MODE_*)
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_rate_sets)(void *priv, int *supp_rates, int *basic_rates,
+ int mode);
+
+ /**
+ * set_cts_protect - Set CTS protection mode (AP only)
+ * @priv: Private driver interface data
+ * @value: Whether CTS protection is enabled
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_cts_protect)(void *priv, int value);
+
+ /**
+ * set_preamble - Set preamble mode (AP only)
+ * @priv: Private driver interface data
+ * @value: Whether short preamble is enabled
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_preamble)(void *priv, int value);
+
+ /**
+ * set_short_slot_time - Set short slot time (AP only)
+ * @priv: Private driver interface data
+ * @value: Whether short slot time is enabled
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_short_slot_time)(void *priv, int value);
+
+ /**
+ * set_tx_queue_params - Set TX queue parameters
+ * @priv: Private driver interface data
+ * @queue: Queue number (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);
+
+ /**
+ * valid_bss_mask - Validate BSSID mask
+ * @priv: Private driver interface data
+ * @addr: Address
+ * @mask: Mask
+ * Returns: 0 if mask is valid, -1 if mask is not valid, 1 if mask can
+ * be used, but the main interface address must be the first address in
+ * the block if mask is applied
+ */
+ int (*valid_bss_mask)(void *priv, const u8 *addr, const u8 *mask);
+
+ /**
+ * if_add - Add a virtual interface
+ * @priv: Private driver interface data
+ * @type: Interface type
+ * @ifname: Interface name for the new virtual interface
+ * @addr: Local address to use for the interface or %NULL to use the
+ * parent interface address
+ * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
+ * @drv_priv: Pointer for overwriting the driver context or %NULL if
+ * not allowed (applies only to %WPA_IF_AP_BSS type)
+ * @force_ifname: Buffer for returning an interface name that the
+ * driver ended up using if it differs from the requested ifname
+ * @if_addr: Buffer for returning the allocated interface address
+ * (this may differ from the requested addr if the driver cannot
+ * change interface address)
+ * @bridge: Bridge interface to use or %NULL if no bridge configured
+ * 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);
+
+ /**
+ * if_remove - Remove a virtual interface
+ * @priv: Private driver interface data
+ * @type: Interface type
+ * @ifname: Interface name of the virtual interface to be removed
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*if_remove)(void *priv, enum wpa_driver_if_type type,
+ const char *ifname);
+
+ /**
+ * set_sta_vlan - Bind a station into a specific interface (AP only)
+ * @priv: Private driver interface data
+ * @ifname: Interface (main or virtual BSS or VLAN)
+ * @addr: MAC address of the associated station
+ * @vlan_id: VLAN ID
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to bind a station to a specific virtual
+ * interface. It is only used if when virtual interfaces are supported,
+ * e.g., to assign stations to different VLAN interfaces based on
+ * information from a RADIUS server. This allows separate broadcast
+ * domains to be used with a single BSS.
+ */
+ int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname,
+ int vlan_id);
+
+ /**
+ * commit - Optional commit changes handler (AP only)
+ * @priv: driver private data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This optional handler function can be registered if the driver
+ * interface implementation needs to commit changes (e.g., by setting
+ * network interface up) at the end of initial configuration. If set,
+ * this handler will be called after initial setup has been completed.
+ */
+ int (*commit)(void *priv);
+
+ /**
+ * send_ether - Send an ethernet packet (AP only)
+ * @priv: private driver interface data
+ * @dst: Destination MAC address
+ * @src: Source MAC address
+ * @proto: Ethertype
+ * @data: EAPOL packet starting with IEEE 802.1X header
+ * @data_len: Length of the EAPOL packet in octets
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*send_ether)(void *priv, const u8 *dst, const u8 *src, u16 proto,
+ const u8 *data, size_t data_len);
+
+ /**
+ * set_radius_acl_auth - Notification of RADIUS ACL change
+ * @priv: Private driver interface data
+ * @mac: MAC address of the station
+ * @accepted: Whether the station was accepted
+ * @session_timeout: Session timeout for the station
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted,
+ u32 session_timeout);
+
+ /**
+ * set_radius_acl_expire - Notification of RADIUS ACL expiration
+ * @priv: Private driver interface data
+ * @mac: MAC address of the station
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_radius_acl_expire)(void *priv, const u8 *mac);
+
+ /**
+ * set_ht_params - Set HT parameters (AP only)
+ * @priv: Private driver interface data
+ * @ht_capab: HT Capabilities IE
+ * @ht_capab_len: Length of ht_capab in octets
+ * @ht_oper: HT Operation IE
+ * @ht_oper_len: Length of ht_oper in octets
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_ht_params)(void *priv,
+ const u8 *ht_capab, size_t ht_capab_len,
+ const u8 *ht_oper, size_t ht_oper_len);
+
+ /**
+ * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP)
+ * @priv: Private driver interface data
+ * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s)
+ * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove
+ * extra IE(s)
+ * @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_beacon()
+ * 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.
+ */
+ 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
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
+ const char *bridge_ifname);
+
+ /**
+ * 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
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * disable_11b_rates - Set whether IEEE 802.11b rates are used for TX
+ * @priv: Private driver interface data
+ * @disabled: Whether IEEE 802.11b rates are disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ *
+ * This command is used to disable IEEE 802.11b rates (1, 2, 5.5, and
+ * 11 Mbps) as TX rates for data and management frames. This can be
+ * used to optimize channel use when there is no need to support IEEE
+ * 802.11b-only devices.
+ */
+ int (*disable_11b_rates)(void *priv, int disabled);
+
+ /**
+ * deinit_ap - Deinitialize AP mode
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ *
+ * This optional function can be used to disable AP mode related
+ * configuration and change the driver mode to station mode to allow
+ * normal station operations like scanning to be completed.
+ */
+ int (*deinit_ap)(void *priv);
+
+ /**
+ * suspend - Notification on system suspend/hibernate event
+ * @priv: Private driver interface data
+ */
+ void (*suspend)(void *priv);
+
+ /**
+ * resume - Notification on system resume/thaw event
+ * @priv: Private driver interface data
+ */
+ void (*resume)(void *priv);
+
+ /**
+ * signal_monitor - Set signal monitoring parameters
+ * @priv: Private driver interface data
+ * @threshold: Threshold value for signal change events; 0 = disabled
+ * @hysteresis: Minimum change in signal strength before indicating a
+ * new event
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ *
+ * This function can be used to configure monitoring of signal strength
+ * with the current AP. Whenever signal strength drops below the
+ * %threshold value or increases above it, EVENT_SIGNAL_CHANGE event
+ * should be generated assuming the signal strength has changed at
+ * least %hysteresis from the previously indicated signal change event.
+ */
+ int (*signal_monitor)(void *priv, int threshold, int hysteresis);
+
+ /**
+ * send_frame - Send IEEE 802.11 frame (testing use only)
+ * @priv: Private driver interface data
+ * @data: IEEE 802.11 frame with IEEE 802.11 header
+ * @data_len: Size of the frame
+ * @encrypt: Whether to encrypt the frame (if keys are set)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used for debugging purposes and is not
+ * required to be implemented for normal operations.
+ */
+ int (*send_frame)(void *priv, const u8 *data, size_t data_len,
+ int encrypt);
+
+ /**
+ * 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);
+
+ /**
+ * set_intra_bss - Enables/Disables intra BSS bridging
+ */
+ int (*set_intra_bss)(void *priv, int enabled);
+
+ /**
+ * 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);
+
+ /**
+ * 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, -1 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);
+
+ int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer);
+
+ /**
+ * 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);
+};
+
+
+/**
+ * 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
+};
+
+
+/**
+ * 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;
+ } 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;
+ } 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 ft_ies - FT information elements (EVENT_FT_RESPONSE)
+ *
+ * During FT (IEEE 802.11r) authentication sequence, the driver is
+ * expected to use this event to report received FT IEs (MDIE, FTIE,
+ * RSN IE, TIE, possible resource request) to the supplicant. The FT
+ * IEs for the next message will be delivered through the
+ * struct wpa_driver_ops::update_ft_ies() callback.
+ */
+ struct ft_ies {
+ const u8 *ies;
+ size_t ies_len;
+ int ft_action;
+ u8 target_ap[ETH_ALEN];
+ /** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */
+ const u8 *ric_ies;
+ /** Length of ric_ies buffer in octets */
+ size_t ric_ies_len;
+ } ft_ies;
+
+ /**
+ * struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START
+ */
+ struct ibss_rsn_start {
+ u8 peer[ETH_ALEN];
+ } ibss_rsn_start;
+
+ /**
+ * struct auth_info - Data for EVENT_AUTH events
+ */
+ struct auth_info {
+ u8 peer[ETH_ALEN];
+ u16 auth_type;
+ u16 status_code;
+ const u8 *ies;
+ size_t ies_len;
+ } auth;
+
+ /**
+ * struct assoc_reject - Data for EVENT_ASSOC_REJECT events
+ */
+ struct assoc_reject {
+ /**
+ * 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 *frame;
+ size_t len;
+ } rx_from_unknown;
+
+ /**
+ * struct rx_mgmt - Data for EVENT_RX_MGMT events
+ */
+ struct rx_mgmt {
+ const u8 *frame;
+ size_t frame_len;
+ u32 datarate;
+ u32 ssi_signal;
+ } rx_mgmt;
+
+ /**
+ * struct rx_action - Data for EVENT_RX_ACTION events
+ */
+ struct rx_action {
+ /**
+ * da - Destination address of the received Action frame
+ */
+ const u8 *da;
+
+ /**
+ * sa - Source address of the received Action frame
+ */
+ const u8 *sa;
+
+ /**
+ * bssid - Address 3 of the received Action frame
+ */
+ const u8 *bssid;
+
+ /**
+ * category - Action frame category
+ */
+ u8 category;
+
+ /**
+ * data - Action frame body after category field
+ */
+ const u8 *data;
+
+ /**
+ * len - Length of data in octets
+ */
+ size_t len;
+
+ /**
+ * freq - Frequency (in MHz) on which the frame was received
+ */
+ int freq;
+ } rx_action;
+
+ /**
+ * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events
+ *
+ * This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events.
+ */
+ struct remain_on_channel {
+ /**
+ * freq - Channel frequency in MHz
+ */
+ unsigned int freq;
+
+ /**
+ * duration - Duration to remain on the channel in milliseconds
+ */
+ unsigned int duration;
+ } remain_on_channel;
+
+ /**
+ * struct scan_info - Optional data for EVENT_SCAN_RESULTS events
+ * @aborted: Whether the scan was aborted
+ * @freqs: Scanned frequencies in MHz (%NULL = all channels scanned)
+ * @num_freqs: Number of entries in freqs array
+ * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
+ * SSID)
+ * @num_ssids: Number of entries in ssids array
+ */
+ struct scan_info {
+ int aborted;
+ const int *freqs;
+ size_t num_freqs;
+ struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
+ size_t num_ssids;
+ } scan_info;
+
+ /**
+ * struct mlme_rx - Data for EVENT_MLME_RX events
+ */
+ struct mlme_rx {
+ const u8 *buf;
+ size_t len;
+ int freq;
+ int channel;
+ int ssi;
+ } mlme_rx;
+
+ /**
+ * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events
+ */
+ struct rx_probe_req {
+ /**
+ * sa - Source address of the received Probe Request frame
+ */
+ const u8 *sa;
+
+ /**
+ * ie - IEs from the Probe Request body
+ */
+ const u8 *ie;
+
+ /**
+ * ie_len - Length of ie buffer in octets
+ */
+ size_t ie_len;
+ } rx_probe_req;
+
+ /**
+ * struct new_sta - Data for EVENT_NEW_STA events
+ */
+ struct new_sta {
+ const u8 *addr;
+ } new_sta;
+
+ /**
+ * struct eapol_rx - Data for EVENT_EAPOL_RX events
+ */
+ struct eapol_rx {
+ const u8 *src;
+ const u8 *data;
+ size_t data_len;
+ } eapol_rx;
+
+ /**
+ * 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;
+};
+
+/**
+ * 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);
+}
+
+#endif /* DRIVER_H */
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
new file mode 100644
index 0000000..6ac1cea
--- /dev/null
+++ b/src/drivers/driver_atheros.c
@@ -0,0 +1,1381 @@
+/*
+ * hostapd / Driver interaction with Atheros driver
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, Video54 Technologies
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "common.h"
+#ifndef _BYTE_ORDER
+#ifdef WORDS_BIGENDIAN
+#define _BYTE_ORDER _BIG_ENDIAN
+#else
+#define _BYTE_ORDER _LITTLE_ENDIAN
+#endif
+#endif /* _BYTE_ORDER */
+
+/*
+ * Note, the ATH_WPS_IE setting must match with the driver build.. If the
+ * driver does not include this, the IEEE80211_IOCTL_GETWPAIE ioctl will fail.
+ */
+#define ATH_WPS_IE
+
+#include "os/linux/include/ieee80211_external.h"
+
+
+#ifdef CONFIG_WPS
+#include <netpacket/packet.h>
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW 0x0019
+#endif
+#endif /* CONFIG_WPS */
+
+#include "wireless_copy.h"
+
+#include "driver.h"
+#include "eloop.h"
+#include "priv_netlink.h"
+#include "l2_packet/l2_packet.h"
+#include "common/ieee802_11_defs.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+
+
+struct 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;
+};
+
+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<<IEEE80211_CIPHER_AES_CCM;
+ if (params->wpa_pairwise & WPA_CIPHER_TKIP)
+ v |= 1<<IEEE80211_CIPHER_TKIP;
+ if (params->wpa_pairwise & WPA_CIPHER_NONE)
+ v |= 1<<IEEE80211_CIPHER_NONE;
+ wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
+ if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) {
+ printf("Unable to set pairwise key ciphers to 0x%x\n", v);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
+ __func__, params->wpa_key_mgmt);
+ if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS,
+ params->wpa_key_mgmt)) {
+ printf("Unable to set key management algorithms to 0x%x\n",
+ params->wpa_key_mgmt);
+ return -1;
+ }
+
+ v = 0;
+ if (params->rsn_preauth)
+ v |= BIT(0);
+#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__, 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
+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) {
+ hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
+ HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
+ return -1;
+ }
+ if (params->wpa && atheros_configure_wpa(drv, params) != 0) {
+ hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
+ HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
+ return -1;
+ }
+ if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
+ (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+ hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
+ HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+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);
+
+ 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);
+ }
+ 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;
+ 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_receive(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.ie = mgmt->u.probe_req.variable;
+ event.rx_probe_req.ie_len =
+ len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+ wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
+}
+#endif /* CONFIG_WPS */
+
+static int atheros_receive_probe_req(struct atheros_driver_data *drv)
+{
+ int ret = 0;
+#ifdef CONFIG_WPS
+ struct ieee80211req_set_filter filt;
+
+ wpa_printf(MSG_DEBUG, "%s Enter", __func__);
+ filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ;
+
+ ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+ sizeof(struct ieee80211req_set_filter));
+ if (ret)
+ return ret;
+
+ drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
+ atheros_raw_receive, drv, 1);
+ if (drv->sock_raw == NULL)
+ return -1;
+#endif /* CONFIG_WPS */
+ return ret;
+}
+
+#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", __func__,
+ (unsigned long) 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)) {
+ 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);
+ }
+
+ 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;
+
+ 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 */
+
+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);
+ } else if (strncmp(custom, "Manage.prob_req ", 16) == 0) {
+ /*
+ * Atheros driver uses a hack to pass Probe Request frames as a
+ * binary data in the custom wireless event. The old way (using
+ * packet sniffing) didn't work when bridging.
+ * Format: "Manage.prob_req <frame len>" | zero padding | frame
+ */
+#define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */
+ int len = atoi(custom + 16);
+ if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) {
+ wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event "
+ "length %d", len);
+ return;
+ }
+ atheros_raw_receive(drv, NULL,
+ (u8 *) custom + WPS_FRAM_TAG_SIZE, len);
+#endif /* CONFIG_WPS */
+ }
+}
+
+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';
+ 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;
+ }
+
+ 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;
+ 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 */
+
+ atheros_receive_probe_req(drv);
+
+ if (atheros_wireless_event_init(drv))
+ goto bad;
+
+ return drv;
+bad:
+ if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
+ l2_packet_deinit(drv->sock_recv);
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+ if (drv->ioctl_sock >= 0)
+ close(drv->ioctl_sock);
+ if (drv != NULL)
+ free(drv);
+ return NULL;
+}
+
+
+static void
+atheros_deinit(void *priv)
+{
+ struct atheros_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);
+ 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;
+
+ 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);
+}
+
+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,
+};
diff --git a/src/drivers/driver_broadcom.c b/src/drivers/driver_broadcom.c
new file mode 100644
index 0000000..cb88543
--- /dev/null
+++ b/src/drivers/driver_broadcom.c
@@ -0,0 +1,599 @@
+/*
+ * WPA Supplicant - driver interaction with old Broadcom wl.o driver
+ * Copyright (c) 2004, Nikki Chumkov <nikki@gattaca.ru>
+ * Copyright (c) 2004, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * Please note that the newer Broadcom driver ("hybrid Linux driver") supports
+ * Linux wireless extensions and does not need (or even work) with this old
+ * driver wrapper. Use driver_wext.c with that driver.
+ */
+
+#include "includes.h"
+
+#include <sys/ioctl.h>
+
+#include "common.h"
+
+#if 0
+#include <netpacket/packet.h>
+#include <net/ethernet.h> /* the L2 protocols */
+#else
+#include <linux/if_packet.h>
+#include <linux/if_ether.h> /* The L2 protocols */
+#endif
+#include <net/if.h>
+#include <typedefs.h>
+
+/* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys
+ * WRT54G GPL tarball. */
+#include <wlioctl.h>
+
+#include "driver.h"
+#include "eloop.h"
+
+struct wpa_driver_broadcom_data {
+ void *ctx;
+ int ioctl_sock;
+ int event_sock;
+ char ifname[IFNAMSIZ + 1];
+};
+
+
+#ifndef WLC_DEAUTHENTICATE
+#define WLC_DEAUTHENTICATE 143
+#endif
+#ifndef WLC_DEAUTHENTICATE_WITH_REASON
+#define WLC_DEAUTHENTICATE_WITH_REASON 201
+#endif
+#ifndef WLC_SET_TKIP_COUNTERMEASURES
+#define WLC_SET_TKIP_COUNTERMEASURES 202
+#endif
+
+#if !defined(PSK_ENABLED) /* NEW driver interface */
+#define WL_VERSION 360130
+/* wireless authentication bit vector */
+#define WPA_ENABLED 1
+#define PSK_ENABLED 2
+
+#define WAUTH_WPA_ENABLED(wauth) ((wauth) & WPA_ENABLED)
+#define WAUTH_PSK_ENABLED(wauth) ((wauth) & PSK_ENABLED)
+#define WAUTH_ENABLED(wauth) ((wauth) & (WPA_ENABLED | PSK_ENABLED))
+
+#define WSEC_PRIMARY_KEY WL_PRIMARY_KEY
+
+typedef wl_wsec_key_t wsec_key_t;
+#endif
+
+typedef struct {
+ uint32 val;
+ struct ether_addr ea;
+ uint16 res;
+} wlc_deauth_t;
+
+
+static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+
+static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd,
+ void *buf, int len)
+{
+ struct ifreq ifr;
+ wl_ioctl_t ioc;
+ int ret = 0;
+
+ wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl(%s,%d,len=%d,val=%p)",
+ drv->ifname, cmd, len, buf);
+ /* wpa_hexdump(MSG_MSGDUMP, "BROADCOM: wlioctl buf", buf, len); */
+
+ ioc.cmd = cmd;
+ ioc.buf = buf;
+ ioc.len = len;
+ os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ);
+ ifr.ifr_data = (caddr_t) &ioc;
+ if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE, &ifr)) < 0) {
+ if (cmd != WLC_GET_MAGIC)
+ perror(ifr.ifr_name);
+ wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl cmd=%d res=%d",
+ cmd, ret);
+ }
+
+ return ret;
+}
+
+static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0)
+ return 0;
+
+ os_memset(bssid, 0, ETH_ALEN);
+ return -1;
+}
+
+static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ wlc_ssid_t s;
+
+ if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1)
+ return -1;
+
+ os_memcpy(ssid, s.SSID, s.SSID_len);
+ return s.SSID_len;
+}
+
+static int wpa_driver_broadcom_set_wpa(void *priv, int enable)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ unsigned int wauth, wsec;
+ struct ether_addr ea;
+
+ os_memset(&ea, enable ? 0xff : 0, sizeof(ea));
+ if (broadcom_ioctl(drv, WLC_GET_WPA_AUTH, &wauth, sizeof(wauth)) ==
+ -1 ||
+ broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1)
+ return -1;
+
+ if (enable) {
+ wauth = PSK_ENABLED;
+ wsec = TKIP_ENABLED;
+ } else {
+ wauth = 255;
+ wsec &= ~(TKIP_ENABLED | AES_ENABLED);
+ }
+
+ if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) ==
+ -1 ||
+ broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1)
+ return -1;
+
+ /* FIX: magic number / error handling? */
+ broadcom_ioctl(drv, 122, &ea, sizeof(ea));
+
+ return 0;
+}
+
+static int wpa_driver_broadcom_set_key(const char *ifname, void *priv,
+ enum wpa_alg alg,
+ const u8 *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ int ret;
+ wsec_key_t wkt;
+
+ os_memset(&wkt, 0, sizeof wkt);
+ wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d",
+ set_tx ? "PRIMARY " : "", key_idx, alg);
+ if (key && key_len > 0)
+ wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len);
+
+ switch (alg) {
+ case WPA_ALG_NONE:
+ wkt.algo = CRYPTO_ALGO_OFF;
+ break;
+ case WPA_ALG_WEP:
+ wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */
+ break;
+ case WPA_ALG_TKIP:
+ wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */
+ break;
+ case WPA_ALG_CCMP:
+ wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM;
+ * AES_OCB_MSDU, AES_OCB_MPDU? */
+ break;
+ default:
+ wkt.algo = CRYPTO_ALGO_NALG;
+ break;
+ }
+
+ if (seq && seq_len > 0)
+ wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len);
+
+ if (addr)
+ wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN);
+
+ wkt.index = key_idx;
+ wkt.len = key_len;
+ if (key && key_len > 0) {
+ os_memcpy(wkt.data, key, key_len);
+ if (key_len == 32) {
+ /* hack hack hack XXX */
+ os_memcpy(&wkt.data[16], &key[24], 8);
+ os_memcpy(&wkt.data[24], &key[16], 8);
+ }
+ }
+ /* wkt.algo = CRYPTO_ALGO_...; */
+ wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY;
+ if (addr && set_tx)
+ os_memcpy(&wkt.ea, addr, sizeof(wkt.ea));
+ ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt));
+ if (addr && set_tx) {
+ /* FIX: magic number / error handling? */
+ broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea));
+ }
+ return ret;
+}
+
+
+static void wpa_driver_broadcom_event_receive(int sock, void *ctx,
+ void *sock_ctx)
+{
+ char buf[8192];
+ int left;
+ wl_wpa_header_t *wwh;
+ union wpa_event_data data;
+ u8 *resp_ies = NULL;
+
+ if ((left = recv(sock, buf, sizeof buf, 0)) < 0)
+ return;
+
+ wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left);
+
+ if ((size_t) left < sizeof(wl_wpa_header_t))
+ return;
+
+ wwh = (wl_wpa_header_t *) buf;
+
+ if (wwh->snap.type != WL_WPA_ETHER_TYPE)
+ return;
+ if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+
+ switch (wwh->type) {
+ case WLC_ASSOC_MSG:
+ left -= WL_WPA_HEADER_LEN;
+ wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)",
+ left);
+ if (left > 0) {
+ resp_ies = os_malloc(left);
+ if (resp_ies == NULL)
+ return;
+ os_memcpy(resp_ies, buf + WL_WPA_HEADER_LEN, left);
+ data.assoc_info.resp_ies = resp_ies;
+ data.assoc_info.resp_ies_len = left;
+ }
+
+ wpa_supplicant_event(ctx, EVENT_ASSOC, &data);
+ os_free(resp_ies);
+ break;
+ case WLC_DISASSOC_MSG:
+ wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE");
+ wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
+ break;
+ case WLC_PTK_MIC_MSG:
+ wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE");
+ data.michael_mic_failure.unicast = 1;
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+ break;
+ case WLC_GTK_MIC_MSG:
+ wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE");
+ data.michael_mic_failure.unicast = 0;
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)",
+ wwh->type);
+ break;
+ }
+}
+
+static void * wpa_driver_broadcom_init(void *ctx, const char *ifname)
+{
+ int s;
+ struct sockaddr_ll ll;
+ struct wpa_driver_broadcom_data *drv;
+ struct ifreq ifr;
+
+ /* open socket to kernel */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return NULL;
+ }
+ /* do it */
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+ perror(ifr.ifr_name);
+ return NULL;
+ }
+
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->ctx = ctx;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ drv->ioctl_sock = s;
+
+ s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2));
+ if (s < 0) {
+ perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))");
+ close(drv->ioctl_sock);
+ os_free(drv);
+ return NULL;
+ }
+
+ os_memset(&ll, 0, sizeof(ll));
+ ll.sll_family = AF_PACKET;
+ ll.sll_protocol = ntohs(ETH_P_802_2);
+ ll.sll_ifindex = ifr.ifr_ifindex;
+ ll.sll_hatype = 0;
+ ll.sll_pkttype = PACKET_HOST;
+ ll.sll_halen = 0;
+
+ if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+ perror("bind(netlink)");
+ close(s);
+ close(drv->ioctl_sock);
+ os_free(drv);
+ return NULL;
+ }
+
+ eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx,
+ NULL);
+ drv->event_sock = s;
+ wpa_driver_broadcom_set_wpa(drv, 1);
+
+ return drv;
+}
+
+static void wpa_driver_broadcom_deinit(void *priv)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ wpa_driver_broadcom_set_wpa(drv, 0);
+ eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx);
+ eloop_unregister_read_sock(drv->event_sock);
+ close(drv->event_sock);
+ close(drv->ioctl_sock);
+ os_free(drv);
+}
+
+static int wpa_driver_broadcom_set_countermeasures(void *priv,
+ int enabled)
+{
+#if 0
+ struct wpa_driver_broadcom_data *drv = priv;
+ /* FIX: ? */
+ return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled,
+ sizeof(enabled));
+#else
+ return 0;
+#endif
+}
+
+static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */
+ int _restrict = (enabled ? 1 : 0);
+
+ if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT,
+ &_restrict, sizeof(_restrict)) < 0 ||
+ broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT,
+ &_restrict, sizeof(_restrict)) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+static int wpa_driver_broadcom_scan(void *priv,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ wlc_ssid_t wst = { 0, "" };
+ const u8 *ssid = params->ssids[0].ssid;
+ size_t ssid_len = params->ssids[0].ssid_len;
+
+ if (ssid && ssid_len > 0 && ssid_len <= sizeof(wst.SSID)) {
+ wst.SSID_len = ssid_len;
+ os_memcpy(wst.SSID, ssid, ssid_len);
+ }
+
+ if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0)
+ return -1;
+
+ eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv,
+ drv->ctx);
+ return 0;
+}
+
+
+static const int frequency_list[] = {
+ 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+ 2447, 2452, 2457, 2462, 2467, 2472, 2484
+};
+
+struct bss_ie_hdr {
+ u8 elem_id;
+ u8 len;
+ u8 oui[3];
+ /* u8 oui_type; */
+ /* u16 version; */
+} __attribute__ ((packed));
+
+static struct wpa_scan_results *
+wpa_driver_broadcom_get_scan_results(void *priv)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ char *buf;
+ wl_scan_results_t *wsr;
+ wl_bss_info_t *wbi;
+ size_t ap_num;
+ struct wpa_scan_results *res;
+
+ buf = os_malloc(WLC_IOCTL_MAXLEN);
+ if (buf == NULL)
+ return NULL;
+
+ wsr = (wl_scan_results_t *) buf;
+
+ wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr);
+ wsr->version = 107;
+ wsr->count = 0;
+
+ if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) {
+ os_free(buf);
+ return NULL;
+ }
+
+ res = os_zalloc(sizeof(*res));
+ if (res == NULL) {
+ os_free(buf);
+ return NULL;
+ }
+
+ res->res = os_zalloc(wsr->count * sizeof(struct wpa_scan_res *));
+ if (res->res == NULL) {
+ os_free(res);
+ os_free(buf);
+ return NULL;
+ }
+
+ for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) {
+ struct wpa_scan_res *r;
+ r = os_malloc(sizeof(*r) + wbi->ie_length);
+ if (r == NULL)
+ break;
+ res->res[res->num++] = r;
+
+ os_memcpy(r->bssid, &wbi->BSSID, ETH_ALEN);
+ r->freq = frequency_list[wbi->channel - 1];
+ /* get ie's */
+ os_memcpy(r + 1, wbi + 1, wbi->ie_length);
+ r->ie_len = wbi->ie_length;
+
+ wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length);
+ }
+
+ wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu "
+ "BSSes)",
+ wsr->buflen, (unsigned long) ap_num);
+
+ os_free(buf);
+ return res;
+ }
+
+static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ wlc_deauth_t wdt;
+ wdt.val = reason_code;
+ os_memcpy(&wdt.ea, addr, sizeof wdt.ea);
+ wdt.res = 0x7fff;
+ return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt,
+ sizeof(wdt));
+}
+
+static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ return broadcom_ioctl(drv, WLC_DISASSOC, NULL, 0);
+}
+
+static int
+wpa_driver_broadcom_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ wlc_ssid_t s;
+ int infra = 1;
+ int auth = 0;
+ int wsec = 4;
+ int dummy;
+ int wpa_auth;
+ int ret;
+
+ ret = wpa_driver_broadcom_set_drop_unencrypted(
+ drv, params->drop_unencrypted);
+
+ s.SSID_len = params->ssid_len;
+ os_memcpy(s.SSID, params->ssid, params->ssid_len);
+
+ switch (params->pairwise_suite) {
+ case CIPHER_WEP40:
+ case CIPHER_WEP104:
+ wsec = 1;
+ break;
+
+ case CIPHER_TKIP:
+ wsec = 2;
+ break;
+
+ case CIPHER_CCMP:
+ wsec = 4;
+ break;
+
+ default:
+ wsec = 0;
+ break;
+ }
+
+ switch (params->key_mgmt_suite) {
+ case KEY_MGMT_802_1X:
+ wpa_auth = 1;
+ break;
+
+ case KEY_MGMT_PSK:
+ wpa_auth = 2;
+ break;
+
+ default:
+ wpa_auth = 255;
+ break;
+ }
+
+ /* printf("broadcom_associate: %u %u %u\n", pairwise_suite,
+ * group_suite, key_mgmt_suite);
+ * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec));
+ * wl join uses wlc_sec_wep here, not wlc_set_wsec */
+
+ if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 ||
+ broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth,
+ sizeof(wpa_auth)) < 0 ||
+ broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 ||
+ broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 ||
+ broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 ||
+ broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 ||
+ broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0)
+ return -1;
+
+ return ret;
+}
+
+const struct wpa_driver_ops wpa_driver_broadcom_ops = {
+ .name = "broadcom",
+ .desc = "Broadcom wl.o driver",
+ .get_bssid = wpa_driver_broadcom_get_bssid,
+ .get_ssid = wpa_driver_broadcom_get_ssid,
+ .set_key = wpa_driver_broadcom_set_key,
+ .init = wpa_driver_broadcom_init,
+ .deinit = wpa_driver_broadcom_deinit,
+ .set_countermeasures = wpa_driver_broadcom_set_countermeasures,
+ .scan2 = wpa_driver_broadcom_scan,
+ .get_scan_results2 = wpa_driver_broadcom_get_scan_results,
+ .deauthenticate = wpa_driver_broadcom_deauthenticate,
+ .disassociate = wpa_driver_broadcom_disassociate,
+ .associate = wpa_driver_broadcom_associate,
+};
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
new file mode 100644
index 0000000..1b52865
--- /dev/null
+++ b/src/drivers/driver_bsd.c
@@ -0,0 +1,1573 @@
+/*
+ * WPA Supplicant - driver interaction with BSD net80211 layer
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, 2Wire, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#else
+#include <net/ethernet.h>
+#endif
+#include <net/route.h>
+
+#ifdef __DragonFly__
+#include <netproto/802_11/ieee80211_ioctl.h>
+#include <netproto/802_11/ieee80211_dragonfly.h>
+#else /* __DragonFly__ */
+#ifdef __GLIBC__
+#include <netinet/ether.h>
+#endif /* __GLIBC__ */
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_ioctl.h>
+#include <net80211/ieee80211_crypto.h>
+#endif /* __DragonFly__ || __GLIBC__ */
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <net80211/ieee80211_freebsd.h>
+#endif
+#if __NetBSD__
+#include <net80211/ieee80211_netbsd.h>
+#endif
+
+#include "l2_packet/l2_packet.h"
+
+struct bsd_driver_data {
+ struct hostapd_data *hapd; /* back pointer */
+
+ int sock; /* open socket for 802.11 ioctls */
+ struct l2_packet_data *sock_xmit;/* raw packet xmit socket */
+ int route; /* routing socket for events */
+ char ifname[IFNAMSIZ+1]; /* interface name */
+ unsigned int ifindex; /* interface index */
+ void *ctx;
+ struct wpa_driver_capa capa; /* driver capability */
+ int is_ap; /* Access point mode */
+ int prev_roaming; /* roaming state to restore on deinit */
+ int prev_privacy; /* privacy state to restore on deinit */
+ int prev_wpa; /* wpa state to restore on deinit */
+};
+
+/* Generic functions for hostapd and wpa_supplicant */
+
+static int
+bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ieee80211req ireq;
+
+ os_memset(&ireq, 0, sizeof(ireq));
+ os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name));
+ ireq.i_type = op;
+ ireq.i_val = val;
+ ireq.i_data = (void *) arg;
+ ireq.i_len = arg_len;
+
+ if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, "
+ "arg_len=%u]: %s", op, val, arg_len,
+ strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg,
+ int arg_len)
+{
+ struct bsd_driver_data *drv = priv;
+
+ os_memset(ireq, 0, sizeof(*ireq));
+ os_strlcpy(ireq->i_name, drv->ifname, sizeof(ireq->i_name));
+ ireq->i_type = op;
+ ireq->i_len = arg_len;
+ ireq->i_data = arg;
+
+ if (ioctl(drv->sock, SIOCG80211, ireq) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, "
+ "arg_len=%u]: %s", op, arg_len, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len)
+{
+ struct ieee80211req ireq;
+
+ if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0)
+ return -1;
+ return ireq.i_len;
+}
+
+static int
+set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len)
+{
+ return bsd_set80211(drv, op, 0, arg, arg_len);
+}
+
+static int
+set80211param(struct bsd_driver_data *drv, int op, int arg)
+{
+ return bsd_set80211(drv, op, arg, NULL, 0);
+}
+
+static int
+bsd_get_ssid(void *priv, u8 *ssid, int len)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef SIOCG80211NWID
+ struct ieee80211_nwid nwid;
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (void *)&nwid;
+ if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 ||
+ nwid.i_len > IEEE80211_NWID_LEN)
+ return -1;
+ os_memcpy(ssid, nwid.i_nwid, nwid.i_len);
+ return nwid.i_len;
+#else
+ return get80211var(drv, IEEE80211_IOC_SSID, ssid, IEEE80211_NWID_LEN);
+#endif
+}
+
+static int
+bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef SIOCS80211NWID
+ struct ieee80211_nwid nwid;
+ struct ifreq ifr;
+
+ os_memcpy(nwid.i_nwid, ssid, ssid_len);
+ nwid.i_len = ssid_len;
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (void *)&nwid;
+ return ioctl(drv->sock, SIOCS80211NWID, &ifr);
+#else
+ return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
+#endif
+}
+
+static int
+bsd_get_if_media(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ifmediareq ifmr;
+
+ os_memset(&ifmr, 0, sizeof(ifmr));
+ os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+ if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) {
+ wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ return ifmr.ifm_current;
+}
+
+static int
+bsd_set_if_media(void *priv, int media)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_media = media;
+
+ if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+bsd_set_mediaopt(void *priv, uint32_t mask, uint32_t mode)
+{
+ int media = bsd_get_if_media(priv);
+
+ if (media < 0)
+ return -1;
+ media &= ~mask;
+ media |= mode;
+ if (bsd_set_if_media(priv, media) < 0)
+ return -1;
+ return 0;
+}
+
+static int
+bsd_del_key(void *priv, const u8 *addr, int key_idx)
+{
+ struct ieee80211req_del_key wk;
+
+ os_memset(&wk, 0, sizeof(wk));
+ if (addr == NULL) {
+ wpa_printf(MSG_DEBUG, "%s: key_idx=%d", __func__, key_idx);
+ wk.idk_keyix = key_idx;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__,
+ MAC2STR(addr));
+ os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+ wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */
+ }
+
+ return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr)
+{
+ struct ieee80211req_mlme mlme;
+
+ os_memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = op;
+ mlme.im_reason = reason;
+ os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
+}
+
+static int
+bsd_ctrl_iface(void *priv, int enable)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+
+ if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) {
+ perror("ioctl[SIOCGIFFLAGS]");
+ return -1;
+ }
+
+ if (enable)
+ ifr.ifr_flags |= IFF_UP;
+ else
+ ifr.ifr_flags &= ~IFF_UP;
+
+ if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) {
+ perror("ioctl[SIOCSIFFLAGS]");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+ const unsigned char *addr, int key_idx, int set_tx, const u8 *seq,
+ size_t seq_len, const u8 *key, size_t key_len)
+{
+ struct ieee80211req_key wk;
+
+ wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d "
+ "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx,
+ set_tx, seq_len, key_len);
+
+ if (alg == WPA_ALG_NONE) {
+#ifndef HOSTAPD
+ if (addr == NULL || 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;
+ wk.ik_keylen = key_len;
+ if (seq)
+ os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+ os_memcpy(wk.ik_keydata, key, key_len);
+
+ return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
+{
+#ifndef IEEE80211_IOC_APPIE
+ static const char *ciphernames[] =
+ { "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" };
+ int v;
+
+ switch (params->wpa_group) {
+ case WPA_CIPHER_CCMP:
+ v = IEEE80211_CIPHER_AES_CCM;
+ break;
+ case WPA_CIPHER_TKIP:
+ v = IEEE80211_CIPHER_TKIP;
+ break;
+ case WPA_CIPHER_WEP104:
+ v = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_CIPHER_WEP40:
+ v = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_CIPHER_NONE:
+ v = IEEE80211_CIPHER_NONE;
+ break;
+ default:
+ printf("Unknown group key cipher %u\n",
+ params->wpa_group);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)",
+ __func__, ciphernames[v], v);
+ if (set80211param(priv, IEEE80211_IOC_MCASTCIPHER, v)) {
+ printf("Unable to set group key cipher to %u (%s)\n",
+ v, ciphernames[v]);
+ return -1;
+ }
+ if (v == IEEE80211_CIPHER_WEP) {
+ /* key length is done only for specific ciphers */
+ v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
+ if (set80211param(priv, IEEE80211_IOC_MCASTKEYLEN, v)) {
+ printf("Unable to set group key length to %u\n", v);
+ return -1;
+ }
+ }
+
+ v = 0;
+ if (params->wpa_pairwise & WPA_CIPHER_CCMP)
+ v |= 1<<IEEE80211_CIPHER_AES_CCM;
+ if (params->wpa_pairwise & WPA_CIPHER_TKIP)
+ v |= 1<<IEEE80211_CIPHER_TKIP;
+ if (params->wpa_pairwise & WPA_CIPHER_NONE)
+ v |= 1<<IEEE80211_CIPHER_NONE;
+ wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
+ if (set80211param(priv, IEEE80211_IOC_UCASTCIPHERS, v)) {
+ printf("Unable to set pairwise key ciphers to 0x%x\n", v);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
+ __func__, params->wpa_key_mgmt);
+ if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS,
+ params->wpa_key_mgmt)) {
+ printf("Unable to set key management algorithms to 0x%x\n",
+ params->wpa_key_mgmt);
+ return -1;
+ }
+
+ v = 0;
+ if (params->rsn_preauth)
+ v |= BIT(0);
+ wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
+ __func__, params->rsn_preauth);
+ if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) {
+ printf("Unable to set RSN capabilities to 0x%x\n", v);
+ return -1;
+ }
+#endif /* IEEE80211_IOC_APPIE */
+
+ wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa);
+ if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) {
+ printf("Unable to set WPA to %u\n", params->wpa);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
+
+ if (!params->enabled) {
+ /* XXX restore state */
+ return set80211param(priv, IEEE80211_IOC_AUTHMODE,
+ IEEE80211_AUTH_AUTO);
+ }
+ if (!params->wpa && !params->ieee802_1x) {
+ wpa_printf(MSG_ERROR, "%s: No 802.1X or WPA enabled",
+ __func__);
+ return -1;
+ }
+ if (params->wpa && bsd_configure_wpa(priv, params) != 0) {
+ wpa_printf(MSG_ERROR, "%s: Failed to configure WPA state",
+ __func__);
+ return -1;
+ }
+ if (set80211param(priv, IEEE80211_IOC_AUTHMODE,
+ (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+ wpa_printf(MSG_ERROR, "%s: Failed to enable WPA/802.1X",
+ __func__);
+ return -1;
+ }
+ return bsd_ctrl_iface(priv, 1);
+}
+
+static int
+bsd_set_sta_authorized(void *priv, const u8 *addr,
+ int total_flags, int flags_or, int flags_and)
+{
+ int authorized = -1;
+
+ /* For now, only support setting Authorized flag */
+ if (flags_or & WPA_STA_AUTHORIZED)
+ authorized = 1;
+ if (!(flags_and & WPA_STA_AUTHORIZED))
+ authorized = 0;
+
+ if (authorized < 0)
+ return 0;
+
+ return bsd_send_mlme_param(priv, authorized ?
+ IEEE80211_MLME_AUTHORIZE :
+ IEEE80211_MLME_UNAUTHORIZE, 0, addr);
+}
+
+static void
+bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
+{
+ struct ieee80211req_wpaie ie;
+ int ielen = 0;
+ u8 *iebuf = NULL;
+
+ /*
+ * Fetch and validate any negotiated WPA/RSN parameters.
+ */
+ memset(&ie, 0, sizeof(ie));
+ memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
+ if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) {
+ printf("Failed to get WPA/RSN information element.\n");
+ goto no_ie;
+ }
+ iebuf = ie.wpa_ie;
+ ielen = ie.wpa_ie[1];
+ if (ielen == 0)
+ iebuf = NULL;
+ else
+ ielen += 2;
+
+no_ie:
+ drv_event_assoc(ctx, addr, iebuf, ielen, 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_disassociate(void *priv, const u8 *addr, int reason_code)
+{
+ return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
+ addr);
+}
+
+static int
+wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg)
+{
+ int authmode;
+
+ if ((auth_alg & WPA_AUTH_ALG_OPEN) &&
+ (auth_alg & WPA_AUTH_ALG_SHARED))
+ authmode = IEEE80211_AUTH_AUTO;
+ else if (auth_alg & WPA_AUTH_ALG_SHARED)
+ authmode = IEEE80211_AUTH_SHARED;
+ else
+ authmode = IEEE80211_AUTH_OPEN;
+
+ return set80211param(priv, IEEE80211_IOC_AUTHMODE, authmode);
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+ struct bsd_driver_data *drv = ctx;
+
+ drv_event_eapol_rx(drv->ctx, src_addr, buf, len);
+}
+
+static int
+wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+ u32 mode;
+ 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;
+
+ pos = (u8 *)(result + 1);
+
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = sr->isr_ssid_len;
+ os_memcpy(pos, sr + 1, sr->isr_ssid_len);
+ pos += sr->isr_ssid_len;
+
+ /*
+ * Deal all rates as supported rate.
+ * Because net80211 doesn't report extended supported rate or not.
+ */
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = sr->isr_nrates;
+ os_memcpy(pos, sr->isr_rates, sr->isr_nrates);
+ pos += sr->isr_nrates;
+
+ *pos++ = WLAN_EID_ERP_INFO;
+ *pos++ = 1;
+ *pos++ = sr->isr_erp;
+
+ os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
+ pos += sr->isr_ie_len;
+
+ result->ie_len = pos - (u8 *)(result + 1);
+
+ tmp = os_realloc(res->res,
+ (res->num + 1) * sizeof(struct wpa_scan_res *));
+ if (tmp == NULL) {
+ os_free(result);
+ return;
+ }
+ tmp[res->num++] = result;
+ res->res = tmp;
+}
+
+struct wpa_scan_results *
+wpa_driver_bsd_get_scan_results2(void *priv)
+{
+ struct ieee80211req_scan_result *sr;
+ struct wpa_scan_results *res;
+ int len, rest;
+ uint8_t buf[24*1024], *pos;
+
+ len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024);
+ if (len < 0)
+ return NULL;
+
+ res = os_zalloc(sizeof(*res));
+ if (res == NULL)
+ return NULL;
+
+ pos = buf;
+ rest = len;
+ while (rest >= sizeof(struct ieee80211req_scan_result)) {
+ sr = (struct ieee80211req_scan_result *)pos;
+ wpa_driver_bsd_add_scan_entry(res, sr);
+ pos += sr->isr_len;
+ rest -= sr->isr_len;
+ }
+
+ wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)",
+ len, (unsigned long)res->num);
+
+ return res;
+}
+
+static int wpa_driver_bsd_capa(struct bsd_driver_data *drv)
+{
+#ifdef IEEE80211_IOC_DEVCAPS
+/* kernel definitions copied from net80211/ieee80211_var.h */
+#define IEEE80211_CIPHER_WEP 0
+#define IEEE80211_CIPHER_TKIP 1
+#define IEEE80211_CIPHER_AES_CCM 3
+#define IEEE80211_CRYPTO_WEP (1<<IEEE80211_CIPHER_WEP)
+#define IEEE80211_CRYPTO_TKIP (1<<IEEE80211_CIPHER_TKIP)
+#define IEEE80211_CRYPTO_AES_CCM (1<<IEEE80211_CIPHER_AES_CCM)
+#define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */
+#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */
+#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */
+ struct ieee80211_devcaps_req devcaps;
+
+ if (get80211var(drv, IEEE80211_IOC_DEVCAPS, &devcaps,
+ sizeof(devcaps)) < 0) {
+ wpa_printf(MSG_ERROR, "failed to IEEE80211_IOC_DEVCAPS: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: drivercaps=0x%08x,cryptocaps=0x%08x",
+ __func__, devcaps.dc_drivercaps, devcaps.dc_cryptocaps);
+
+ if (devcaps.dc_drivercaps & IEEE80211_C_WPA1)
+ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+ if (devcaps.dc_drivercaps & IEEE80211_C_WPA2)
+ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+
+ if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_WEP)
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+ WPA_DRIVER_CAPA_ENC_WEP104;
+ if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_TKIP)
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+ if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_AES_CCM)
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+
+ if (devcaps.dc_drivercaps & IEEE80211_C_HOSTAP)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+#undef IEEE80211_CIPHER_WEP
+#undef IEEE80211_CIPHER_TKIP
+#undef IEEE80211_CIPHER_AES_CCM
+#undef IEEE80211_CRYPTO_WEP
+#undef IEEE80211_CRYPTO_TKIP
+#undef IEEE80211_CRYPTO_AES_CCM
+#undef IEEE80211_C_HOSTAP
+#undef IEEE80211_C_WPA1
+#undef IEEE80211_C_WPA2
+#else /* IEEE80211_IOC_DEVCAPS */
+ /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
+ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 |
+ WPA_DRIVER_CAPA_ENC_WEP104 |
+ WPA_DRIVER_CAPA_ENC_TKIP |
+ WPA_DRIVER_CAPA_ENC_CCMP;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+#endif /* IEEE80211_IOC_DEVCAPS */
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+ drv->capa.max_scan_ssids = IEEE80211_IOC_SCAN_MAX_SSID;
+#else /* IEEE80211_IOC_SCAN_MAX_SSID */
+ drv->capa.max_scan_ssids = 1;
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+ drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+ WPA_DRIVER_AUTH_SHARED |
+ WPA_DRIVER_AUTH_LEAP;
+ return 0;
+}
+
+static void *
+wpa_driver_bsd_init(void *ctx, const char *ifname)
+{
+#define GETPARAM(drv, param, v) \
+ (((v) = get80211param(drv, param)) != -1)
+ struct bsd_driver_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ /*
+ * NB: We require the interface name be mappable to an index.
+ * This implies we do not support having wpa_supplicant
+ * wait for an interface to appear. This seems ok; that
+ * doesn't belong here; it's really the job of devd.
+ */
+ drv->ifindex = if_nametoindex(ifname);
+ if (drv->ifindex == 0) {
+ wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+ __func__, ifname);
+ goto fail1;
+ }
+ drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->sock < 0)
+ goto fail1;
+ drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (drv->route < 0)
+ goto fail;
+ eloop_register_read_sock(drv->route,
+ wpa_driver_bsd_event_receive, ctx, drv);
+
+ drv->ctx = ctx;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+ /* Down interface during setup. */
+ if (bsd_ctrl_iface(drv, 0) < 0)
+ goto fail;
+
+ if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+
+ if (wpa_driver_bsd_capa(drv))
+ goto fail;
+
+ return drv;
+fail:
+ close(drv->sock);
+fail1:
+ os_free(drv);
+ return NULL;
+#undef GETPARAM
+}
+
+static void
+wpa_driver_bsd_deinit(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+
+ wpa_driver_bsd_set_wpa(drv, 0);
+ eloop_unregister_read_sock(drv->route);
+
+ /* NB: mark interface down */
+ bsd_ctrl_iface(drv, 0);
+
+ wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy);
+ if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0)
+ wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state",
+ __func__);
+
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+ (void) close(drv->route); /* ioctl socket */
+ (void) close(drv->sock); /* event socket */
+ os_free(drv);
+}
+
+static int
+wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ struct bsd_driver_data *drv = priv;
+
+ os_memcpy(capa, &drv->capa, sizeof(*capa));
+ return 0;
+}
+#endif /* HOSTAPD */
+
+
+const struct wpa_driver_ops wpa_driver_bsd_ops = {
+ .name = "bsd",
+ .desc = "BSD 802.11 support",
+#ifdef HOSTAPD
+ .hapd_init = bsd_init,
+ .hapd_deinit = bsd_deinit,
+ .set_privacy = bsd_set_privacy,
+ .get_seqnum = bsd_get_seqnum,
+ .flush = bsd_flush,
+ .read_sta_data = bsd_read_sta_driver_data,
+ .sta_disassoc = bsd_sta_disassoc,
+ .sta_deauth = bsd_sta_deauth,
+#else /* HOSTAPD */
+ .init = wpa_driver_bsd_init,
+ .deinit = wpa_driver_bsd_deinit,
+ .get_bssid = wpa_driver_bsd_get_bssid,
+ .get_ssid = wpa_driver_bsd_get_ssid,
+ .set_countermeasures = wpa_driver_bsd_set_countermeasures,
+ .scan2 = wpa_driver_bsd_scan,
+ .get_scan_results2 = wpa_driver_bsd_get_scan_results2,
+ .deauthenticate = wpa_driver_bsd_deauthenticate,
+ .disassociate = wpa_driver_bsd_disassociate,
+ .associate = wpa_driver_bsd_associate,
+ .get_capa = wpa_driver_bsd_get_capa,
+#endif /* HOSTAPD */
+ .set_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,
+ .sta_set_flags = bsd_set_sta_authorized,
+ .set_generic_elem = bsd_set_opt_ie,
+};
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
new file mode 100644
index 0000000..e855c1b
--- /dev/null
+++ b/src/drivers/driver_hostap.c
@@ -0,0 +1,1648 @@
+/*
+ * Driver interaction with Linux Host AP driver
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include "wireless_copy.h"
+#include "common.h"
+#include "driver.h"
+#include "driver_wext.h"
+#include "eloop.h"
+#include "driver_hostap.h"
+
+
+#ifdef HOSTAPD
+
+#include <net/if_arp.h>
+#include <netpacket/packet.h>
+
+#include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "common/ieee802_11_defs.h"
+
+
+/* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X
+ * frames that might be longer than normal default MTU and they are not
+ * fragmented */
+#define HOSTAPD_MTU 2290
+
+static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+struct hostap_driver_data {
+ struct hostapd_data *hapd;
+
+ char iface[IFNAMSIZ + 1];
+ int sock; /* raw packet socket for driver access */
+ int ioctl_sock; /* socket for ioctl() use */
+ struct netlink_data *netlink;
+
+ int we_version;
+
+ u8 *generic_ie;
+ size_t generic_ie_len;
+ u8 *wps_ie;
+ size_t wps_ie_len;
+};
+
+
+static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param,
+ int len);
+static int hostap_set_iface_flags(void *priv, int dev_up);
+
+static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len,
+ u16 stype)
+{
+ struct ieee80211_hdr *hdr;
+ u16 fc, ethertype;
+ u8 *pos, *sa;
+ size_t left;
+ union wpa_event_data event;
+
+ if (len < sizeof(struct ieee80211_hdr))
+ return;
+
+ hdr = (struct ieee80211_hdr *) buf;
+ fc = le_to_host16(hdr->frame_control);
+
+ if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) {
+ printf("Not ToDS data frame (fc=0x%04x)\n", fc);
+ return;
+ }
+
+ sa = hdr->addr2;
+ os_memset(&event, 0, sizeof(event));
+ event.rx_from_unknown.frame = buf;
+ event.rx_from_unknown.len = len;
+ wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event);
+
+ pos = (u8 *) (hdr + 1);
+ left = len - sizeof(*hdr);
+
+ if (left < sizeof(rfc1042_header)) {
+ printf("Too short data frame\n");
+ return;
+ }
+
+ if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) {
+ printf("Data frame with no RFC1042 header\n");
+ return;
+ }
+ pos += sizeof(rfc1042_header);
+ left -= sizeof(rfc1042_header);
+
+ if (left < 2) {
+ printf("No ethertype in data frame\n");
+ return;
+ }
+
+ ethertype = WPA_GET_BE16(pos);
+ pos += 2;
+ left -= 2;
+ switch (ethertype) {
+ case ETH_P_PAE:
+ drv_event_eapol_rx(drv->hapd, sa, pos, left);
+ break;
+
+ default:
+ printf("Unknown ethertype 0x%04x in data frame\n", ethertype);
+ break;
+ }
+}
+
+
+static void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf,
+ size_t len, int ok)
+{
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+ union wpa_event_data event;
+
+ hdr = (struct ieee80211_hdr *) buf;
+ fc = le_to_host16(hdr->frame_control);
+
+ os_memset(&event, 0, sizeof(event));
+ event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+ event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+ event.tx_status.dst = hdr->addr1;
+ event.tx_status.data = buf;
+ event.tx_status.data_len = len;
+ event.tx_status.ack = ok;
+ wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event);
+}
+
+
+static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len)
+{
+ struct ieee80211_hdr *hdr;
+ u16 fc, extra_len, type, stype;
+ unsigned char *extra = NULL;
+ size_t data_len = len;
+ int ver;
+ union wpa_event_data event;
+
+ /* PSPOLL is only 16 bytes, but driver does not (at least yet) pass
+ * these to user space */
+ if (len < 24) {
+ wpa_printf(MSG_MSGDUMP, "handle_frame: too short (%lu)",
+ (unsigned long) len);
+ return;
+ }
+
+ hdr = (struct ieee80211_hdr *) buf;
+ fc = le_to_host16(hdr->frame_control);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ if (type != WLAN_FC_TYPE_MGMT || stype != WLAN_FC_STYPE_BEACON) {
+ wpa_hexdump(MSG_MSGDUMP, "Received management frame",
+ buf, len);
+ }
+
+ ver = fc & WLAN_FC_PVER;
+
+ /* protocol version 3 is reserved for indicating extra data after the
+ * payload, version 2 for indicating ACKed frame (TX callbacks), and
+ * version 1 for indicating failed frame (no ACK, TX callbacks) */
+ if (ver == 3) {
+ u8 *pos = buf + len - 2;
+ extra_len = WPA_GET_LE16(pos);
+ printf("extra data in frame (elen=%d)\n", extra_len);
+ if ((size_t) extra_len + 2 > len) {
+ printf(" extra data overflow\n");
+ return;
+ }
+ len -= extra_len + 2;
+ extra = buf + len;
+ } else if (ver == 1 || ver == 2) {
+ handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0);
+ return;
+ } else if (ver != 0) {
+ printf("unknown protocol version %d\n", ver);
+ return;
+ }
+
+ switch (type) {
+ case WLAN_FC_TYPE_MGMT:
+ os_memset(&event, 0, sizeof(event));
+ event.rx_mgmt.frame = buf;
+ event.rx_mgmt.frame_len = data_len;
+ wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
+ break;
+ case WLAN_FC_TYPE_CTRL:
+ wpa_printf(MSG_DEBUG, "CTRL");
+ break;
+ case WLAN_FC_TYPE_DATA:
+ wpa_printf(MSG_DEBUG, "DATA");
+ handle_data(drv, buf, data_len, stype);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "unknown frame type %d", type);
+ break;
+ }
+}
+
+
+static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct hostap_driver_data *drv = eloop_ctx;
+ int len;
+ unsigned char buf[3000];
+
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ perror("recv");
+ return;
+ }
+
+ handle_frame(drv, buf, len);
+}
+
+
+static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr)
+{
+ struct ifreq ifr;
+ struct sockaddr_ll addr;
+
+ drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (drv->sock < 0) {
+ perror("socket[PF_PACKET,SOCK_RAW]");
+ return -1;
+ }
+
+ if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) {
+ printf("Could not register read socket\n");
+ return -1;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface);
+ if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
+ perror("ioctl(SIOCGIFINDEX)");
+ return -1;
+ }
+
+ if (hostap_set_iface_flags(drv, 1)) {
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sll_family = AF_PACKET;
+ addr.sll_ifindex = ifr.ifr_ifindex;
+ wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
+ addr.sll_ifindex);
+
+ if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind");
+ return -1;
+ }
+
+ return linux_get_ifhwaddr(drv->sock, drv->iface, own_addr);
+}
+
+
+static int hostap_send_mlme(void *priv, const u8 *msg, size_t len)
+{
+ struct hostap_driver_data *drv = priv;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg;
+ int res;
+
+ /* Request TX callback */
+ hdr->frame_control |= host_to_le16(BIT(1));
+ res = send(drv->sock, msg, len, 0);
+ hdr->frame_control &= ~host_to_le16(BIT(1));
+
+ return res;
+}
+
+
+static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data,
+ size_t data_len, int encrypt, const u8 *own_addr,
+ 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);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - "
+ "failed: %d (%s)",
+ (unsigned long) len, errno, strerror(errno));
+ }
+ free(hdr);
+
+ return res;
+}
+
+
+static int hostap_sta_set_flags(void *priv, const u8 *addr,
+ int total_flags, int flags_or, int flags_and)
+{
+ struct hostap_driver_data *drv = priv;
+ struct prism2_hostapd_param param;
+
+ if (flags_or & WPA_STA_AUTHORIZED)
+ flags_or = BIT(5); /* WLAN_STA_AUTHORIZED */
+ if (!(flags_and & WPA_STA_AUTHORIZED))
+ flags_and = ~BIT(5);
+ else
+ flags_and = ~0;
+ memset(&param, 0, sizeof(param));
+ param.cmd = PRISM2_HOSTAPD_SET_FLAGS_STA;
+ memcpy(param.sta_addr, addr, ETH_ALEN);
+ param.u.set_flags_sta.flags_or = flags_or;
+ param.u.set_flags_sta.flags_and = flags_and;
+ return hostapd_ioctl(drv, &param, sizeof(param));
+}
+
+
+static int hostap_set_iface_flags(void *priv, int dev_up)
+{
+ struct hostap_driver_data *drv = priv;
+ struct ifreq ifr;
+ char ifname[IFNAMSIZ];
+
+ os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface);
+ if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0)
+ return -1;
+
+ if (dev_up) {
+ memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ ifr.ifr_mtu = HOSTAPD_MTU;
+ if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) {
+ perror("ioctl[SIOCSIFMTU]");
+ printf("Setting MTU failed - trying to survive with "
+ "current value\n");
+ }
+ }
+
+ return 0;
+}
+
+
+static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param,
+ int len)
+{
+ struct hostap_driver_data *drv = priv;
+ struct iwreq iwr;
+
+ memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+ iwr.u.data.pointer = (caddr_t) param;
+ iwr.u.data.length = len;
+
+ if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
+ perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_hostap_set_key(const char *ifname, void *priv,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct hostap_driver_data *drv = priv;
+ struct prism2_hostapd_param *param;
+ u8 *buf;
+ size_t blen;
+ int ret = 0;
+
+ blen = sizeof(*param) + key_len;
+ buf = os_zalloc(blen);
+ if (buf == NULL)
+ return -1;
+
+ param = (struct prism2_hostapd_param *) buf;
+ param->cmd = PRISM2_SET_ENCRYPTION;
+ if (addr == NULL)
+ memset(param->sta_addr, 0xff, ETH_ALEN);
+ else
+ memcpy(param->sta_addr, addr, ETH_ALEN);
+ switch (alg) {
+ case WPA_ALG_NONE:
+ os_strlcpy((char *) param->u.crypt.alg, "NONE",
+ HOSTAP_CRYPT_ALG_NAME_LEN);
+ break;
+ case WPA_ALG_WEP:
+ os_strlcpy((char *) param->u.crypt.alg, "WEP",
+ HOSTAP_CRYPT_ALG_NAME_LEN);
+ break;
+ case WPA_ALG_TKIP:
+ os_strlcpy((char *) param->u.crypt.alg, "TKIP",
+ HOSTAP_CRYPT_ALG_NAME_LEN);
+ break;
+ case WPA_ALG_CCMP:
+ os_strlcpy((char *) param->u.crypt.alg, "CCMP",
+ HOSTAP_CRYPT_ALG_NAME_LEN);
+ break;
+ default:
+ os_free(buf);
+ return -1;
+ }
+ param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0;
+ param->u.crypt.idx = key_idx;
+ param->u.crypt.key_len = key_len;
+ memcpy((u8 *) (param + 1), key, key_len);
+
+ if (hostapd_ioctl(drv, param, blen)) {
+ printf("Failed to set encryption.\n");
+ ret = -1;
+ }
+ free(buf);
+
+ return ret;
+}
+
+
+static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr,
+ int idx, u8 *seq)
+{
+ struct hostap_driver_data *drv = priv;
+ struct prism2_hostapd_param *param;
+ u8 *buf;
+ size_t blen;
+ int ret = 0;
+
+ blen = sizeof(*param) + 32;
+ buf = os_zalloc(blen);
+ if (buf == NULL)
+ return -1;
+
+ param = (struct prism2_hostapd_param *) buf;
+ param->cmd = PRISM2_GET_ENCRYPTION;
+ if (addr == NULL)
+ memset(param->sta_addr, 0xff, ETH_ALEN);
+ else
+ memcpy(param->sta_addr, addr, ETH_ALEN);
+ param->u.crypt.idx = idx;
+
+ if (hostapd_ioctl(drv, param, blen)) {
+ printf("Failed to get encryption.\n");
+ ret = -1;
+ } else {
+ memcpy(seq, param->u.crypt.seq, 8);
+ }
+ free(buf);
+
+ return ret;
+}
+
+
+static int hostap_ioctl_prism2param(void *priv, int param, int value)
+{
+ struct hostap_driver_data *drv = priv;
+ struct iwreq iwr;
+ int *i;
+
+ memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+ i = (int *) iwr.u.name;
+ *i++ = param;
+ *i++ = value;
+
+ if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) {
+ perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int hostap_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+ struct hostap_driver_data *drv = priv;
+ int enabled = params->enabled;
+
+ /* enable kernel driver support for IEEE 802.1X */
+ if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) {
+ printf("Could not setup IEEE 802.1X support in kernel driver."
+ "\n");
+ return -1;
+ }
+
+ if (!enabled)
+ return 0;
+
+ /* use host driver implementation of encryption to allow
+ * individual keys and passing plaintext EAPOL frames */
+ if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) ||
+ hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) {
+ printf("Could not setup host-based encryption in kernel "
+ "driver.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int hostap_set_privacy(void *priv, int enabled)
+{
+ struct hostap_drvier_data *drv = priv;
+
+ return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED,
+ enabled);
+}
+
+
+static int hostap_set_ssid(void *priv, const u8 *buf, int len)
+{
+ struct hostap_driver_data *drv = priv;
+ struct iwreq iwr;
+
+ memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+ iwr.u.essid.flags = 1; /* SSID active */
+ iwr.u.essid.pointer = (caddr_t) buf;
+ iwr.u.essid.length = len + 1;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+ perror("ioctl[SIOCSIWESSID]");
+ printf("len=%d\n", len);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int hostap_flush(void *priv)
+{
+ struct hostap_driver_data *drv = priv;
+ struct prism2_hostapd_param param;
+
+ memset(&param, 0, sizeof(param));
+ param.cmd = PRISM2_HOSTAPD_FLUSH;
+ return hostapd_ioctl(drv, &param, sizeof(param));
+}
+
+
+static int hostap_read_sta_data(void *priv,
+ struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ struct hostap_driver_data *drv = priv;
+ char buf[1024], line[128], *pos;
+ FILE *f;
+ unsigned long val;
+
+ memset(data, 0, sizeof(*data));
+ snprintf(buf, sizeof(buf), "/proc/net/hostap/%s/" MACSTR,
+ drv->iface, MAC2STR(addr));
+
+ f = fopen(buf, "r");
+ if (!f)
+ return -1;
+ /* Need to read proc file with in one piece, so use large enough
+ * buffer. */
+ setbuffer(f, buf, sizeof(buf));
+
+ while (fgets(line, sizeof(line), f)) {
+ pos = strchr(line, '=');
+ if (!pos)
+ continue;
+ *pos++ = '\0';
+ val = strtoul(pos, NULL, 10);
+ if (strcmp(line, "rx_packets") == 0)
+ data->rx_packets = val;
+ else if (strcmp(line, "tx_packets") == 0)
+ data->tx_packets = val;
+ else if (strcmp(line, "rx_bytes") == 0)
+ data->rx_bytes = val;
+ else if (strcmp(line, "tx_bytes") == 0)
+ data->tx_bytes = val;
+ }
+
+ fclose(f);
+
+ return 0;
+}
+
+
+static int hostap_sta_add(void *priv, struct hostapd_sta_add_params *params)
+{
+ struct hostap_driver_data *drv = priv;
+ struct prism2_hostapd_param param;
+ int tx_supp_rates = 0;
+ size_t i;
+
+#define WLAN_RATE_1M BIT(0)
+#define WLAN_RATE_2M BIT(1)
+#define WLAN_RATE_5M5 BIT(2)
+#define WLAN_RATE_11M BIT(3)
+
+ for (i = 0; i < params->supp_rates_len; i++) {
+ if ((params->supp_rates[i] & 0x7f) == 2)
+ tx_supp_rates |= WLAN_RATE_1M;
+ if ((params->supp_rates[i] & 0x7f) == 4)
+ tx_supp_rates |= WLAN_RATE_2M;
+ if ((params->supp_rates[i] & 0x7f) == 11)
+ tx_supp_rates |= WLAN_RATE_5M5;
+ if ((params->supp_rates[i] & 0x7f) == 22)
+ tx_supp_rates |= WLAN_RATE_11M;
+ }
+
+ memset(&param, 0, sizeof(param));
+ param.cmd = PRISM2_HOSTAPD_ADD_STA;
+ memcpy(param.sta_addr, params->addr, ETH_ALEN);
+ param.u.add_sta.aid = params->aid;
+ param.u.add_sta.capability = params->capability;
+ param.u.add_sta.tx_supp_rates = tx_supp_rates;
+ return hostapd_ioctl(drv, &param, sizeof(param));
+}
+
+
+static int hostap_sta_remove(void *priv, const u8 *addr)
+{
+ struct hostap_driver_data *drv = priv;
+ struct prism2_hostapd_param param;
+
+ hostap_sta_set_flags(drv, addr, 0, 0, ~WPA_STA_AUTHORIZED);
+
+ memset(&param, 0, sizeof(param));
+ param.cmd = PRISM2_HOSTAPD_REMOVE_STA;
+ memcpy(param.sta_addr, addr, ETH_ALEN);
+ if (hostapd_ioctl(drv, &param, sizeof(param))) {
+ printf("Could not remove station from kernel driver.\n");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int hostap_get_inact_sec(void *priv, const u8 *addr)
+{
+ struct hostap_driver_data *drv = priv;
+ struct prism2_hostapd_param param;
+
+ memset(&param, 0, sizeof(param));
+ param.cmd = PRISM2_HOSTAPD_GET_INFO_STA;
+ memcpy(param.sta_addr, addr, ETH_ALEN);
+ if (hostapd_ioctl(drv, &param, sizeof(param))) {
+ return -1;
+ }
+
+ return param.u.get_info_sta.inactive_sec;
+}
+
+
+static int hostap_sta_clear_stats(void *priv, const u8 *addr)
+{
+ struct hostap_driver_data *drv = priv;
+ struct prism2_hostapd_param param;
+
+ memset(&param, 0, sizeof(param));
+ param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS;
+ memcpy(param.sta_addr, addr, ETH_ALEN);
+ if (hostapd_ioctl(drv, &param, sizeof(param))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv)
+{
+ struct prism2_hostapd_param *param;
+ int res;
+ size_t blen, elem_len;
+
+ elem_len = drv->generic_ie_len + drv->wps_ie_len;
+ blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len;
+ if (blen < sizeof(*param))
+ blen = sizeof(*param);
+
+ param = os_zalloc(blen);
+ if (param == NULL)
+ return -1;
+
+ param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT;
+ param->u.generic_elem.len = elem_len;
+ if (drv->generic_ie) {
+ os_memcpy(param->u.generic_elem.data, drv->generic_ie,
+ drv->generic_ie_len);
+ }
+ if (drv->wps_ie) {
+ os_memcpy(&param->u.generic_elem.data[drv->generic_ie_len],
+ drv->wps_ie, drv->wps_ie_len);
+ }
+ wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE",
+ param->u.generic_elem.data, elem_len);
+ res = hostapd_ioctl(drv, param, blen);
+
+ os_free(param);
+
+ return res;
+}
+
+
+static int hostap_set_generic_elem(void *priv,
+ const u8 *elem, size_t elem_len)
+{
+ struct hostap_driver_data *drv = priv;
+
+ os_free(drv->generic_ie);
+ drv->generic_ie = NULL;
+ drv->generic_ie_len = 0;
+ if (elem) {
+ drv->generic_ie = os_malloc(elem_len);
+ if (drv->generic_ie == NULL)
+ return -1;
+ os_memcpy(drv->generic_ie, elem, elem_len);
+ drv->generic_ie_len = elem_len;
+ }
+
+ return hostapd_ioctl_set_generic_elem(drv);
+}
+
+
+static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
+ const struct wpabuf *proberesp,
+ 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));
+}
+
+
+static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason)
+{
+ struct hostap_driver_data *drv = priv;
+ struct ieee80211_mgmt mgmt;
+
+ memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_DISASSOC);
+ memcpy(mgmt.da, addr, ETH_ALEN);
+ memcpy(mgmt.sa, own_addr, ETH_ALEN);
+ memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+ mgmt.u.disassoc.reason_code = host_to_le16(reason);
+ return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
+ sizeof(mgmt.u.disassoc));
+}
+
+
+static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
+ u16 *num_modes,
+ u16 *flags)
+{
+ struct hostapd_hw_modes *mode;
+ int i, clen, rlen;
+ const short chan2freq[14] = {
+ 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+ 2447, 2452, 2457, 2462, 2467, 2472, 2484
+ };
+
+ mode = os_zalloc(sizeof(struct hostapd_hw_modes));
+ if (mode == NULL)
+ return NULL;
+
+ *num_modes = 1;
+ *flags = 0;
+
+ mode->mode = HOSTAPD_MODE_IEEE80211B;
+ mode->num_channels = 14;
+ mode->num_rates = 4;
+
+ clen = mode->num_channels * sizeof(struct hostapd_channel_data);
+ rlen = mode->num_rates * sizeof(int);
+
+ mode->channels = os_zalloc(clen);
+ mode->rates = os_zalloc(rlen);
+ if (mode->channels == NULL || mode->rates == NULL) {
+ os_free(mode->channels);
+ os_free(mode->rates);
+ os_free(mode);
+ return NULL;
+ }
+
+ for (i = 0; i < 14; i++) {
+ mode->channels[i].chan = i + 1;
+ mode->channels[i].freq = chan2freq[i];
+ /* TODO: Get allowed channel list from the driver */
+ if (i >= 11)
+ mode->channels[i].flag = HOSTAPD_CHAN_DISABLED;
+ }
+
+ mode->rates[0] = 10;
+ mode->rates[1] = 20;
+ mode->rates[2] = 55;
+ mode->rates[3] = 110;
+
+ return mode;
+}
+
+#else /* HOSTAPD */
+
+struct wpa_driver_hostap_data {
+ void *wext; /* private data for driver_wext */
+ void *ctx;
+ char ifname[IFNAMSIZ + 1];
+ int sock;
+ int current_mode; /* infra/adhoc */
+};
+
+
+static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg);
+
+
+static int hostapd_ioctl(struct wpa_driver_hostap_data *drv,
+ struct prism2_hostapd_param *param,
+ int len, int show_err)
+{
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.pointer = (caddr_t) param;
+ iwr.u.data.length = len;
+
+ if (ioctl(drv->sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
+ int ret = errno;
+ if (show_err)
+ perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_hostap_set_wpa_ie(struct wpa_driver_hostap_data *drv,
+ const u8 *wpa_ie, size_t wpa_ie_len)
+{
+ struct prism2_hostapd_param *param;
+ int res;
+ size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len;
+ if (blen < sizeof(*param))
+ blen = sizeof(*param);
+
+ param = os_zalloc(blen);
+ if (param == NULL)
+ return -1;
+
+ param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT;
+ param->u.generic_elem.len = wpa_ie_len;
+ os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len);
+ res = hostapd_ioctl(drv, param, blen, 1);
+
+ os_free(param);
+
+ return res;
+}
+
+
+static int prism2param(struct wpa_driver_hostap_data *drv, int param,
+ int value)
+{
+ struct iwreq iwr;
+ int *i, ret = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ i = (int *) iwr.u.name;
+ *i++ = param;
+ *i++ = value;
+
+ if (ioctl(drv->sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) {
+ perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]");
+ ret = -1;
+ }
+ return ret;
+}
+
+
+static int wpa_driver_hostap_set_wpa(void *priv, int enabled)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+
+ if (!enabled && wpa_driver_hostap_set_wpa_ie(drv, NULL, 0) < 0)
+ ret = -1;
+ if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, enabled ? 2 : 0) < 0)
+ ret = -1;
+ if (prism2param(drv, PRISM2_PARAM_WPA, enabled) < 0)
+ ret = -1;
+
+ return ret;
+}
+
+
+static void show_set_key_error(struct prism2_hostapd_param *param)
+{
+ switch (param->u.crypt.err) {
+ case HOSTAP_CRYPT_ERR_UNKNOWN_ALG:
+ wpa_printf(MSG_INFO, "Unknown algorithm '%s'.",
+ param->u.crypt.alg);
+ wpa_printf(MSG_INFO, "You may need to load kernel module to "
+ "register that algorithm.");
+ wpa_printf(MSG_INFO, "E.g., 'modprobe hostap_crypt_wep' for "
+ "WEP.");
+ break;
+ case HOSTAP_CRYPT_ERR_UNKNOWN_ADDR:
+ wpa_printf(MSG_INFO, "Unknown address " MACSTR ".",
+ MAC2STR(param->sta_addr));
+ break;
+ case HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED:
+ wpa_printf(MSG_INFO, "Crypt algorithm initialization failed.");
+ break;
+ case HOSTAP_CRYPT_ERR_KEY_SET_FAILED:
+ wpa_printf(MSG_INFO, "Key setting failed.");
+ break;
+ case HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED:
+ wpa_printf(MSG_INFO, "TX key index setting failed.");
+ break;
+ case HOSTAP_CRYPT_ERR_CARD_CONF_FAILED:
+ wpa_printf(MSG_INFO, "Card configuration failed.");
+ break;
+ }
+}
+
+
+static int wpa_driver_hostap_set_key(const char *ifname, void *priv,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ struct prism2_hostapd_param *param;
+ u8 *buf;
+ size_t blen;
+ int ret = 0;
+ char *alg_name;
+
+ switch (alg) {
+ case WPA_ALG_NONE:
+ alg_name = "none";
+ break;
+ case WPA_ALG_WEP:
+ alg_name = "WEP";
+ break;
+ case WPA_ALG_TKIP:
+ alg_name = "TKIP";
+ break;
+ case WPA_ALG_CCMP:
+ alg_name = "CCMP";
+ break;
+ default:
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu "
+ "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx,
+ (unsigned long) seq_len, (unsigned long) key_len);
+
+ if (seq_len > 8)
+ return -2;
+
+ blen = sizeof(*param) + key_len;
+ buf = os_zalloc(blen);
+ if (buf == NULL)
+ return -1;
+
+ param = (struct prism2_hostapd_param *) buf;
+ param->cmd = PRISM2_SET_ENCRYPTION;
+ /* TODO: In theory, STA in client mode can use five keys; four default
+ * keys for receiving (with keyidx 0..3) and one individual key for
+ * both transmitting and receiving (keyidx 0) _unicast_ packets. Now,
+ * keyidx 0 is reserved for this unicast use and default keys can only
+ * use keyidx 1..3 (i.e., default key with keyidx 0 is not supported).
+ * This should be fine for more or less all cases, but for completeness
+ * sake, the driver could be enhanced to support the missing key. */
+#if 0
+ if (addr == NULL)
+ os_memset(param->sta_addr, 0xff, ETH_ALEN);
+ else
+ os_memcpy(param->sta_addr, addr, ETH_ALEN);
+#else
+ os_memset(param->sta_addr, 0xff, ETH_ALEN);
+#endif
+ os_strlcpy((char *) param->u.crypt.alg, alg_name,
+ HOSTAP_CRYPT_ALG_NAME_LEN);
+ param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0;
+ param->u.crypt.idx = key_idx;
+ if (seq)
+ os_memcpy(param->u.crypt.seq, seq, seq_len);
+ param->u.crypt.key_len = key_len;
+ os_memcpy((u8 *) (param + 1), key, key_len);
+
+ if (hostapd_ioctl(drv, param, blen, 1)) {
+ wpa_printf(MSG_WARNING, "Failed to set encryption.");
+ show_set_key_error(param);
+ ret = -1;
+ }
+ os_free(buf);
+
+ return ret;
+}
+
+
+static int wpa_driver_hostap_set_countermeasures(void *priv, int enabled)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+ return prism2param(drv, PRISM2_PARAM_TKIP_COUNTERMEASURES, enabled);
+}
+
+
+static int wpa_driver_hostap_reset(struct wpa_driver_hostap_data *drv,
+ int type)
+{
+ struct iwreq iwr;
+ int *i, ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: type=%d", __FUNCTION__, type);
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ i = (int *) iwr.u.name;
+ *i++ = type;
+
+ if (ioctl(drv->sock, PRISM2_IOCTL_RESET, &iwr) < 0) {
+ perror("ioctl[PRISM2_IOCTL_RESET]");
+ ret = -1;
+ }
+ return ret;
+}
+
+
+static int wpa_driver_hostap_mlme(struct wpa_driver_hostap_data *drv,
+ const u8 *addr, int cmd, int reason_code)
+{
+ struct prism2_hostapd_param param;
+ int ret;
+
+ /* There does not seem to be a better way of deauthenticating or
+ * disassociating with Prism2/2.5/3 than sending the management frame
+ * and then resetting the Port0 to make sure both the AP and the STA
+ * end up in disconnected state. */
+ os_memset(&param, 0, sizeof(param));
+ param.cmd = PRISM2_HOSTAPD_MLME;
+ os_memcpy(param.sta_addr, addr, ETH_ALEN);
+ param.u.mlme.cmd = cmd;
+ param.u.mlme.reason_code = reason_code;
+ ret = hostapd_ioctl(drv, &param, sizeof(param), 1);
+ if (ret == 0) {
+ os_sleep(0, 100000);
+ ret = wpa_driver_hostap_reset(drv, 2);
+ }
+ return ret;
+}
+
+
+static int wpa_driver_hostap_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DEAUTH,
+ reason_code);
+}
+
+
+static int wpa_driver_hostap_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DISASSOC,
+ reason_code);
+}
+
+
+static int
+wpa_driver_hostap_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ int ret = 0;
+ int allow_unencrypted_eapol;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (prism2param(drv, PRISM2_PARAM_DROP_UNENCRYPTED,
+ params->drop_unencrypted) < 0)
+ ret = -1;
+ if (wpa_driver_hostap_set_auth_alg(drv, params->auth_alg) < 0)
+ ret = -1;
+ if (params->mode != drv->current_mode) {
+ /* At the moment, Host AP driver requires host_roaming=2 for
+ * infrastructure mode and host_roaming=0 for adhoc. */
+ if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING,
+ params->mode == IEEE80211_MODE_IBSS ? 0 : 2) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to set host_roaming",
+ __func__);
+ }
+ drv->current_mode = params->mode;
+ }
+
+ if (prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED,
+ params->key_mgmt_suite != KEY_MGMT_NONE) < 0)
+ ret = -1;
+ if (wpa_driver_hostap_set_wpa_ie(drv, params->wpa_ie,
+ params->wpa_ie_len) < 0)
+ ret = -1;
+ if (wpa_driver_wext_set_mode(drv->wext, params->mode) < 0)
+ ret = -1;
+ if (params->freq &&
+ wpa_driver_wext_set_freq(drv->wext, params->freq) < 0)
+ ret = -1;
+ if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len)
+ < 0)
+ ret = -1;
+ if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0)
+ ret = -1;
+
+ /* Allow unencrypted EAPOL messages even if pairwise keys are set when
+ * not using WPA. IEEE 802.1X specifies that these frames are not
+ * encrypted, but WPA encrypts them when pairwise keys are in use. */
+ if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
+ params->key_mgmt_suite == KEY_MGMT_PSK)
+ allow_unencrypted_eapol = 0;
+ else
+ allow_unencrypted_eapol = 1;
+
+ if (prism2param(drv, PRISM2_PARAM_IEEE_802_1X,
+ allow_unencrypted_eapol) < 0) {
+ wpa_printf(MSG_DEBUG, "hostap: Failed to configure "
+ "ieee_802_1x param");
+ /* Ignore this error.. driver_hostap.c can also be used with
+ * other drivers that do not support this prism2_param. */
+ }
+
+ return ret;
+}
+
+
+static int wpa_driver_hostap_scan(void *priv,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ struct prism2_hostapd_param param;
+ int ret;
+ const u8 *ssid = params->ssids[0].ssid;
+ size_t ssid_len = params->ssids[0].ssid_len;
+
+ if (ssid == NULL) {
+ /* Use standard Linux Wireless Extensions ioctl if possible
+ * because some drivers using hostap code in wpa_supplicant
+ * might not support Host AP specific scan request (with SSID
+ * info). */
+ return wpa_driver_wext_scan(drv->wext, params);
+ }
+
+ if (ssid_len > 32)
+ ssid_len = 32;
+
+ os_memset(&param, 0, sizeof(param));
+ param.cmd = PRISM2_HOSTAPD_SCAN_REQ;
+ param.u.scan_req.ssid_len = ssid_len;
+ os_memcpy(param.u.scan_req.ssid, ssid, ssid_len);
+ ret = hostapd_ioctl(drv, &param, sizeof(param), 1);
+
+ /* Not all drivers generate "scan completed" wireless event, so try to
+ * read results after a timeout. */
+ eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext,
+ drv->ctx);
+ eloop_register_timeout(3, 0, wpa_driver_wext_scan_timeout, drv->wext,
+ drv->ctx);
+
+ return ret;
+}
+
+
+static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ int algs = 0;
+
+ if (auth_alg & WPA_AUTH_ALG_OPEN)
+ algs |= 1;
+ if (auth_alg & WPA_AUTH_ALG_SHARED)
+ algs |= 2;
+ if (auth_alg & WPA_AUTH_ALG_LEAP)
+ algs |= 4;
+ if (algs == 0)
+ algs = 1; /* at least one algorithm should be set */
+
+ return prism2param(drv, PRISM2_PARAM_AP_AUTH_ALGS, algs);
+}
+
+
+static int wpa_driver_hostap_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ return wpa_driver_wext_get_bssid(drv->wext, bssid);
+}
+
+
+static int wpa_driver_hostap_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ return wpa_driver_wext_get_ssid(drv->wext, ssid);
+}
+
+
+static struct wpa_scan_results * wpa_driver_hostap_get_scan_results(void *priv)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ return wpa_driver_wext_get_scan_results(drv->wext);
+}
+
+
+static int wpa_driver_hostap_set_operstate(void *priv, int state)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ return wpa_driver_wext_set_operstate(drv->wext, state);
+}
+
+
+static void * wpa_driver_hostap_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_hostap_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->wext = wpa_driver_wext_init(ctx, ifname);
+ if (drv->wext == NULL) {
+ os_free(drv);
+ return NULL;
+ }
+
+ drv->ctx = ctx;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->sock < 0) {
+ perror("socket");
+ wpa_driver_wext_deinit(drv->wext);
+ os_free(drv);
+ return NULL;
+ }
+
+ if (os_strncmp(ifname, "wlan", 4) == 0) {
+ /*
+ * Host AP driver may use both wlan# and wifi# interface in
+ * wireless events.
+ */
+ char ifname2[IFNAMSIZ + 1];
+ os_strlcpy(ifname2, ifname, sizeof(ifname2));
+ os_memcpy(ifname2, "wifi", 4);
+ wpa_driver_wext_alternative_ifindex(drv->wext, ifname2);
+ }
+
+ wpa_driver_hostap_set_wpa(drv, 1);
+
+ return drv;
+}
+
+
+static void wpa_driver_hostap_deinit(void *priv)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ wpa_driver_hostap_set_wpa(drv, 0);
+ wpa_driver_wext_deinit(drv->wext);
+ close(drv->sock);
+ os_free(drv);
+}
+
+#endif /* HOSTAPD */
+
+
+const struct wpa_driver_ops wpa_driver_hostap_ops = {
+ .name = "hostap",
+ .desc = "Host AP driver (Intersil Prism2/2.5/3)",
+ .set_key = wpa_driver_hostap_set_key,
+#ifdef HOSTAPD
+ .hapd_init = hostap_init,
+ .hapd_deinit = hostap_driver_deinit,
+ .set_ieee8021x = hostap_set_ieee8021x,
+ .set_privacy = hostap_set_privacy,
+ .get_seqnum = hostap_get_seqnum,
+ .flush = hostap_flush,
+ .set_generic_elem = hostap_set_generic_elem,
+ .read_sta_data = hostap_read_sta_data,
+ .hapd_send_eapol = hostap_send_eapol,
+ .sta_set_flags = hostap_sta_set_flags,
+ .sta_deauth = hostap_sta_deauth,
+ .sta_disassoc = hostap_sta_disassoc,
+ .sta_remove = hostap_sta_remove,
+ .hapd_set_ssid = hostap_set_ssid,
+ .send_mlme = hostap_send_mlme,
+ .sta_add = hostap_sta_add,
+ .get_inact_sec = hostap_get_inact_sec,
+ .sta_clear_stats = hostap_sta_clear_stats,
+ .get_hw_feature_data = hostap_get_hw_feature_data,
+ .set_ap_wps_ie = hostap_set_ap_wps_ie,
+#else /* HOSTAPD */
+ .get_bssid = wpa_driver_hostap_get_bssid,
+ .get_ssid = wpa_driver_hostap_get_ssid,
+ .set_countermeasures = wpa_driver_hostap_set_countermeasures,
+ .scan2 = wpa_driver_hostap_scan,
+ .get_scan_results2 = wpa_driver_hostap_get_scan_results,
+ .deauthenticate = wpa_driver_hostap_deauthenticate,
+ .disassociate = wpa_driver_hostap_disassociate,
+ .associate = wpa_driver_hostap_associate,
+ .init = wpa_driver_hostap_init,
+ .deinit = wpa_driver_hostap_deinit,
+ .set_operstate = wpa_driver_hostap_set_operstate,
+#endif /* HOSTAPD */
+};
diff --git a/src/drivers/driver_hostap.h b/src/drivers/driver_hostap.h
new file mode 100644
index 0000000..66b2bb3
--- /dev/null
+++ b/src/drivers/driver_hostap.h
@@ -0,0 +1,216 @@
+/*
+ * Driver interaction with Linux Host AP driver
+ * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef HOSTAP_DRIVER_H
+#define HOSTAP_DRIVER_H
+
+/* netdevice private ioctls (used, e.g., with iwpriv from user space) */
+
+/* New wireless extensions API - SET/GET convention (even ioctl numbers are
+ * root only)
+ */
+#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
+#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
+#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2)
+#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3)
+#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4)
+#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6)
+#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8)
+#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10)
+#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12)
+#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14)
+#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16)
+#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18)
+#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20)
+#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22)
+
+/* following are not in SIOCGIWPRIV list; check permission in the driver code
+ */
+#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13)
+#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14)
+
+
+/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */
+enum {
+ /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */
+ PRISM2_PARAM_TXRATECTRL = 2,
+ PRISM2_PARAM_BEACON_INT = 3,
+ PRISM2_PARAM_PSEUDO_IBSS = 4,
+ PRISM2_PARAM_ALC = 5,
+ /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */
+ PRISM2_PARAM_DUMP = 7,
+ PRISM2_PARAM_OTHER_AP_POLICY = 8,
+ PRISM2_PARAM_AP_MAX_INACTIVITY = 9,
+ PRISM2_PARAM_AP_BRIDGE_PACKETS = 10,
+ PRISM2_PARAM_DTIM_PERIOD = 11,
+ PRISM2_PARAM_AP_NULLFUNC_ACK = 12,
+ PRISM2_PARAM_MAX_WDS = 13,
+ PRISM2_PARAM_AP_AUTOM_AP_WDS = 14,
+ PRISM2_PARAM_AP_AUTH_ALGS = 15,
+ PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16,
+ PRISM2_PARAM_HOST_ENCRYPT = 17,
+ PRISM2_PARAM_HOST_DECRYPT = 18,
+ PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19,
+ PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20,
+ PRISM2_PARAM_HOST_ROAMING = 21,
+ PRISM2_PARAM_BCRX_STA_KEY = 22,
+ PRISM2_PARAM_IEEE_802_1X = 23,
+ PRISM2_PARAM_ANTSEL_TX = 24,
+ PRISM2_PARAM_ANTSEL_RX = 25,
+ PRISM2_PARAM_MONITOR_TYPE = 26,
+ PRISM2_PARAM_WDS_TYPE = 27,
+ PRISM2_PARAM_HOSTSCAN = 28,
+ PRISM2_PARAM_AP_SCAN = 29,
+ PRISM2_PARAM_ENH_SEC = 30,
+ PRISM2_PARAM_IO_DEBUG = 31,
+ PRISM2_PARAM_BASIC_RATES = 32,
+ PRISM2_PARAM_OPER_RATES = 33,
+ PRISM2_PARAM_HOSTAPD = 34,
+ PRISM2_PARAM_HOSTAPD_STA = 35,
+ PRISM2_PARAM_WPA = 36,
+ PRISM2_PARAM_PRIVACY_INVOKED = 37,
+ PRISM2_PARAM_TKIP_COUNTERMEASURES = 38,
+ PRISM2_PARAM_DROP_UNENCRYPTED = 39,
+ PRISM2_PARAM_SCAN_CHANNEL_MASK = 40,
+};
+
+enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1,
+ HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 };
+
+
+/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */
+enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1,
+ AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3,
+ AP_MAC_CMD_KICKALL = 4 };
+
+
+/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */
+enum {
+ PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */,
+ /* Note! Old versions of prism2_srec have a fatal error in CRC-16
+ * calculation, which will corrupt all non-volatile downloads.
+ * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to
+ * prevent use of old versions of prism2_srec for non-volatile
+ * download. */
+ PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */,
+ PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */,
+ /* Persistent versions of volatile download commands (keep firmware
+ * data in memory and automatically re-download after hw_reset */
+ PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5,
+ PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6,
+};
+
+struct prism2_download_param {
+ u32 dl_cmd;
+ u32 start_addr;
+ u32 num_areas;
+ struct prism2_download_area {
+ u32 addr; /* wlan card address */
+ u32 len;
+ caddr_t ptr; /* pointer to data in user space */
+ } data[0];
+};
+
+#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072
+#define PRISM2_MAX_DOWNLOAD_LEN 262144
+
+
+/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */
+enum {
+ PRISM2_HOSTAPD_FLUSH = 1,
+ PRISM2_HOSTAPD_ADD_STA = 2,
+ PRISM2_HOSTAPD_REMOVE_STA = 3,
+ PRISM2_HOSTAPD_GET_INFO_STA = 4,
+ /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */
+ PRISM2_SET_ENCRYPTION = 6,
+ PRISM2_GET_ENCRYPTION = 7,
+ PRISM2_HOSTAPD_SET_FLAGS_STA = 8,
+ PRISM2_HOSTAPD_GET_RID = 9,
+ PRISM2_HOSTAPD_SET_RID = 10,
+ PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11,
+ PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12,
+ PRISM2_HOSTAPD_MLME = 13,
+ PRISM2_HOSTAPD_SCAN_REQ = 14,
+ PRISM2_HOSTAPD_STA_CLEAR_STATS = 15,
+};
+
+#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
+#define PRISM2_HOSTAPD_RID_HDR_LEN \
+((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data))
+#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
+((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
+
+/* Maximum length for algorithm names (-1 for nul termination) used in ioctl()
+ */
+#define HOSTAP_CRYPT_ALG_NAME_LEN 16
+
+
+struct prism2_hostapd_param {
+ u32 cmd;
+ u8 sta_addr[ETH_ALEN];
+ union {
+ struct {
+ u16 aid;
+ u16 capability;
+ u8 tx_supp_rates;
+ } add_sta;
+ struct {
+ u32 inactive_sec;
+ } get_info_sta;
+ struct {
+ u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN];
+ u32 flags;
+ u32 err;
+ u8 idx;
+ u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+ u16 key_len;
+ u8 key[0];
+ } crypt;
+ struct {
+ u32 flags_and;
+ u32 flags_or;
+ } set_flags_sta;
+ struct {
+ u16 rid;
+ u16 len;
+ u8 data[0];
+ } rid;
+ struct {
+ u8 len;
+ u8 data[0];
+ } generic_elem;
+ struct {
+#define MLME_STA_DEAUTH 0
+#define MLME_STA_DISASSOC 1
+ u16 cmd;
+ u16 reason_code;
+ } mlme;
+ struct {
+ u8 ssid_len;
+ u8 ssid[32];
+ } scan_req;
+ } u;
+};
+
+#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0)
+#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1)
+
+#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2
+#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3
+#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4
+#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5
+#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6
+#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7
+
+#endif /* HOSTAP_DRIVER_H */
diff --git a/src/drivers/driver_iphone.m b/src/drivers/driver_iphone.m
new file mode 100644
index 0000000..8213fda
--- /dev/null
+++ b/src/drivers/driver_iphone.m
@@ -0,0 +1,466 @@
+/*
+ * WPA Supplicant - iPhone/iPod touch Apple80211 driver interface
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#define Boolean __DummyBoolean
+#include <CoreFoundation/CoreFoundation.h>
+#undef Boolean
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+
+#include "MobileApple80211.h"
+
+struct wpa_driver_iphone_data {
+ void *ctx;
+ Apple80211Ref wireless_ctx;
+ CFArrayRef scan_results;
+ int ctrl_power;
+};
+
+
+static const void * cfdict_get_key_str(CFDictionaryRef dict, const char *key)
+{
+ const void *res;
+ CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, key,
+ kCFStringEncodingMacRoman);
+ if (str == NULL)
+ return NULL;
+
+ res = CFDictionaryGetValue(dict, str);
+ CFRelease(str);
+ return res;
+}
+
+
+static int wpa_driver_iphone_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_iphone_data *drv = priv;
+ CFDataRef data;
+ int err, len;
+
+ err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_SSID, 0,
+ &data);
+ if (err != 0) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(SSID) "
+ "failed: %d", err);
+ return -1;
+ }
+
+ len = CFDataGetLength(data);
+ if (len > 32) {
+ CFRelease(data);
+ return -1;
+ }
+ os_memcpy(ssid, CFDataGetBytePtr(data), len);
+ CFRelease(data);
+
+ return len;
+}
+
+
+static int wpa_driver_iphone_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_iphone_data *drv = priv;
+ CFStringRef data;
+ int err;
+ int a1, a2, a3, a4, a5, a6;
+
+ err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_BSSID, 0,
+ &data);
+ if (err != 0) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(BSSID) "
+ "failed: %d", err);
+ return -1;
+ }
+
+ sscanf(CFStringGetCStringPtr(data, kCFStringEncodingMacRoman),
+ "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6);
+ bssid[0] = a1;
+ bssid[1] = a2;
+ bssid[2] = a3;
+ bssid[3] = a4;
+ bssid[4] = a5;
+ bssid[5] = a6;
+
+ CFRelease(data);
+
+ return 0;
+}
+
+
+static void wpa_driver_iphone_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static int wpa_driver_iphone_scan(void *priv, const u8 *ssid, size_t ssid_len)
+{
+ struct wpa_driver_iphone_data *drv = priv;
+ int err;
+
+ if (drv->scan_results) {
+ CFRelease(drv->scan_results);
+ drv->scan_results = NULL;
+ }
+
+ err = Apple80211Scan(drv->wireless_ctx, &drv->scan_results, NULL);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211Scan failed: %d",
+ err);
+ return -1;
+ }
+
+ eloop_register_timeout(0, 0, wpa_driver_iphone_scan_timeout, drv,
+ drv->ctx);
+ return 0;
+}
+
+
+static int wpa_driver_iphone_get_scan_results(void *priv,
+ struct wpa_scan_result *results,
+ size_t max_size)
+{
+ struct wpa_driver_iphone_data *drv = priv;
+ size_t i, num;
+
+ if (drv->scan_results == NULL)
+ return 0;
+
+ num = CFArrayGetCount(drv->scan_results);
+ if (num > max_size)
+ num = max_size;
+ os_memset(results, 0, num * sizeof(struct wpa_scan_result));
+
+ for (i = 0; i < num; i++) {
+ struct wpa_scan_result *res = &results[i];
+ CFDictionaryRef dict =
+ CFArrayGetValueAtIndex(drv->scan_results, i);
+ CFDataRef data;
+ CFStringRef str;
+ CFNumberRef num;
+ int val;
+
+ data = cfdict_get_key_str(dict, "SSID");
+ if (data) {
+ res->ssid_len = CFDataGetLength(data);
+ if (res->ssid_len > 32)
+ res->ssid_len = 32;
+ os_memcpy(res->ssid, CFDataGetBytePtr(data),
+ res->ssid_len);
+ }
+
+ str = cfdict_get_key_str(dict, "BSSID");
+ if (str) {
+ int a1, a2, a3, a4, a5, a6;
+ sscanf(CFStringGetCStringPtr(
+ str, kCFStringEncodingMacRoman),
+ "%x:%x:%x:%x:%x:%x",
+ &a1, &a2, &a3, &a4, &a5, &a6);
+ res->bssid[0] = a1;
+ res->bssid[1] = a2;
+ res->bssid[2] = a3;
+ res->bssid[3] = a4;
+ res->bssid[4] = a5;
+ res->bssid[5] = a6;
+ }
+
+ num = cfdict_get_key_str(dict, "CAPABILITIES");
+ if (num) {
+ if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
+ res->caps = val;
+ }
+
+ num = cfdict_get_key_str(dict, "CHANNEL");
+ if (num) {
+ if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
+ res->freq = 2407 + val * 5;
+ }
+
+ num = cfdict_get_key_str(dict, "RSSI");
+ if (num) {
+ if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
+ res->level = val;
+ }
+
+ num = cfdict_get_key_str(dict, "NOISE");
+ if (num) {
+ if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
+ res->noise = val;
+ }
+
+ data = cfdict_get_key_str(dict, "IE");
+ if (data) {
+ u8 *ptr = (u8 *) CFDataGetBytePtr(data);
+ int len = CFDataGetLength(data);
+ u8 *pos = ptr, *end = ptr + len;
+
+ while (pos + 2 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_RSN &&
+ pos[1] <= SSID_MAX_WPA_IE_LEN) {
+ os_memcpy(res->rsn_ie, pos,
+ 2 + pos[1]);
+ res->rsn_ie_len = 2 + pos[1];
+ }
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
+ pos[1] > 4 && pos[2] == 0x00 &&
+ pos[3] == 0x50 && pos[4] == 0xf2 &&
+ pos[5] == 0x01) {
+ os_memcpy(res->wpa_ie, pos,
+ 2 + pos[1]);
+ res->wpa_ie_len = 2 + pos[1];
+ }
+
+ pos = pos + 2 + pos[1];
+ }
+ }
+ }
+
+ return num;
+}
+
+
+static void wpa_driver_iphone_assoc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_iphone_data *drv = eloop_ctx;
+ u8 bssid[ETH_ALEN];
+
+ if (wpa_driver_iphone_get_bssid(drv, bssid) != 0) {
+ eloop_register_timeout(1, 0, wpa_driver_iphone_assoc_timeout,
+ drv, drv->ctx);
+ return;
+ }
+
+ wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL);
+}
+
+
+static int wpa_driver_iphone_associate(
+ void *priv, struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_iphone_data *drv = priv;
+ int i, num, err;
+ size_t ssid_len;
+ CFDictionaryRef bss = NULL;
+
+ /*
+ * TODO: Consider generating parameters instead of just using an entry
+ * from scan results in order to support ap_scan=2.
+ */
+
+ if (drv->scan_results == NULL) {
+ wpa_printf(MSG_DEBUG, "iPhone: No scan results - cannot "
+ "associate");
+ return -1;
+ }
+
+ num = CFArrayGetCount(drv->scan_results);
+
+ for (i = 0; i < num; i++) {
+ CFDictionaryRef dict =
+ CFArrayGetValueAtIndex(drv->scan_results, i);
+ CFDataRef data;
+
+ data = cfdict_get_key_str(dict, "SSID");
+ if (data == NULL)
+ continue;
+
+ ssid_len = CFDataGetLength(data);
+ if (ssid_len != params->ssid_len ||
+ os_memcmp(CFDataGetBytePtr(data), params->ssid, ssid_len)
+ != 0)
+ continue;
+
+ bss = dict;
+ break;
+ }
+
+ if (bss == NULL) {
+ wpa_printf(MSG_DEBUG, "iPhone: Could not find SSID from scan "
+ "results - cannot associate");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "iPhone: Trying to associate with a BSS found "
+ "from scan results");
+
+ err = Apple80211Associate(drv->wireless_ctx, bss, NULL);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211Associate() failed: "
+ "%d", err);
+ return -1;
+ }
+
+ /*
+ * Driver is actually already associated; report association from an
+ * eloop callback.
+ */
+ eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx);
+ eloop_register_timeout(0, 0, wpa_driver_iphone_assoc_timeout, drv,
+ drv->ctx);
+
+ return 0;
+}
+
+
+static int wpa_driver_iphone_set_key(void *priv, wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx, const u8 *seq,
+ size_t seq_len, const u8 *key,
+ size_t key_len)
+{
+ /*
+ * TODO: Need to either support configuring PMK for 4-way handshake or
+ * PTK for TKIP/CCMP.
+ */
+ return -1;
+}
+
+
+static int wpa_driver_iphone_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ os_memset(capa, 0, sizeof(*capa));
+
+ capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 |
+ WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP;
+ capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED |
+ WPA_DRIVER_AUTH_LEAP;
+ capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+
+ return 0;
+}
+
+
+static void * wpa_driver_iphone_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_iphone_data *drv;
+ int err;
+ char power;
+ CFStringRef name;
+ CFDictionaryRef dict;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->ctx = ctx;
+ err = Apple80211Open(&drv->wireless_ctx);
+ if (err) {
+ wpa_printf(MSG_ERROR, "iPhone: Apple80211Open failed: %d",
+ err);
+ os_free(drv);
+ return NULL;
+ }
+
+ name = CFStringCreateWithCString(kCFAllocatorDefault, ifname,
+ kCFStringEncodingISOLatin1);
+ if (name == NULL) {
+ wpa_printf(MSG_ERROR, "iPhone: ifname -> CFString failed");
+ Apple80211Close(drv->wireless_ctx);
+ os_free(drv);
+ return NULL;
+ }
+
+ err = Apple80211BindToInterface(drv->wireless_ctx, name);
+ CFRelease(name);
+
+ if (err) {
+ wpa_printf(MSG_ERROR, "iPhone: Apple80211BindToInterface "
+ "failed: %d", err);
+ Apple80211Close(drv->wireless_ctx);
+ os_free(drv);
+ return NULL;
+ }
+
+ err = Apple80211GetPower(drv->wireless_ctx, &power);
+ if (err)
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211GetPower failed: %d",
+ err);
+
+ wpa_printf(MSG_DEBUG, "iPhone: Power=%d", power);
+
+ if (!power) {
+ drv->ctrl_power = 1;
+ err = Apple80211SetPower(drv->wireless_ctx, 1);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower "
+ "failed: %d", err);
+ Apple80211Close(drv->wireless_ctx);
+ os_free(drv);
+ return NULL;
+ }
+ }
+
+ err = Apple80211GetInfoCopy(drv->wireless_ctx, &dict);
+ if (err == 0) {
+ CFShow(dict);
+ CFRelease(dict);
+ } else {
+ printf("Apple80211GetInfoCopy: %d\n", err);
+ }
+
+ return drv;
+}
+
+
+static void wpa_driver_iphone_deinit(void *priv)
+{
+ struct wpa_driver_iphone_data *drv = priv;
+ int err;
+
+ eloop_cancel_timeout(wpa_driver_iphone_scan_timeout, drv, drv->ctx);
+ eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx);
+
+ if (drv->ctrl_power) {
+ wpa_printf(MSG_DEBUG, "iPhone: Power down the interface");
+ err = Apple80211SetPower(drv->wireless_ctx, 0);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower(0) "
+ "failed: %d", err);
+ }
+ }
+
+ err = Apple80211Close(drv->wireless_ctx);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211Close failed: %d",
+ err);
+ }
+
+ if (drv->scan_results)
+ CFRelease(drv->scan_results);
+
+ os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_iphone_ops = {
+ .name = "iphone",
+ .desc = "iPhone/iPod touch Apple80211 driver",
+ .get_ssid = wpa_driver_iphone_get_ssid,
+ .get_bssid = wpa_driver_iphone_get_bssid,
+ .init = wpa_driver_iphone_init,
+ .deinit = wpa_driver_iphone_deinit,
+ .scan = wpa_driver_iphone_scan,
+ .get_scan_results = wpa_driver_iphone_get_scan_results,
+ .associate = wpa_driver_iphone_associate,
+ .set_key = wpa_driver_iphone_set_key,
+ .get_capa = wpa_driver_iphone_get_capa,
+};
diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c
new file mode 100644
index 0000000..630fbf4
--- /dev/null
+++ b/src/drivers/driver_madwifi.c
@@ -0,0 +1,1856 @@
+/*
+ * WPA Supplicant - driver interaction with MADWIFI 802.11 driver
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, Video54 Technologies
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * While this driver wrapper supports both AP (hostapd) and station
+ * (wpa_supplicant) operations, the station side is deprecated and
+ * driver_wext.c should be used instead. This driver wrapper should only be
+ * used with hostapd for AP mode functionality.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "driver.h"
+#include "driver_wext.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "wireless_copy.h"
+
+/*
+ * Avoid conflicts with wpa_supplicant definitions by undefining a definition.
+ */
+#undef WME_OUI_TYPE
+
+#include <include/compat.h>
+#include <net80211/ieee80211.h>
+#ifdef WME_NUM_AC
+/* Assume this is built against BSD branch of madwifi driver. */
+#define MADWIFI_BSD
+#include <net80211/_ieee80211.h>
+#endif /* WME_NUM_AC */
+#include <net80211/ieee80211_crypto.h>
+#include <net80211/ieee80211_ioctl.h>
+
+#ifdef CONFIG_WPS
+#ifdef IEEE80211_IOCTL_FILTERFRAME
+#include <netpacket/packet.h>
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW 0x0019
+#endif
+#endif /* IEEE80211_IOCTL_FILTERFRAME */
+#endif /* CONFIG_WPS */
+
+/*
+ * Avoid conflicts with hostapd definitions by undefining couple of defines
+ * from madwifi header files.
+ */
+#undef RSN_VERSION
+#undef WPA_VERSION
+#undef WPA_OUI_TYPE
+#undef WME_OUI_TYPE
+
+
+#ifdef IEEE80211_IOCTL_SETWMMPARAMS
+/* Assume this is built against madwifi-ng */
+#define MADWIFI_NG
+#endif /* IEEE80211_IOCTL_SETWMMPARAMS */
+
+#define WPA_KEY_RSC_LEN 8
+
+#ifdef HOSTAPD
+
+#include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "l2_packet/l2_packet.h"
+
+
+struct madwifi_driver_data {
+ struct hostapd_data *hapd; /* back pointer */
+
+ char iface[IFNAMSIZ + 1];
+ int ifindex;
+ struct l2_packet_data *sock_xmit; /* raw packet xmit socket */
+ struct l2_packet_data *sock_recv; /* raw packet recv socket */
+ int ioctl_sock; /* socket for ioctl() use */
+ struct netlink_data *netlink;
+ int we_version;
+ u8 acct_mac[ETH_ALEN];
+ struct hostap_sta_driver_data acct_data;
+
+ struct l2_packet_data *sock_raw; /* raw 802.11 management frames */
+};
+
+static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason_code);
+
+static int
+set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len)
+{
+ struct iwreq iwr;
+ int do_inline = len < IFNAMSIZ;
+
+ memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+#ifdef IEEE80211_IOCTL_FILTERFRAME
+ /* FILTERFRAME must be NOT inline, regardless of size. */
+ if (op == IEEE80211_IOCTL_FILTERFRAME)
+ do_inline = 0;
+#endif /* IEEE80211_IOCTL_FILTERFRAME */
+ if (op == IEEE80211_IOCTL_SET_APPIEBUF)
+ do_inline = 0;
+ if (do_inline) {
+ /*
+ * Argument data fits inline; put it there.
+ */
+ memcpy(iwr.u.name, data, len);
+ } else {
+ /*
+ * Argument data too big for inline transfer; setup a
+ * parameter block instead; the kernel will transfer
+ * the data for the driver.
+ */
+ iwr.u.data.pointer = data;
+ iwr.u.data.length = len;
+ }
+
+ if (ioctl(drv->ioctl_sock, op, &iwr) < 0) {
+#ifdef MADWIFI_NG
+ int first = IEEE80211_IOCTL_SETPARAM;
+ static const char *opnames[] = {
+ "ioctl[IEEE80211_IOCTL_SETPARAM]",
+ "ioctl[IEEE80211_IOCTL_GETPARAM]",
+ "ioctl[IEEE80211_IOCTL_SETMODE]",
+ "ioctl[IEEE80211_IOCTL_GETMODE]",
+ "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]",
+ "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]",
+ "ioctl[IEEE80211_IOCTL_SETCHANLIST]",
+ "ioctl[IEEE80211_IOCTL_GETCHANLIST]",
+ "ioctl[IEEE80211_IOCTL_CHANSWITCH]",
+ "ioctl[IEEE80211_IOCTL_GET_APPIEBUF]",
+ "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]",
+ "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]",
+ "ioctl[IEEE80211_IOCTL_FILTERFRAME]",
+ "ioctl[IEEE80211_IOCTL_GETCHANINFO]",
+ "ioctl[IEEE80211_IOCTL_SETOPTIE]",
+ "ioctl[IEEE80211_IOCTL_GETOPTIE]",
+ "ioctl[IEEE80211_IOCTL_SETMLME]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_SETKEY]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_DELKEY]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_ADDMAC]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_DELMAC]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_WDSMAC]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_WDSDELMAC]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_KICKMAC]",
+ };
+#else /* MADWIFI_NG */
+ int first = IEEE80211_IOCTL_SETPARAM;
+ static const char *opnames[] = {
+ "ioctl[IEEE80211_IOCTL_SETPARAM]",
+ "ioctl[IEEE80211_IOCTL_GETPARAM]",
+ "ioctl[IEEE80211_IOCTL_SETKEY]",
+ "ioctl[SIOCIWFIRSTPRIV+3]",
+ "ioctl[IEEE80211_IOCTL_DELKEY]",
+ "ioctl[SIOCIWFIRSTPRIV+5]",
+ "ioctl[IEEE80211_IOCTL_SETMLME]",
+ "ioctl[SIOCIWFIRSTPRIV+7]",
+ "ioctl[IEEE80211_IOCTL_SETOPTIE]",
+ "ioctl[IEEE80211_IOCTL_GETOPTIE]",
+ "ioctl[IEEE80211_IOCTL_ADDMAC]",
+ "ioctl[SIOCIWFIRSTPRIV+11]",
+ "ioctl[IEEE80211_IOCTL_DELMAC]",
+ "ioctl[SIOCIWFIRSTPRIV+13]",
+ "ioctl[IEEE80211_IOCTL_CHANLIST]",
+ "ioctl[SIOCIWFIRSTPRIV+15]",
+ "ioctl[IEEE80211_IOCTL_GETRSN]",
+ "ioctl[SIOCIWFIRSTPRIV+17]",
+ "ioctl[IEEE80211_IOCTL_GETKEY]",
+ };
+#endif /* MADWIFI_NG */
+ int idx = op - first;
+ if (first <= op &&
+ idx < (int) (sizeof(opnames) / sizeof(opnames[0])) &&
+ opnames[idx])
+ perror(opnames[idx]);
+ else
+ perror("ioctl[unknown???]");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+set80211param(struct madwifi_driver_data *drv, int op, int arg)
+{
+ struct iwreq iwr;
+
+ memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+ iwr.u.mode = op;
+ memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg));
+
+ if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
+ perror("ioctl[IEEE80211_IOCTL_SETPARAM]");
+ wpa_printf(MSG_DEBUG, "%s: Failed to set parameter (op %d "
+ "arg %d)", __func__, op, arg);
+ return -1;
+ }
+ return 0;
+}
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char *
+ether_sprintf(const u8 *addr)
+{
+ static char buf[sizeof(MACSTR)];
+
+ if (addr != NULL)
+ snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+ else
+ snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
+ return buf;
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+/*
+ * Configure WPA parameters.
+ */
+static int
+madwifi_configure_wpa(struct madwifi_driver_data *drv,
+ struct wpa_bss_params *params)
+{
+ int v;
+
+ switch (params->wpa_group) {
+ case WPA_CIPHER_CCMP:
+ v = IEEE80211_CIPHER_AES_CCM;
+ break;
+ case WPA_CIPHER_TKIP:
+ v = IEEE80211_CIPHER_TKIP;
+ break;
+ case WPA_CIPHER_WEP104:
+ v = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_CIPHER_WEP40:
+ v = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_CIPHER_NONE:
+ v = IEEE80211_CIPHER_NONE;
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "Unknown group key cipher %u",
+ params->wpa_group);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v);
+ if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) {
+ printf("Unable to set group key cipher to %u\n", v);
+ return -1;
+ }
+ if (v == IEEE80211_CIPHER_WEP) {
+ /* key length is done only for specific ciphers */
+ v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
+ if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) {
+ printf("Unable to set group key length to %u\n", v);
+ return -1;
+ }
+ }
+
+ v = 0;
+ if (params->wpa_pairwise & WPA_CIPHER_CCMP)
+ v |= 1<<IEEE80211_CIPHER_AES_CCM;
+ if (params->wpa_pairwise & WPA_CIPHER_TKIP)
+ v |= 1<<IEEE80211_CIPHER_TKIP;
+ if (params->wpa_pairwise & WPA_CIPHER_NONE)
+ v |= 1<<IEEE80211_CIPHER_NONE;
+ wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
+ if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) {
+ printf("Unable to set pairwise key ciphers to 0x%x\n", v);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
+ __func__, params->wpa_key_mgmt);
+ if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS,
+ params->wpa_key_mgmt)) {
+ printf("Unable to set key management algorithms to 0x%x\n",
+ params->wpa_key_mgmt);
+ return -1;
+ }
+
+ v = 0;
+ if (params->rsn_preauth)
+ v |= BIT(0);
+ wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
+ __func__, params->rsn_preauth);
+ if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
+ printf("Unable to set RSN capabilities to 0x%x\n", v);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa);
+ if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) {
+ printf("Unable to set WPA to %u\n", params->wpa);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+ struct madwifi_driver_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
+
+ if (!params->enabled) {
+ /* XXX restore state */
+ return set80211param(priv, IEEE80211_PARAM_AUTHMODE,
+ IEEE80211_AUTH_AUTO);
+ }
+ if (!params->wpa && !params->ieee802_1x) {
+ hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
+ HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
+ return -1;
+ }
+ if (params->wpa && madwifi_configure_wpa(drv, params) != 0) {
+ hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
+ HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
+ return -1;
+ }
+ if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
+ (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+ hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
+ HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+madwifi_set_privacy(void *priv, int enabled)
+{
+ struct madwifi_driver_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+ return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled);
+}
+
+static int
+madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized)
+{
+ struct madwifi_driver_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d",
+ __func__, ether_sprintf(addr), authorized);
+
+ if (authorized)
+ mlme.im_op = IEEE80211_MLME_AUTHORIZE;
+ else
+ mlme.im_op = IEEE80211_MLME_UNAUTHORIZE;
+ mlme.im_reason = 0;
+ memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR,
+ __func__, authorized ? "" : "un", MAC2STR(addr));
+ }
+
+ return ret;
+}
+
+static int
+madwifi_sta_set_flags(void *priv, const u8 *addr,
+ int total_flags, int flags_or, int flags_and)
+{
+ /* For now, only support setting Authorized flag */
+ if (flags_or & WPA_STA_AUTHORIZED)
+ return madwifi_set_sta_authorized(priv, addr, 1);
+ if (!(flags_and & WPA_STA_AUTHORIZED))
+ return madwifi_set_sta_authorized(priv, addr, 0);
+ return 0;
+}
+
+static int
+madwifi_del_key(void *priv, const u8 *addr, int key_idx)
+{
+ struct madwifi_driver_data *drv = priv;
+ struct ieee80211req_del_key wk;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d",
+ __func__, ether_sprintf(addr), key_idx);
+
+ memset(&wk, 0, sizeof(wk));
+ if (addr != NULL) {
+ memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+ wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE;
+ } else {
+ wk.idk_keyix = key_idx;
+ }
+
+ ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk));
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s"
+ " key_idx %d)", __func__, ether_sprintf(addr),
+ key_idx);
+ }
+
+ return ret;
+}
+
+static int
+wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+ const u8 *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct madwifi_driver_data *drv = priv;
+ struct ieee80211req_key wk;
+ u_int8_t cipher;
+ int ret;
+
+ if (alg == WPA_ALG_NONE)
+ return madwifi_del_key(drv, addr, key_idx);
+
+ wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d",
+ __func__, alg, ether_sprintf(addr), key_idx);
+
+ if (alg == WPA_ALG_WEP)
+ cipher = IEEE80211_CIPHER_WEP;
+ else if (alg == WPA_ALG_TKIP)
+ cipher = IEEE80211_CIPHER_TKIP;
+ else if (alg == WPA_ALG_CCMP)
+ cipher = IEEE80211_CIPHER_AES_CCM;
+ else {
+ printf("%s: unknown/unsupported algorithm %d\n",
+ __func__, alg);
+ return -1;
+ }
+
+ if (key_len > sizeof(wk.ik_keydata)) {
+ printf("%s: key length %lu too big\n", __func__,
+ (unsigned long) key_len);
+ return -3;
+ }
+
+ memset(&wk, 0, sizeof(wk));
+ wk.ik_type = cipher;
+ wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT;
+ if (addr == NULL || 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.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 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);
+}
+
+#else /* HOSTAPD */
+
+struct wpa_driver_madwifi_data {
+ void *wext; /* private data for driver_wext */
+ void *ctx;
+ char ifname[IFNAMSIZ + 1];
+ int sock;
+};
+
+static int wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg);
+static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies,
+ size_t ies_len);
+
+
+static int
+set80211priv(struct wpa_driver_madwifi_data *drv, int op, void *data, int len,
+ int show_err)
+{
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ if (len < IFNAMSIZ &&
+ op != IEEE80211_IOCTL_SET_APPIEBUF) {
+ /*
+ * Argument data fits inline; put it there.
+ */
+ os_memcpy(iwr.u.name, data, len);
+ } else {
+ /*
+ * Argument data too big for inline transfer; setup a
+ * parameter block instead; the kernel will transfer
+ * the data for the driver.
+ */
+ iwr.u.data.pointer = data;
+ iwr.u.data.length = len;
+ }
+
+ if (ioctl(drv->sock, op, &iwr) < 0) {
+ if (show_err) {
+#ifdef MADWIFI_NG
+ int first = IEEE80211_IOCTL_SETPARAM;
+ int last = IEEE80211_IOCTL_KICKMAC;
+ static const char *opnames[] = {
+ "ioctl[IEEE80211_IOCTL_SETPARAM]",
+ "ioctl[IEEE80211_IOCTL_GETPARAM]",
+ "ioctl[IEEE80211_IOCTL_SETMODE]",
+ "ioctl[IEEE80211_IOCTL_GETMODE]",
+ "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]",
+ "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]",
+ "ioctl[IEEE80211_IOCTL_SETCHANLIST]",
+ "ioctl[IEEE80211_IOCTL_GETCHANLIST]",
+ "ioctl[IEEE80211_IOCTL_CHANSWITCH]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]",
+ "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_GETCHANINFO]",
+ "ioctl[IEEE80211_IOCTL_SETOPTIE]",
+ "ioctl[IEEE80211_IOCTL_GETOPTIE]",
+ "ioctl[IEEE80211_IOCTL_SETMLME]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_SETKEY]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_DELKEY]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_ADDMAC]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_DELMAC]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_WDSMAC]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_WDSDELMAC]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_KICKMAC]",
+ };
+#else /* MADWIFI_NG */
+ int first = IEEE80211_IOCTL_SETPARAM;
+ int last = IEEE80211_IOCTL_CHANLIST;
+ static const char *opnames[] = {
+ "ioctl[IEEE80211_IOCTL_SETPARAM]",
+ "ioctl[IEEE80211_IOCTL_GETPARAM]",
+ "ioctl[IEEE80211_IOCTL_SETKEY]",
+ "ioctl[IEEE80211_IOCTL_GETKEY]",
+ "ioctl[IEEE80211_IOCTL_DELKEY]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_SETMLME]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_SETOPTIE]",
+ "ioctl[IEEE80211_IOCTL_GETOPTIE]",
+ "ioctl[IEEE80211_IOCTL_ADDMAC]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_DELMAC]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_CHANLIST]",
+ };
+#endif /* MADWIFI_NG */
+ int idx = op - first;
+ if (first <= op && op <= last &&
+ idx < (int) (sizeof(opnames) / sizeof(opnames[0]))
+ && opnames[idx])
+ perror(opnames[idx]);
+ else
+ perror("ioctl[unknown???]");
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static int
+set80211param(struct wpa_driver_madwifi_data *drv, int op, int arg,
+ int show_err)
+{
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.mode = op;
+ os_memcpy(iwr.u.name+sizeof(u32), &arg, sizeof(arg));
+
+ if (ioctl(drv->sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
+ if (show_err)
+ perror("ioctl[IEEE80211_IOCTL_SETPARAM]");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+wpa_driver_madwifi_set_wpa_ie(struct wpa_driver_madwifi_data *drv,
+ const u8 *wpa_ie, size_t wpa_ie_len)
+{
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ /* NB: SETOPTIE is not fixed-size so must not be inlined */
+ iwr.u.data.pointer = (void *) wpa_ie;
+ iwr.u.data.length = wpa_ie_len;
+
+ if (ioctl(drv->sock, IEEE80211_IOCTL_SETOPTIE, &iwr) < 0) {
+ perror("ioctl[IEEE80211_IOCTL_SETOPTIE]");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+wpa_driver_madwifi_del_key(struct wpa_driver_madwifi_data *drv, int key_idx,
+ const u8 *addr)
+{
+ struct ieee80211req_del_key wk;
+
+ wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __FUNCTION__, key_idx);
+ os_memset(&wk, 0, sizeof(wk));
+ wk.idk_keyix = key_idx;
+ if (addr != NULL)
+ os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+
+ return set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk), 1);
+}
+
+static int
+wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+ const u8 *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ struct ieee80211req_key wk;
+ char *alg_name;
+ u_int8_t cipher;
+
+ if (alg == WPA_ALG_NONE)
+ return wpa_driver_madwifi_del_key(drv, key_idx, addr);
+
+ switch (alg) {
+ case WPA_ALG_WEP:
+ if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
+ ETH_ALEN) == 0) {
+ /*
+ * madwifi did not seem to like static WEP key
+ * configuration with IEEE80211_IOCTL_SETKEY, so use
+ * Linux wireless extensions ioctl for this.
+ */
+ return wpa_driver_wext_set_key(ifname, drv->wext, alg,
+ addr, key_idx, set_tx,
+ seq, seq_len,
+ key, key_len);
+ }
+ alg_name = "WEP";
+ cipher = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_ALG_TKIP:
+ alg_name = "TKIP";
+ cipher = IEEE80211_CIPHER_TKIP;
+ break;
+ case WPA_ALG_CCMP:
+ alg_name = "CCMP";
+ cipher = IEEE80211_CIPHER_AES_CCM;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d",
+ __FUNCTION__, alg);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu "
+ "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx,
+ (unsigned long) seq_len, (unsigned long) key_len);
+
+ if (seq_len > sizeof(u_int64_t)) {
+ wpa_printf(MSG_DEBUG, "%s: seq_len %lu too big",
+ __FUNCTION__, (unsigned long) seq_len);
+ return -2;
+ }
+ if (key_len > sizeof(wk.ik_keydata)) {
+ wpa_printf(MSG_DEBUG, "%s: key length %lu too big",
+ __FUNCTION__, (unsigned long) key_len);
+ return -3;
+ }
+
+ os_memset(&wk, 0, sizeof(wk));
+ wk.ik_type = cipher;
+ wk.ik_flags = IEEE80211_KEY_RECV;
+ if (addr == NULL ||
+ os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0)
+ wk.ik_flags |= IEEE80211_KEY_GROUP;
+ if (set_tx) {
+ wk.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT;
+ os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+ } else
+ os_memset(wk.ik_macaddr, 0, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = key_idx;
+ wk.ik_keylen = key_len;
+#ifdef WORDS_BIGENDIAN
+ if (seq) {
+ size_t i;
+ u8 tmp[WPA_KEY_RSC_LEN];
+ os_memset(tmp, 0, sizeof(tmp));
+ for (i = 0; i < seq_len; i++)
+ tmp[WPA_KEY_RSC_LEN - i - 1] = seq[i];
+ os_memcpy(&wk.ik_keyrsc, tmp, WPA_KEY_RSC_LEN);
+ }
+#else /* WORDS_BIGENDIAN */
+ if (seq)
+ os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+#endif /* WORDS_BIGENDIAN */
+ os_memcpy(wk.ik_keydata, key, key_len);
+
+ return set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk), 1);
+}
+
+static int
+wpa_driver_madwifi_set_countermeasures(void *priv, int enabled)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+ return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled, 1);
+}
+
+static int
+wpa_driver_madwifi_deauthenticate(void *priv, const u8 *addr, int reason_code)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ mlme.im_op = IEEE80211_MLME_DEAUTH;
+ mlme.im_reason = reason_code;
+ os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1);
+}
+
+static int
+wpa_driver_madwifi_disassociate(void *priv, const u8 *addr, int reason_code)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ mlme.im_op = IEEE80211_MLME_DISASSOC;
+ mlme.im_reason = reason_code;
+ os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1);
+}
+
+static int
+wpa_driver_madwifi_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+ int ret = 0, privacy = 1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (set80211param(drv, IEEE80211_PARAM_DROPUNENCRYPTED,
+ params->drop_unencrypted, 1) < 0)
+ ret = -1;
+ if (wpa_driver_madwifi_set_auth_alg(drv, params->auth_alg) < 0)
+ ret = -1;
+
+ /*
+ * NB: Don't need to set the freq or cipher-related state as
+ * this is implied by the bssid which is used to locate
+ * the scanned node state which holds it. The ssid is
+ * needed to disambiguate an AP that broadcasts multiple
+ * ssid's but uses the same bssid.
+ */
+ /* XXX error handling is wrong but unclear what to do... */
+ if (wpa_driver_madwifi_set_wpa_ie(drv, params->wpa_ie,
+ params->wpa_ie_len) < 0)
+ ret = -1;
+
+ if (params->pairwise_suite == CIPHER_NONE &&
+ params->group_suite == CIPHER_NONE &&
+ params->key_mgmt_suite == KEY_MGMT_NONE &&
+ params->wpa_ie_len == 0)
+ privacy = 0;
+
+ if (set80211param(drv, IEEE80211_PARAM_PRIVACY, privacy, 1) < 0)
+ ret = -1;
+
+ if (params->wpa_ie_len &&
+ set80211param(drv, IEEE80211_PARAM_WPA,
+ params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1, 1) < 0)
+ ret = -1;
+
+ if (params->bssid == NULL) {
+ /* ap_scan=2 mode - driver takes care of AP selection and
+ * roaming */
+ /* FIX: this does not seem to work; would probably need to
+ * change something in the driver */
+ if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0)
+ ret = -1;
+
+ if (wpa_driver_wext_set_ssid(drv->wext, params->ssid,
+ params->ssid_len) < 0)
+ ret = -1;
+ } else {
+ if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0)
+ ret = -1;
+ if (wpa_driver_wext_set_ssid(drv->wext, params->ssid,
+ params->ssid_len) < 0)
+ ret = -1;
+ os_memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = IEEE80211_MLME_ASSOC;
+ os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
+ if (set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme,
+ sizeof(mlme), 1) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: SETMLME[ASSOC] failed",
+ __func__);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+static int
+wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ int authmode;
+
+ if ((auth_alg & WPA_AUTH_ALG_OPEN) &&
+ (auth_alg & WPA_AUTH_ALG_SHARED))
+ authmode = IEEE80211_AUTH_AUTO;
+ else if (auth_alg & WPA_AUTH_ALG_SHARED)
+ authmode = IEEE80211_AUTH_SHARED;
+ else
+ authmode = IEEE80211_AUTH_OPEN;
+
+ return set80211param(drv, IEEE80211_PARAM_AUTHMODE, authmode, 1);
+}
+
+static int
+wpa_driver_madwifi_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+ const u8 *ssid = params->ssids[0].ssid;
+ size_t ssid_len = params->ssids[0].ssid_len;
+
+ wpa_driver_madwifi_set_probe_req_ie(drv, params->extra_ies,
+ params->extra_ies_len);
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+ /* set desired ssid before scan */
+ /* FIX: scan should not break the current association, so using
+ * set_ssid may not be the best way of doing this.. */
+ if (wpa_driver_wext_set_ssid(drv->wext, ssid, ssid_len) < 0)
+ ret = -1;
+
+ if (ioctl(drv->sock, SIOCSIWSCAN, &iwr) < 0) {
+ perror("ioctl[SIOCSIWSCAN]");
+ ret = -1;
+ }
+
+ /*
+ * madwifi delivers a scan complete event so no need to poll, but
+ * register a backup timeout anyway to make sure that we recover even
+ * if the driver does not send this event for any reason. This timeout
+ * will only be used if the event is not delivered (event handler will
+ * cancel the timeout).
+ */
+ eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext,
+ drv->ctx);
+ eloop_register_timeout(30, 0, wpa_driver_wext_scan_timeout, drv->wext,
+ drv->ctx);
+
+ return ret;
+}
+
+static int wpa_driver_madwifi_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ return wpa_driver_wext_get_bssid(drv->wext, bssid);
+}
+
+
+static int wpa_driver_madwifi_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ return wpa_driver_wext_get_ssid(drv->wext, ssid);
+}
+
+
+static struct wpa_scan_results *
+wpa_driver_madwifi_get_scan_results(void *priv)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ return wpa_driver_wext_get_scan_results(drv->wext);
+}
+
+
+static int wpa_driver_madwifi_set_operstate(void *priv, int state)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ return wpa_driver_wext_set_operstate(drv->wext, state);
+}
+
+
+static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies,
+ size_t ies_len)
+{
+ struct ieee80211req_getset_appiebuf *probe_req_ie;
+ int ret;
+
+ probe_req_ie = os_malloc(sizeof(*probe_req_ie) + ies_len);
+ if (probe_req_ie == NULL)
+ return -1;
+
+ probe_req_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_REQ;
+ probe_req_ie->app_buflen = ies_len;
+ os_memcpy(probe_req_ie->app_buf, ies, ies_len);
+
+ ret = set80211priv(priv, IEEE80211_IOCTL_SET_APPIEBUF, probe_req_ie,
+ sizeof(struct ieee80211req_getset_appiebuf) +
+ ies_len, 1);
+
+ os_free(probe_req_ie);
+
+ return ret;
+}
+
+
+static void * wpa_driver_madwifi_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_madwifi_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->wext = wpa_driver_wext_init(ctx, ifname);
+ if (drv->wext == NULL)
+ goto fail;
+
+ drv->ctx = ctx;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->sock < 0)
+ goto fail2;
+
+ if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based "
+ "roaming", __FUNCTION__);
+ goto fail3;
+ }
+
+ if (set80211param(drv, IEEE80211_PARAM_WPA, 3, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support",
+ __FUNCTION__);
+ goto fail3;
+ }
+
+ return drv;
+
+fail3:
+ close(drv->sock);
+fail2:
+ wpa_driver_wext_deinit(drv->wext);
+fail:
+ os_free(drv);
+ return NULL;
+}
+
+
+static void wpa_driver_madwifi_deinit(void *priv)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+
+ if (wpa_driver_madwifi_set_wpa_ie(drv, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to clear WPA IE",
+ __FUNCTION__);
+ }
+ if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to enable driver-based "
+ "roaming", __FUNCTION__);
+ }
+ if (set80211param(drv, IEEE80211_PARAM_PRIVACY, 0, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to disable forced Privacy "
+ "flag", __FUNCTION__);
+ }
+ if (set80211param(drv, IEEE80211_PARAM_WPA, 0, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to disable WPA",
+ __FUNCTION__);
+ }
+
+ wpa_driver_wext_deinit(drv->wext);
+
+ close(drv->sock);
+ os_free(drv);
+}
+
+#endif /* HOSTAPD */
+
+
+const struct wpa_driver_ops wpa_driver_madwifi_ops = {
+ .name = "madwifi",
+ .desc = "MADWIFI 802.11 support (Atheros, etc.)",
+ .set_key = wpa_driver_madwifi_set_key,
+#ifdef HOSTAPD
+ .hapd_init = madwifi_init,
+ .hapd_deinit = madwifi_deinit,
+ .set_ieee8021x = madwifi_set_ieee8021x,
+ .set_privacy = madwifi_set_privacy,
+ .get_seqnum = madwifi_get_seqnum,
+ .flush = madwifi_flush,
+ .set_generic_elem = madwifi_set_opt_ie,
+ .sta_set_flags = madwifi_sta_set_flags,
+ .read_sta_data = madwifi_read_sta_driver_data,
+ .hapd_send_eapol = madwifi_send_eapol,
+ .sta_disassoc = madwifi_sta_disassoc,
+ .sta_deauth = madwifi_sta_deauth,
+ .hapd_set_ssid = madwifi_set_ssid,
+ .hapd_get_ssid = madwifi_get_ssid,
+ .hapd_set_countermeasures = madwifi_set_countermeasures,
+ .sta_clear_stats = madwifi_sta_clear_stats,
+ .commit = madwifi_commit,
+ .set_ap_wps_ie = madwifi_set_ap_wps_ie,
+#else /* HOSTAPD */
+ .get_bssid = wpa_driver_madwifi_get_bssid,
+ .get_ssid = wpa_driver_madwifi_get_ssid,
+ .init = wpa_driver_madwifi_init,
+ .deinit = wpa_driver_madwifi_deinit,
+ .set_countermeasures = wpa_driver_madwifi_set_countermeasures,
+ .scan2 = wpa_driver_madwifi_scan,
+ .get_scan_results2 = wpa_driver_madwifi_get_scan_results,
+ .deauthenticate = wpa_driver_madwifi_deauthenticate,
+ .disassociate = wpa_driver_madwifi_disassociate,
+ .associate = wpa_driver_madwifi_associate,
+ .set_operstate = wpa_driver_madwifi_set_operstate,
+#endif /* HOSTAPD */
+};
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
new file mode 100644
index 0000000..aeb7304
--- /dev/null
+++ b/src/drivers/driver_ndis.c
@@ -0,0 +1,3331 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifdef __CYGWIN__
+/* Avoid some header file conflicts by not including standard headers for
+ * cygwin builds when Packet32.h is included. */
+#include "build_config.h"
+int close(int fd);
+#else /* __CYGWIN__ */
+#include "includes.h"
+#endif /* __CYGWIN__ */
+#ifdef CONFIG_USE_NDISUIO
+#include <winsock2.h>
+#else /* CONFIG_USE_NDISUIO */
+#include <Packet32.h>
+#endif /* CONFIG_USE_NDISUIO */
+#ifdef __MINGW32_VERSION
+#include <ddk/ntddndis.h>
+#else /* __MINGW32_VERSION */
+#include <ntddndis.h>
+#endif /* __MINGW32_VERSION */
+
+#ifdef _WIN32_WCE
+#include <winioctl.h>
+#include <nuiouser.h>
+#include <devload.h>
+#endif /* _WIN32_WCE */
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "driver_ndis.h"
+
+int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv);
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+static void wpa_driver_ndis_deinit(void *priv);
+static void wpa_driver_ndis_poll(void *drv);
+static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv);
+static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv);
+static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv);
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+/* FIX: to be removed once this can be compiled with the complete NDIS
+ * header files */
+#ifndef OID_802_11_BSSID
+#define OID_802_11_BSSID 0x0d010101
+#define OID_802_11_SSID 0x0d010102
+#define OID_802_11_INFRASTRUCTURE_MODE 0x0d010108
+#define OID_802_11_ADD_WEP 0x0D010113
+#define OID_802_11_REMOVE_WEP 0x0D010114
+#define OID_802_11_DISASSOCIATE 0x0D010115
+#define OID_802_11_BSSID_LIST 0x0d010217
+#define OID_802_11_AUTHENTICATION_MODE 0x0d010118
+#define OID_802_11_PRIVACY_FILTER 0x0d010119
+#define OID_802_11_BSSID_LIST_SCAN 0x0d01011A
+#define OID_802_11_WEP_STATUS 0x0d01011B
+#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS
+#define OID_802_11_ADD_KEY 0x0d01011D
+#define OID_802_11_REMOVE_KEY 0x0d01011E
+#define OID_802_11_ASSOCIATION_INFORMATION 0x0d01011F
+#define OID_802_11_TEST 0x0d010120
+#define OID_802_11_CAPABILITY 0x0d010122
+#define OID_802_11_PMKID 0x0d010123
+
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+
+typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
+
+typedef struct NDIS_802_11_SSID {
+ ULONG SsidLength;
+ UCHAR Ssid[NDIS_802_11_LENGTH_SSID];
+} NDIS_802_11_SSID;
+
+typedef LONG NDIS_802_11_RSSI;
+
+typedef enum NDIS_802_11_NETWORK_TYPE {
+ Ndis802_11FH,
+ Ndis802_11DS,
+ Ndis802_11OFDM5,
+ Ndis802_11OFDM24,
+ Ndis802_11NetworkTypeMax
+} NDIS_802_11_NETWORK_TYPE;
+
+typedef struct NDIS_802_11_CONFIGURATION_FH {
+ ULONG Length;
+ ULONG HopPattern;
+ ULONG HopSet;
+ ULONG DwellTime;
+} NDIS_802_11_CONFIGURATION_FH;
+
+typedef struct NDIS_802_11_CONFIGURATION {
+ ULONG Length;
+ ULONG BeaconPeriod;
+ ULONG ATIMWindow;
+ ULONG DSConfig;
+ NDIS_802_11_CONFIGURATION_FH FHConfig;
+} NDIS_802_11_CONFIGURATION;
+
+typedef enum NDIS_802_11_NETWORK_INFRASTRUCTURE {
+ Ndis802_11IBSS,
+ Ndis802_11Infrastructure,
+ Ndis802_11AutoUnknown,
+ Ndis802_11InfrastructureMax
+} NDIS_802_11_NETWORK_INFRASTRUCTURE;
+
+typedef enum NDIS_802_11_AUTHENTICATION_MODE {
+ Ndis802_11AuthModeOpen,
+ Ndis802_11AuthModeShared,
+ Ndis802_11AuthModeAutoSwitch,
+ Ndis802_11AuthModeWPA,
+ Ndis802_11AuthModeWPAPSK,
+ Ndis802_11AuthModeWPANone,
+ Ndis802_11AuthModeWPA2,
+ Ndis802_11AuthModeWPA2PSK,
+ Ndis802_11AuthModeMax
+} NDIS_802_11_AUTHENTICATION_MODE;
+
+typedef enum NDIS_802_11_WEP_STATUS {
+ Ndis802_11WEPEnabled,
+ Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+ Ndis802_11WEPDisabled,
+ Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+ Ndis802_11WEPKeyAbsent,
+ Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+ Ndis802_11WEPNotSupported,
+ Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+ Ndis802_11Encryption2Enabled,
+ Ndis802_11Encryption2KeyAbsent,
+ Ndis802_11Encryption3Enabled,
+ Ndis802_11Encryption3KeyAbsent
+} NDIS_802_11_WEP_STATUS, NDIS_802_11_ENCRYPTION_STATUS;
+
+typedef enum NDIS_802_11_PRIVACY_FILTER {
+ Ndis802_11PrivFilterAcceptAll,
+ Ndis802_11PrivFilter8021xWEP
+} NDIS_802_11_PRIVACY_FILTER;
+
+typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES];
+typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX];
+
+typedef struct NDIS_WLAN_BSSID_EX {
+ ULONG Length;
+ NDIS_802_11_MAC_ADDRESS MacAddress; /* BSSID */
+ UCHAR Reserved[2];
+ NDIS_802_11_SSID Ssid;
+ ULONG Privacy;
+ NDIS_802_11_RSSI Rssi;
+ NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+ NDIS_802_11_CONFIGURATION Configuration;
+ NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+ NDIS_802_11_RATES_EX SupportedRates;
+ ULONG IELength;
+ UCHAR IEs[1];
+} NDIS_WLAN_BSSID_EX;
+
+typedef struct NDIS_802_11_BSSID_LIST_EX {
+ ULONG NumberOfItems;
+ NDIS_WLAN_BSSID_EX Bssid[1];
+} NDIS_802_11_BSSID_LIST_EX;
+
+typedef struct NDIS_802_11_FIXED_IEs {
+ UCHAR Timestamp[8];
+ USHORT BeaconInterval;
+ USHORT Capabilities;
+} NDIS_802_11_FIXED_IEs;
+
+typedef struct NDIS_802_11_WEP {
+ ULONG Length;
+ ULONG KeyIndex;
+ ULONG KeyLength;
+ UCHAR KeyMaterial[1];
+} NDIS_802_11_WEP;
+
+typedef ULONG NDIS_802_11_KEY_INDEX;
+typedef ULONGLONG NDIS_802_11_KEY_RSC;
+
+typedef struct NDIS_802_11_KEY {
+ ULONG Length;
+ ULONG KeyIndex;
+ ULONG KeyLength;
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_KEY_RSC KeyRSC;
+ UCHAR KeyMaterial[1];
+} NDIS_802_11_KEY;
+
+typedef struct NDIS_802_11_REMOVE_KEY {
+ ULONG Length;
+ ULONG KeyIndex;
+ NDIS_802_11_MAC_ADDRESS BSSID;
+} NDIS_802_11_REMOVE_KEY;
+
+typedef struct NDIS_802_11_AI_REQFI {
+ USHORT Capabilities;
+ USHORT ListenInterval;
+ NDIS_802_11_MAC_ADDRESS CurrentAPAddress;
+} NDIS_802_11_AI_REQFI;
+
+typedef struct NDIS_802_11_AI_RESFI {
+ USHORT Capabilities;
+ USHORT StatusCode;
+ USHORT AssociationId;
+} NDIS_802_11_AI_RESFI;
+
+typedef struct NDIS_802_11_ASSOCIATION_INFORMATION {
+ ULONG Length;
+ USHORT AvailableRequestFixedIEs;
+ NDIS_802_11_AI_REQFI RequestFixedIEs;
+ ULONG RequestIELength;
+ ULONG OffsetRequestIEs;
+ USHORT AvailableResponseFixedIEs;
+ NDIS_802_11_AI_RESFI ResponseFixedIEs;
+ ULONG ResponseIELength;
+ ULONG OffsetResponseIEs;
+} NDIS_802_11_ASSOCIATION_INFORMATION;
+
+typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
+ NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
+ NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
+} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
+
+typedef struct NDIS_802_11_CAPABILITY {
+ ULONG Length;
+ ULONG Version;
+ ULONG NoOfPMKIDs;
+ ULONG NoOfAuthEncryptPairsSupported;
+ NDIS_802_11_AUTHENTICATION_ENCRYPTION
+ AuthenticationEncryptionSupported[1];
+} NDIS_802_11_CAPABILITY;
+
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct BSSID_INFO {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO;
+
+typedef struct NDIS_802_11_PMKID {
+ ULONG Length;
+ ULONG BSSIDInfoCount;
+ BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID;
+
+typedef enum NDIS_802_11_STATUS_TYPE {
+ Ndis802_11StatusType_Authentication,
+ Ndis802_11StatusType_PMKID_CandidateList = 2,
+ Ndis802_11StatusTypeMax
+} NDIS_802_11_STATUS_TYPE;
+
+typedef struct NDIS_802_11_STATUS_INDICATION {
+ NDIS_802_11_STATUS_TYPE StatusType;
+} NDIS_802_11_STATUS_INDICATION;
+
+typedef struct PMKID_CANDIDATE {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ ULONG Flags;
+} PMKID_CANDIDATE;
+
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
+ ULONG Version;
+ ULONG NumCandidates;
+ PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST;
+
+typedef struct NDIS_802_11_AUTHENTICATION_REQUEST {
+ ULONG Length;
+ NDIS_802_11_MAC_ADDRESS Bssid;
+ ULONG Flags;
+} NDIS_802_11_AUTHENTICATION_REQUEST;
+
+#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01
+#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02
+#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06
+#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E
+
+#endif /* OID_802_11_BSSID */
+
+
+#ifndef OID_802_11_PMKID
+/* Platform SDK for XP did not include WPA2, so add needed definitions */
+
+#define OID_802_11_CAPABILITY 0x0d010122
+#define OID_802_11_PMKID 0x0d010123
+
+#define Ndis802_11AuthModeWPA2 6
+#define Ndis802_11AuthModeWPA2PSK 7
+
+#define Ndis802_11StatusType_PMKID_CandidateList 2
+
+typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
+ NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
+ NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
+} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
+
+typedef struct NDIS_802_11_CAPABILITY {
+ ULONG Length;
+ ULONG Version;
+ ULONG NoOfPMKIDs;
+ ULONG NoOfAuthEncryptPairsSupported;
+ NDIS_802_11_AUTHENTICATION_ENCRYPTION
+ AuthenticationEncryptionSupported[1];
+} NDIS_802_11_CAPABILITY;
+
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct BSSID_INFO {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO;
+
+typedef struct NDIS_802_11_PMKID {
+ ULONG Length;
+ ULONG BSSIDInfoCount;
+ BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID;
+
+typedef struct PMKID_CANDIDATE {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ ULONG Flags;
+} PMKID_CANDIDATE;
+
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
+ ULONG Version;
+ ULONG NumCandidates;
+ PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST;
+
+#endif /* OID_802_11_CAPABILITY */
+
+
+#ifndef OID_DOT11_CURRENT_OPERATION_MODE
+/* Native 802.11 OIDs */
+#define OID_DOT11_NDIS_START 0x0D010300
+#define OID_DOT11_CURRENT_OPERATION_MODE (OID_DOT11_NDIS_START + 8)
+#define OID_DOT11_SCAN_REQUEST (OID_DOT11_NDIS_START + 11)
+
+typedef enum _DOT11_BSS_TYPE {
+ dot11_BSS_type_infrastructure = 1,
+ dot11_BSS_type_independent = 2,
+ dot11_BSS_type_any = 3
+} DOT11_BSS_TYPE, * PDOT11_BSS_TYPE;
+
+typedef UCHAR DOT11_MAC_ADDRESS[6];
+typedef DOT11_MAC_ADDRESS * PDOT11_MAC_ADDRESS;
+
+typedef enum _DOT11_SCAN_TYPE {
+ dot11_scan_type_active = 1,
+ dot11_scan_type_passive = 2,
+ dot11_scan_type_auto = 3,
+ dot11_scan_type_forced = 0x80000000
+} DOT11_SCAN_TYPE, * PDOT11_SCAN_TYPE;
+
+typedef struct _DOT11_SCAN_REQUEST_V2 {
+ DOT11_BSS_TYPE dot11BSSType;
+ DOT11_MAC_ADDRESS dot11BSSID;
+ DOT11_SCAN_TYPE dot11ScanType;
+ BOOLEAN bRestrictedScan;
+ ULONG udot11SSIDsOffset;
+ ULONG uNumOfdot11SSIDs;
+ BOOLEAN bUseRequestIE;
+ ULONG uRequestIDsOffset;
+ ULONG uNumOfRequestIDs;
+ ULONG uPhyTypeInfosOffset;
+ ULONG uNumOfPhyTypeInfos;
+ ULONG uIEsOffset;
+ ULONG uIEsLength;
+ UCHAR ucBuffer[1];
+} DOT11_SCAN_REQUEST_V2, * PDOT11_SCAN_REQUEST_V2;
+
+#endif /* OID_DOT11_CURRENT_OPERATION_MODE */
+
+#ifdef CONFIG_USE_NDISUIO
+#ifndef _WIN32_WCE
+#ifdef __MINGW32_VERSION
+typedef ULONG NDIS_OID;
+#endif /* __MINGW32_VERSION */
+/* from nuiouser.h */
+#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK
+
+#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
+ CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
+
+#define IOCTL_NDISUIO_OPEN_DEVICE \
+ _NDISUIO_CTL_CODE(0x200, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_QUERY_OID_VALUE \
+ _NDISUIO_CTL_CODE(0x201, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_SET_OID_VALUE \
+ _NDISUIO_CTL_CODE(0x205, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_SET_ETHER_TYPE \
+ _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_QUERY_BINDING \
+ _NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_BIND_WAIT \
+ _NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+typedef struct _NDISUIO_QUERY_OID
+{
+ NDIS_OID Oid;
+ UCHAR Data[sizeof(ULONG)];
+} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
+
+typedef struct _NDISUIO_SET_OID
+{
+ NDIS_OID Oid;
+ UCHAR Data[sizeof(ULONG)];
+} NDISUIO_SET_OID, *PNDISUIO_SET_OID;
+
+typedef struct _NDISUIO_QUERY_BINDING
+{
+ ULONG BindingIndex;
+ ULONG DeviceNameOffset;
+ ULONG DeviceNameLength;
+ ULONG DeviceDescrOffset;
+ ULONG DeviceDescrLength;
+} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING;
+#endif /* _WIN32_WCE */
+#endif /* CONFIG_USE_NDISUIO */
+
+
+static int ndis_get_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
+ char *data, size_t len)
+{
+#ifdef CONFIG_USE_NDISUIO
+ NDISUIO_QUERY_OID *o;
+ size_t buflen = sizeof(*o) + len;
+ DWORD written;
+ int ret;
+ size_t hdrlen;
+
+ o = os_zalloc(buflen);
+ if (o == NULL)
+ return -1;
+ o->Oid = oid;
+#ifdef _WIN32_WCE
+ o->ptcDeviceName = drv->adapter_name;
+#endif /* _WIN32_WCE */
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_OID_VALUE,
+ o, sizeof(NDISUIO_QUERY_OID), o, buflen, &written,
+ NULL)) {
+ wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_QUERY_OID_VALUE "
+ "failed (oid=%08x): %d", oid, (int) GetLastError());
+ os_free(o);
+ return -1;
+ }
+ hdrlen = sizeof(NDISUIO_QUERY_OID) - sizeof(o->Data);
+ if (written < hdrlen) {
+ wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d); "
+ "too short", oid, (unsigned int) written);
+ os_free(o);
+ return -1;
+ }
+ written -= hdrlen;
+ if (written > len) {
+ wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d) > "
+ "len (%d)",oid, (unsigned int) written, len);
+ os_free(o);
+ return -1;
+ }
+ os_memcpy(data, o->Data, written);
+ ret = written;
+ os_free(o);
+ return ret;
+#else /* CONFIG_USE_NDISUIO */
+ char *buf;
+ PACKET_OID_DATA *o;
+ int ret;
+
+ buf = os_zalloc(sizeof(*o) + len);
+ if (buf == NULL)
+ return -1;
+ o = (PACKET_OID_DATA *) buf;
+ o->Oid = oid;
+ o->Length = len;
+
+ if (!PacketRequest(drv->adapter, FALSE, o)) {
+ wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+ __func__, oid, len);
+ os_free(buf);
+ return -1;
+ }
+ if (o->Length > len) {
+ wpa_printf(MSG_DEBUG, "%s: oid=0x%x Length (%d) > len (%d)",
+ __func__, oid, (unsigned int) o->Length, len);
+ os_free(buf);
+ return -1;
+ }
+ os_memcpy(data, o->Data, o->Length);
+ ret = o->Length;
+ os_free(buf);
+ return ret;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_set_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
+ const char *data, size_t len)
+{
+#ifdef CONFIG_USE_NDISUIO
+ NDISUIO_SET_OID *o;
+ size_t buflen, reallen;
+ DWORD written;
+ char txt[50];
+
+ os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
+ wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
+
+ buflen = sizeof(*o) + len;
+ reallen = buflen - sizeof(o->Data);
+ o = os_zalloc(buflen);
+ if (o == NULL)
+ return -1;
+ o->Oid = oid;
+#ifdef _WIN32_WCE
+ o->ptcDeviceName = drv->adapter_name;
+#endif /* _WIN32_WCE */
+ if (data)
+ os_memcpy(o->Data, data, len);
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_SET_OID_VALUE,
+ o, reallen, NULL, 0, &written, NULL)) {
+ wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_SET_OID_VALUE "
+ "(oid=%08x) failed: %d", oid, (int) GetLastError());
+ os_free(o);
+ return -1;
+ }
+ os_free(o);
+ return 0;
+#else /* CONFIG_USE_NDISUIO */
+ char *buf;
+ PACKET_OID_DATA *o;
+ char txt[50];
+
+ os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
+ wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
+
+ buf = os_zalloc(sizeof(*o) + len);
+ if (buf == NULL)
+ return -1;
+ o = (PACKET_OID_DATA *) buf;
+ o->Oid = oid;
+ o->Length = len;
+ if (data)
+ os_memcpy(o->Data, data, len);
+
+ if (!PacketRequest(drv->adapter, TRUE, o)) {
+ wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+ __func__, oid, len);
+ os_free(buf);
+ return -1;
+ }
+ os_free(buf);
+ return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_set_auth_mode(struct wpa_driver_ndis_data *drv, int mode)
+{
+ u32 auth_mode = mode;
+ if (ndis_set_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+ (char *) &auth_mode, sizeof(auth_mode)) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+ "OID_802_11_AUTHENTICATION_MODE (%d)",
+ (int) auth_mode);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int ndis_get_auth_mode(struct wpa_driver_ndis_data *drv)
+{
+ u32 auth_mode;
+ int res;
+ res = ndis_get_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+ (char *) &auth_mode, sizeof(auth_mode));
+ if (res != sizeof(auth_mode)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
+ "OID_802_11_AUTHENTICATION_MODE");
+ return -1;
+ }
+ return auth_mode;
+}
+
+
+static int ndis_set_encr_status(struct wpa_driver_ndis_data *drv, int encr)
+{
+ u32 encr_status = encr;
+ if (ndis_set_oid(drv, OID_802_11_ENCRYPTION_STATUS,
+ (char *) &encr_status, sizeof(encr_status)) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+ "OID_802_11_ENCRYPTION_STATUS (%d)", encr);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int ndis_get_encr_status(struct wpa_driver_ndis_data *drv)
+{
+ u32 encr;
+ int res;
+ res = ndis_get_oid(drv, OID_802_11_ENCRYPTION_STATUS,
+ (char *) &encr, sizeof(encr));
+ if (res != sizeof(encr)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
+ "OID_802_11_ENCRYPTION_STATUS");
+ return -1;
+ }
+ return encr;
+}
+
+
+static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+
+ if (drv->wired) {
+ /*
+ * Report PAE group address as the "BSSID" for wired
+ * connection.
+ */
+ os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+ return 0;
+ }
+
+ return ndis_get_oid(drv, OID_802_11_BSSID, (char *) bssid, ETH_ALEN) <
+ 0 ? -1 : 0;
+}
+
+
+static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ NDIS_802_11_SSID buf;
+ int res;
+
+ res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
+ if (res < 4) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID");
+ if (drv->wired) {
+ wpa_printf(MSG_DEBUG, "NDIS: Allow get_ssid failure "
+ "with a wired interface");
+ return 0;
+ }
+ return -1;
+ }
+ os_memcpy(ssid, buf.Ssid, buf.SsidLength);
+ return buf.SsidLength;
+}
+
+
+static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv,
+ const u8 *ssid, size_t ssid_len)
+{
+ NDIS_802_11_SSID buf;
+
+ os_memset(&buf, 0, sizeof(buf));
+ buf.SsidLength = ssid_len;
+ os_memcpy(buf.Ssid, ssid, ssid_len);
+ /*
+ * Make sure radio is marked enabled here so that scan request will not
+ * force SSID to be changed to a random one in order to enable radio at
+ * that point.
+ */
+ drv->radio_enabled = 1;
+ return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
+}
+
+
+/* Disconnect using OID_802_11_DISASSOCIATE. This will also turn the radio off.
+ */
+static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv)
+{
+ drv->radio_enabled = 0;
+ return ndis_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4);
+}
+
+
+/* Disconnect by setting SSID to random (i.e., likely not used). */
+static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv)
+{
+ char ssid[32];
+ int i;
+ for (i = 0; i < 32; i++)
+ ssid[i] = rand() & 0xff;
+ return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, 32);
+}
+
+
+static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ return wpa_driver_ndis_disconnect(drv);
+}
+
+
+static int wpa_driver_ndis_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ return wpa_driver_ndis_disconnect(drv);
+}
+
+
+static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static int wpa_driver_ndis_scan_native80211(
+ struct wpa_driver_ndis_data *drv,
+ struct wpa_driver_scan_params *params)
+{
+ DOT11_SCAN_REQUEST_V2 req;
+ int res;
+
+ os_memset(&req, 0, sizeof(req));
+ req.dot11BSSType = dot11_BSS_type_any;
+ os_memset(req.dot11BSSID, 0xff, ETH_ALEN);
+ req.dot11ScanType = dot11_scan_type_auto;
+ res = ndis_set_oid(drv, OID_DOT11_SCAN_REQUEST, (char *) &req,
+ sizeof(req));
+ eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
+ drv->ctx);
+ return res;
+}
+
+
+static int wpa_driver_ndis_scan(void *priv,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ int res;
+
+ if (drv->native80211)
+ return wpa_driver_ndis_scan_native80211(drv, params);
+
+ if (!drv->radio_enabled) {
+ wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first"
+ " scan");
+ if (wpa_driver_ndis_disconnect(drv) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio");
+ }
+ drv->radio_enabled = 1;
+ }
+
+ res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, " ", 4);
+ eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
+ drv->ctx);
+ return res;
+}
+
+
+static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
+{
+ const u8 *end, *pos;
+
+ pos = (const u8 *) (res + 1);
+ end = pos + res->ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == ie)
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+
+static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid(
+ struct wpa_scan_res *r, NDIS_802_11_SSID *ssid)
+{
+ struct wpa_scan_res *nr;
+ u8 *pos;
+
+ if (wpa_scan_get_ie(r, WLAN_EID_SSID))
+ return r; /* SSID IE already present */
+
+ if (ssid->SsidLength == 0 || ssid->SsidLength > 32)
+ return r; /* No valid SSID inside scan data */
+
+ nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength);
+ if (nr == NULL)
+ return r;
+
+ pos = ((u8 *) (nr + 1)) + nr->ie_len;
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = ssid->SsidLength;
+ os_memcpy(pos, ssid->Ssid, ssid->SsidLength);
+ nr->ie_len += 2 + ssid->SsidLength;
+
+ return nr;
+}
+
+
+static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ NDIS_802_11_BSSID_LIST_EX *b;
+ size_t blen, count, i;
+ int len;
+ char *pos;
+ struct wpa_scan_results *results;
+ struct wpa_scan_res *r;
+
+ blen = 65535;
+ b = os_zalloc(blen);
+ if (b == NULL)
+ return NULL;
+ len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
+ if (len < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
+ os_free(b);
+ return NULL;
+ }
+ count = b->NumberOfItems;
+
+ results = os_zalloc(sizeof(*results));
+ if (results == NULL) {
+ os_free(b);
+ return NULL;
+ }
+ results->res = os_zalloc(count * sizeof(struct wpa_scan_res *));
+ if (results->res == NULL) {
+ os_free(results);
+ os_free(b);
+ return NULL;
+ }
+
+ pos = (char *) &b->Bssid[0];
+ for (i = 0; i < count; i++) {
+ NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
+ NDIS_802_11_FIXED_IEs *fixed;
+
+ if (bss->IELength < sizeof(NDIS_802_11_FIXED_IEs)) {
+ wpa_printf(MSG_DEBUG, "NDIS: too small IELength=%d",
+ (int) bss->IELength);
+ break;
+ }
+ if (((char *) bss->IEs) + bss->IELength > (char *) b + blen) {
+ /*
+ * Some NDIS drivers have been reported to include an
+ * entry with an invalid IELength in scan results and
+ * this has crashed wpa_supplicant, so validate the
+ * returned value before using it.
+ */
+ wpa_printf(MSG_DEBUG, "NDIS: skipped invalid scan "
+ "result IE (BSSID=" MACSTR ") IELength=%d",
+ MAC2STR(bss->MacAddress),
+ (int) bss->IELength);
+ break;
+ }
+
+ r = os_zalloc(sizeof(*r) + bss->IELength -
+ sizeof(NDIS_802_11_FIXED_IEs));
+ if (r == NULL)
+ break;
+
+ os_memcpy(r->bssid, bss->MacAddress, ETH_ALEN);
+ r->level = (int) bss->Rssi;
+ r->freq = bss->Configuration.DSConfig / 1000;
+ fixed = (NDIS_802_11_FIXED_IEs *) bss->IEs;
+ r->beacon_int = WPA_GET_LE16((u8 *) &fixed->BeaconInterval);
+ r->caps = WPA_GET_LE16((u8 *) &fixed->Capabilities);
+ r->tsf = WPA_GET_LE64(fixed->Timestamp);
+ os_memcpy(r + 1, bss->IEs + sizeof(NDIS_802_11_FIXED_IEs),
+ bss->IELength - sizeof(NDIS_802_11_FIXED_IEs));
+ r->ie_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+ r = wpa_driver_ndis_add_scan_ssid(r, &bss->Ssid);
+
+ results->res[results->num++] = r;
+
+ pos += bss->Length;
+ if (pos > (char *) b + blen)
+ break;
+ }
+
+ os_free(b);
+
+ return results;
+}
+
+
+static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv,
+ int key_idx, const u8 *addr,
+ const u8 *bssid, int pairwise)
+{
+ NDIS_802_11_REMOVE_KEY rkey;
+ NDIS_802_11_KEY_INDEX index;
+ int res, res2;
+
+ os_memset(&rkey, 0, sizeof(rkey));
+
+ rkey.Length = sizeof(rkey);
+ rkey.KeyIndex = key_idx;
+ if (pairwise)
+ rkey.KeyIndex |= 1 << 30;
+ os_memcpy(rkey.BSSID, bssid, ETH_ALEN);
+
+ res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey,
+ sizeof(rkey));
+ if (!pairwise) {
+ index = key_idx;
+ res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP,
+ (char *) &index, sizeof(index));
+ } else
+ res2 = 0;
+
+ if (res < 0 && res2 < 0)
+ return -1;
+ return 0;
+}
+
+
+static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv,
+ int pairwise, int key_idx, int set_tx,
+ const u8 *key, size_t key_len)
+{
+ NDIS_802_11_WEP *wep;
+ size_t len;
+ int res;
+
+ len = 12 + key_len;
+ wep = os_zalloc(len);
+ if (wep == NULL)
+ return -1;
+ wep->Length = len;
+ wep->KeyIndex = key_idx;
+ if (set_tx)
+ wep->KeyIndex |= 1 << 31;
+#if 0 /* Setting bit30 does not seem to work with some NDIS drivers */
+ if (pairwise)
+ wep->KeyIndex |= 1 << 30;
+#endif
+ wep->KeyLength = key_len;
+ os_memcpy(wep->KeyMaterial, key, key_len);
+
+ wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_WEP",
+ (u8 *) wep, len);
+ res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len);
+
+ os_free(wep);
+
+ return res;
+}
+
+
+static int wpa_driver_ndis_set_key(const char *ifname, void *priv,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ size_t len, i;
+ NDIS_802_11_KEY *nkey;
+ int res, pairwise;
+ u8 bssid[ETH_ALEN];
+
+ if (addr == NULL || 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 = os_malloc(dlen + 1);
+ if (drv->adapter_desc) {
+ os_memcpy(drv->adapter_desc, desc, dlen);
+ drv->adapter_desc[dlen] = '\0';
+ }
+
+ os_free(b);
+
+ if (drv->adapter_desc == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
+ drv->adapter_desc);
+
+ return 0;
+#else /* CONFIG_USE_NDISUIO */
+ PTSTR _names;
+ char *names, *pos, *pos2;
+ ULONG len;
+ BOOLEAN res;
+#define MAX_ADAPTERS 32
+ char *name[MAX_ADAPTERS];
+ char *desc[MAX_ADAPTERS];
+ int num_name, num_desc, i, found_name, found_desc;
+ size_t dlen;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
+ PacketGetVersion());
+
+ len = 8192;
+ _names = os_zalloc(len);
+ if (_names == NULL)
+ return -1;
+
+ res = PacketGetAdapterNames(_names, &len);
+ if (!res && len > 8192) {
+ os_free(_names);
+ _names = os_zalloc(len);
+ if (_names == NULL)
+ return -1;
+ res = PacketGetAdapterNames(_names, &len);
+ }
+
+ if (!res) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
+ "(PacketGetAdapterNames)");
+ os_free(_names);
+ return -1;
+ }
+
+ names = (char *) _names;
+ if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
+ "UNICODE");
+ /* Convert to ASCII */
+ pos2 = pos = names;
+ while (pos2 < names + len) {
+ if (pos2[0] == '\0' && pos2[1] == '\0' &&
+ pos2[2] == '\0' && pos2[3] == '\0') {
+ pos2 += 4;
+ break;
+ }
+ *pos++ = pos2[0];
+ pos2 += 2;
+ }
+ os_memcpy(pos + 2, names, pos - names);
+ pos += 2;
+ } else
+ pos = names;
+
+ num_name = 0;
+ while (pos < names + len) {
+ name[num_name] = pos;
+ while (*pos && pos < names + len)
+ pos++;
+ if (pos + 1 >= names + len) {
+ os_free(names);
+ return -1;
+ }
+ pos++;
+ num_name++;
+ if (num_name >= MAX_ADAPTERS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
+ os_free(names);
+ return -1;
+ }
+ if (*pos == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
+ num_name);
+ pos++;
+ break;
+ }
+ }
+
+ num_desc = 0;
+ while (pos < names + len) {
+ desc[num_desc] = pos;
+ while (*pos && pos < names + len)
+ pos++;
+ if (pos + 1 >= names + len) {
+ os_free(names);
+ return -1;
+ }
+ pos++;
+ num_desc++;
+ if (num_desc >= MAX_ADAPTERS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
+ "descriptions");
+ os_free(names);
+ return -1;
+ }
+ if (*pos == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
+ "found", num_name);
+ pos++;
+ break;
+ }
+ }
+
+ /*
+ * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
+ * descriptions. Fill in dummy descriptors to work around this.
+ */
+ while (num_desc < num_name)
+ desc[num_desc++] = "dummy description";
+
+ if (num_name != num_desc) {
+ wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
+ "description counts (%d != %d)",
+ num_name, num_desc);
+ os_free(names);
+ return -1;
+ }
+
+ found_name = found_desc = -1;
+ for (i = 0; i < num_name; i++) {
+ wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s",
+ i, name[i], desc[i]);
+ if (found_name == -1 && os_strstr(name[i], drv->ifname))
+ found_name = i;
+ if (found_desc == -1 &&
+ os_strncmp(desc[i], drv->ifname, os_strlen(drv->ifname)) ==
+ 0)
+ found_desc = i;
+ }
+
+ if (found_name < 0 && found_desc >= 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on "
+ "description '%s'",
+ name[found_desc], desc[found_desc]);
+ found_name = found_desc;
+ os_strlcpy(drv->ifname,
+ os_strncmp(name[found_desc], "\\Device\\NPF_", 12)
+ == 0 ? name[found_desc] + 12 : name[found_desc],
+ sizeof(drv->ifname));
+ }
+
+ if (found_name < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
+ drv->ifname);
+ os_free(names);
+ return -1;
+ }
+
+ i = found_name;
+ pos = os_strrchr(desc[i], '(');
+ if (pos) {
+ dlen = pos - desc[i];
+ pos--;
+ if (pos > desc[i] && *pos == ' ')
+ dlen--;
+ } else {
+ dlen = os_strlen(desc[i]);
+ }
+ drv->adapter_desc = os_malloc(dlen + 1);
+ if (drv->adapter_desc) {
+ os_memcpy(drv->adapter_desc, desc[i], dlen);
+ drv->adapter_desc[dlen] = '\0';
+ }
+
+ os_free(names);
+
+ if (drv->adapter_desc == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
+ drv->adapter_desc);
+
+ return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+#if defined(CONFIG_NATIVE_WINDOWS) || defined(__CYGWIN__)
+#ifndef _WIN32_WCE
+/*
+ * These structures are undocumented for WinXP; only WinCE version is
+ * documented. These would be included wzcsapi.h if it were available. Some
+ * changes here have been needed to make the structures match with WinXP SP2.
+ * It is unclear whether these work with any other version.
+ */
+
+typedef struct {
+ LPWSTR wszGuid;
+} INTF_KEY_ENTRY, *PINTF_KEY_ENTRY;
+
+typedef struct {
+ DWORD dwNumIntfs;
+ PINTF_KEY_ENTRY pIntfs;
+} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE;
+
+typedef struct {
+ DWORD dwDataLen;
+ LPBYTE pData;
+} RAW_DATA, *PRAW_DATA;
+
+typedef struct {
+ LPWSTR wszGuid;
+ LPWSTR wszDescr;
+ ULONG ulMediaState;
+ ULONG ulMediaType;
+ ULONG ulPhysicalMediaType;
+ INT nInfraMode;
+ INT nAuthMode;
+ INT nWepStatus;
+#ifndef _WIN32_WCE
+ u8 pad[2]; /* why is this needed? */
+#endif /* _WIN32_WCE */
+ DWORD dwCtlFlags;
+ DWORD dwCapabilities; /* something added for WinXP SP2(?) */
+ RAW_DATA rdSSID;
+ RAW_DATA rdBSSID;
+ RAW_DATA rdBSSIDList;
+ RAW_DATA rdStSSIDList;
+ RAW_DATA rdCtrlData;
+#ifdef UNDER_CE
+ BOOL bInitialized;
+#endif
+ DWORD nWPAMCastCipher;
+ /* add some extra buffer for later additions since this interface is
+ * far from stable */
+ u8 later_additions[100];
+} INTF_ENTRY, *PINTF_ENTRY;
+
+#define INTF_ALL 0xffffffff
+#define INTF_ALL_FLAGS 0x0000ffff
+#define INTF_CTLFLAGS 0x00000010
+#define INTFCTL_ENABLED 0x8000
+#endif /* _WIN32_WCE */
+
+
+#ifdef _WIN32_WCE
+static int wpa_driver_ndis_rebind_adapter(struct wpa_driver_ndis_data *drv)
+{
+ HANDLE ndis;
+ TCHAR multi[100];
+ int len;
+
+ len = _tcslen(drv->adapter_name);
+ if (len > 80)
+ return -1;
+
+ ndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (ndis == INVALID_HANDLE_VALUE) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to open file to NDIS "
+ "device: %d", (int) GetLastError());
+ return -1;
+ }
+
+ len++;
+ memcpy(multi, drv->adapter_name, len * sizeof(TCHAR));
+ memcpy(&multi[len], TEXT("NDISUIO\0"), 9 * sizeof(TCHAR));
+ len += 9;
+
+ if (!DeviceIoControl(ndis, IOCTL_NDIS_REBIND_ADAPTER,
+ multi, len * sizeof(TCHAR), NULL, 0, NULL, NULL))
+ {
+ wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDIS_REBIND_ADAPTER "
+ "failed: 0x%x", (int) GetLastError());
+ wpa_hexdump_ascii(MSG_DEBUG, "NDIS: rebind multi_sz",
+ (u8 *) multi, len * sizeof(TCHAR));
+ CloseHandle(ndis);
+ return -1;
+ }
+
+ CloseHandle(ndis);
+
+ wpa_printf(MSG_DEBUG, "NDIS: Requested NDIS rebind of NDISUIO "
+ "protocol");
+
+ return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
+ int enable)
+{
+#ifdef _WIN32_WCE
+ HKEY hk, hk2;
+ LONG ret;
+ DWORD i, hnd, len;
+ TCHAR keyname[256], devname[256];
+
+#define WZC_DRIVER TEXT("Drivers\\BuiltIn\\ZeroConfig")
+
+ if (enable) {
+ HANDLE h;
+ h = ActivateDeviceEx(WZC_DRIVER, NULL, 0, NULL);
+ if (h == INVALID_HANDLE_VALUE || h == 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to re-enable WZC "
+ "- ActivateDeviceEx failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: WZC re-enabled");
+ return wpa_driver_ndis_rebind_adapter(drv);
+ }
+
+ /*
+ * Unfortunately, just disabling the WZC for an interface is not enough
+ * to free NDISUIO for us, so need to disable and unload WZC completely
+ * for now when using WinCE with NDISUIO. In addition, must request
+ * NDISUIO protocol to be rebound to the adapter in order to free the
+ * NDISUIO binding that WZC hold before us.
+ */
+
+ /* Enumerate HKLM\Drivers\Active\* to find a handle to WZC. */
+ ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVLOAD_ACTIVE_KEY, 0, 0, &hk);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(DEVLOAD_ACTIVE_KEY) "
+ "failed: %d %d", (int) ret, (int) GetLastError());
+ return -1;
+ }
+
+ for (i = 0; ; i++) {
+ len = sizeof(keyname);
+ ret = RegEnumKeyEx(hk, i, keyname, &len, NULL, NULL, NULL,
+ NULL);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Could not find active "
+ "WZC - assuming it is not running.");
+ RegCloseKey(hk);
+ return -1;
+ }
+
+ ret = RegOpenKeyEx(hk, keyname, 0, 0, &hk2);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(active dev) "
+ "failed: %d %d",
+ (int) ret, (int) GetLastError());
+ continue;
+ }
+
+ len = sizeof(devname);
+ ret = RegQueryValueEx(hk2, DEVLOAD_DEVKEY_VALNAME, NULL, NULL,
+ (LPBYTE) devname, &len);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx("
+ "DEVKEY_VALNAME) failed: %d %d",
+ (int) ret, (int) GetLastError());
+ RegCloseKey(hk2);
+ continue;
+ }
+
+ if (_tcscmp(devname, WZC_DRIVER) == 0)
+ break;
+
+ RegCloseKey(hk2);
+ }
+
+ RegCloseKey(hk);
+
+ /* Found WZC - get handle to it. */
+ len = sizeof(hnd);
+ ret = RegQueryValueEx(hk2, DEVLOAD_HANDLE_VALNAME, NULL, NULL,
+ (PUCHAR) &hnd, &len);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(HANDLE_VALNAME) "
+ "failed: %d %d", (int) ret, (int) GetLastError());
+ RegCloseKey(hk2);
+ return -1;
+ }
+
+ RegCloseKey(hk2);
+
+ /* Deactivate WZC */
+ if (!DeactivateDevice((HANDLE) hnd)) {
+ wpa_printf(MSG_DEBUG, "NDIS: DeactivateDevice failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily");
+ drv->wzc_disabled = 1;
+ return wpa_driver_ndis_rebind_adapter(drv);
+
+#else /* _WIN32_WCE */
+
+ HMODULE hm;
+ DWORD (WINAPI *wzc_enum_interf)(LPWSTR pSrvAddr,
+ PINTFS_KEY_TABLE pIntfs);
+ DWORD (WINAPI *wzc_query_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
+ PINTF_ENTRY pIntf,
+ LPDWORD pdwOutFlags);
+ DWORD (WINAPI *wzc_set_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
+ PINTF_ENTRY pIntf, LPDWORD pdwOutFlags);
+ int ret = -1, j;
+ DWORD res;
+ INTFS_KEY_TABLE guids;
+ INTF_ENTRY intf;
+ char guid[128];
+ WCHAR *pos;
+ DWORD flags, i;
+
+ hm = LoadLibrary(TEXT("wzcsapi.dll"));
+ if (hm == NULL) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to load wzcsapi.dll (%u) "
+ "- WZC probably not running",
+ (unsigned int) GetLastError());
+ return -1;
+ }
+
+#ifdef _WIN32_WCE
+ wzc_enum_interf = (void *) GetProcAddressA(hm, "WZCEnumInterfaces");
+ wzc_query_interf = (void *) GetProcAddressA(hm, "WZCQueryInterface");
+ wzc_set_interf = (void *) GetProcAddressA(hm, "WZCSetInterface");
+#else /* _WIN32_WCE */
+ wzc_enum_interf = (void *) GetProcAddress(hm, "WZCEnumInterfaces");
+ wzc_query_interf = (void *) GetProcAddress(hm, "WZCQueryInterface");
+ wzc_set_interf = (void *) GetProcAddress(hm, "WZCSetInterface");
+#endif /* _WIN32_WCE */
+
+ if (wzc_enum_interf == NULL || wzc_query_interf == NULL ||
+ wzc_set_interf == NULL) {
+ wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces, "
+ "WZCQueryInterface, or WZCSetInterface not found "
+ "in wzcsapi.dll");
+ goto fail;
+ }
+
+ os_memset(&guids, 0, sizeof(guids));
+ res = wzc_enum_interf(NULL, &guids);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces failed: %d; "
+ "WZC service is apparently not running",
+ (int) res);
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces: %d interfaces",
+ (int) guids.dwNumIntfs);
+
+ for (i = 0; i < guids.dwNumIntfs; i++) {
+ pos = guids.pIntfs[i].wszGuid;
+ for (j = 0; j < sizeof(guid); j++) {
+ guid[j] = (char) *pos;
+ if (*pos == 0)
+ break;
+ pos++;
+ }
+ guid[sizeof(guid) - 1] = '\0';
+ wpa_printf(MSG_DEBUG, "NDIS: intfs %d GUID '%s'",
+ (int) i, guid);
+ if (os_strstr(drv->ifname, guid) == NULL)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Current interface found from "
+ "WZC");
+ break;
+ }
+
+ if (i >= guids.dwNumIntfs) {
+ wpa_printf(MSG_DEBUG, "NDIS: Current interface not found from "
+ "WZC");
+ goto fail;
+ }
+
+ os_memset(&intf, 0, sizeof(intf));
+ intf.wszGuid = guids.pIntfs[i].wszGuid;
+ /* Set flags to verify that the structure has not changed. */
+ intf.dwCtlFlags = -1;
+ flags = 0;
+ res = wzc_query_interf(NULL, INTFCTL_ENABLED, &intf, &flags);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Could not query flags for the "
+ "WZC interface: %d (0x%x)",
+ (int) res, (int) res);
+ wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+ (unsigned int) GetLastError());
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: WZC interface flags 0x%x dwCtlFlags 0x%x",
+ (int) flags, (int) intf.dwCtlFlags);
+
+ if (intf.dwCtlFlags == -1) {
+ wpa_printf(MSG_DEBUG, "NDIS: Looks like wzcsapi has changed "
+ "again - could not disable WZC");
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: intf",
+ (u8 *) &intf, sizeof(intf));
+ goto fail;
+ }
+
+ if (enable) {
+ if (!(intf.dwCtlFlags & INTFCTL_ENABLED)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Enabling WZC for this "
+ "interface");
+ intf.dwCtlFlags |= INTFCTL_ENABLED;
+ res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
+ &flags);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to enable "
+ "WZC: %d (0x%x)",
+ (int) res, (int) res);
+ wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+ (unsigned int) GetLastError());
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "NDIS: Re-enabled WZC for this "
+ "interface");
+ drv->wzc_disabled = 0;
+ }
+ } else {
+ if (intf.dwCtlFlags & INTFCTL_ENABLED) {
+ wpa_printf(MSG_DEBUG, "NDIS: Disabling WZC for this "
+ "interface");
+ intf.dwCtlFlags &= ~INTFCTL_ENABLED;
+ res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
+ &flags);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to "
+ "disable WZC: %d (0x%x)",
+ (int) res, (int) res);
+ wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+ (unsigned int) GetLastError());
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily "
+ "for this interface");
+ drv->wzc_disabled = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "NDIS: WZC was not enabled for "
+ "this interface");
+ }
+ }
+
+ ret = 0;
+
+fail:
+ FreeLibrary(hm);
+
+ return ret;
+#endif /* _WIN32_WCE */
+}
+
+#else /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
+
+static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
+ int enable)
+{
+ return 0;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
+
+
+#ifdef CONFIG_USE_NDISUIO
+/*
+ * l2_packet_ndis.c is sharing the same handle to NDISUIO, so we must be able
+ * to export this handle. This is somewhat ugly, but there is no better
+ * mechanism available to pass data from driver interface to l2_packet wrapper.
+ */
+static HANDLE driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
+
+HANDLE driver_ndis_get_ndisuio_handle(void)
+{
+ return driver_ndis_ndisuio_handle;
+}
+#endif /* CONFIG_USE_NDISUIO */
+
+
+static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+#ifndef _WIN32_WCE
+#define NDISUIO_DEVICE_NAME TEXT("\\\\.\\\\Ndisuio")
+ DWORD written;
+#endif /* _WIN32_WCE */
+ drv->ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ INVALID_HANDLE_VALUE);
+ if (drv->ndisuio == INVALID_HANDLE_VALUE) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
+ "NDISUIO: %d", (int) GetLastError());
+ return -1;
+ }
+ driver_ndis_ndisuio_handle = drv->ndisuio;
+
+#ifndef _WIN32_WCE
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
+ NULL, 0, &written, NULL)) {
+ wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
+ "%d", (int) GetLastError());
+ CloseHandle(drv->ndisuio);
+ drv->ndisuio = INVALID_HANDLE_VALUE;
+ return -1;
+ }
+#endif /* _WIN32_WCE */
+
+ return 0;
+#else /* CONFIG_USE_NDISUIO */
+ return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+ DWORD written;
+#define MAX_NDIS_DEVICE_NAME_LEN 256
+ WCHAR ifname[MAX_NDIS_DEVICE_NAME_LEN];
+ size_t len, i, pos;
+ const char *prefix = "\\DEVICE\\";
+
+#ifdef _WIN32_WCE
+ pos = 0;
+#else /* _WIN32_WCE */
+ pos = 8;
+#endif /* _WIN32_WCE */
+ len = pos + os_strlen(drv->ifname);
+ if (len >= MAX_NDIS_DEVICE_NAME_LEN)
+ return -1;
+ for (i = 0; i < pos; i++)
+ ifname[i] = (WCHAR) prefix[i];
+ for (i = pos; i < len; i++)
+ ifname[i] = (WCHAR) drv->ifname[i - pos];
+ ifname[i] = L'\0';
+
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_OPEN_DEVICE,
+ ifname, len * sizeof(WCHAR), NULL, 0, &written,
+ NULL)) {
+ wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_OPEN_DEVICE "
+ "failed: %d", (int) GetLastError());
+ wpa_hexdump_ascii(MSG_DEBUG, "NDIS: ifname",
+ (const u8 *) ifname, len * sizeof(WCHAR));
+ CloseHandle(drv->ndisuio);
+ drv->ndisuio = INVALID_HANDLE_VALUE;
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: Opened NDISUIO device successfully");
+
+ return 0;
+#else /* CONFIG_USE_NDISUIO */
+ char ifname[128];
+ os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", drv->ifname);
+ drv->adapter = PacketOpenAdapter(ifname);
+ if (drv->adapter == NULL) {
+ wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for "
+ "'%s'", ifname);
+ return -1;
+ }
+ return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+ driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
+ if (drv->ndisuio != INVALID_HANDLE_VALUE)
+ CloseHandle(drv->ndisuio);
+#else /* CONFIG_USE_NDISUIO */
+ if (drv->adapter)
+ PacketCloseAdapter(drv->adapter);
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_add_multicast(struct wpa_driver_ndis_data *drv)
+{
+ if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST,
+ (const char *) pae_group_addr, ETH_ALEN) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address "
+ "to the multicast list");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void * wpa_driver_ndis_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_ndis_data *drv;
+ u32 mode;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->ctx = ctx;
+ /*
+ * Compatibility code to strip possible prefix from the GUID. Previous
+ * versions include \Device\NPF_ prefix for all names, but the internal
+ * interface name is now only the GUI. Both Packet32 and NDISUIO
+ * prefixes are supported.
+ */
+ if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0)
+ ifname += 12;
+ else if (os_strncmp(ifname, "\\DEVICE\\", 8) == 0)
+ ifname += 8;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+ if (wpa_driver_ndis_adapter_init(drv) < 0) {
+ os_free(drv);
+ return NULL;
+ }
+
+ if (wpa_driver_ndis_get_names(drv) < 0) {
+ wpa_driver_ndis_adapter_close(drv);
+ os_free(drv);
+ return NULL;
+ }
+
+ wpa_driver_ndis_set_wzc(drv, 0);
+
+ if (wpa_driver_ndis_adapter_open(drv) < 0) {
+ wpa_driver_ndis_adapter_close(drv);
+ os_free(drv);
+ return NULL;
+ }
+
+ if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS,
+ (char *) drv->own_addr, ETH_ALEN) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS "
+ "failed");
+ wpa_driver_ndis_adapter_close(drv);
+ os_free(drv);
+ return NULL;
+ }
+ wpa_driver_ndis_get_capability(drv);
+
+ /* Make sure that the driver does not have any obsolete PMKID entries.
+ */
+ wpa_driver_ndis_flush_pmkid(drv);
+
+ /*
+ * Disconnect to make sure that driver re-associates if it was
+ * connected.
+ */
+ wpa_driver_ndis_disconnect(drv);
+
+ eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL);
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+ drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail,
+ drv->ifname, drv->adapter_desc);
+ if (drv->events == NULL) {
+ wpa_driver_ndis_deinit(drv);
+ return NULL;
+ }
+ eloop_register_event(drv->event_avail, sizeof(drv->event_avail),
+ wpa_driver_ndis_event_pipe_cb, drv, NULL);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+#ifdef _WIN32_WCE
+ if (ndisuio_notification_init(drv) < 0) {
+ wpa_driver_ndis_deinit(drv);
+ return NULL;
+ }
+#endif /* _WIN32_WCE */
+
+ /* Set mode here in case card was configured for ad-hoc mode
+ * previously. */
+ mode = Ndis802_11Infrastructure;
+ if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+ (char *) &mode, sizeof(mode)) < 0) {
+ char buf[8];
+ int res;
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+ "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+ (int) mode);
+ /* Try to continue anyway */
+
+ res = ndis_get_oid(drv, OID_DOT11_CURRENT_OPERATION_MODE, buf,
+ sizeof(buf));
+ if (res > 0) {
+ wpa_printf(MSG_INFO, "NDIS: The driver seems to use "
+ "Native 802.11 OIDs. These are not yet "
+ "fully supported.");
+ drv->native80211 = 1;
+ } else if (!drv->has_capability || drv->capa.enc == 0) {
+ /*
+ * Note: This will also happen with NDIS 6 drivers with
+ * Vista.
+ */
+ wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide "
+ "any wireless capabilities - assume it is "
+ "a wired interface");
+ drv->wired = 1;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_WIRED;
+ drv->has_capability = 1;
+ ndis_add_multicast(drv);
+ }
+ }
+
+ return drv;
+}
+
+
+static void wpa_driver_ndis_deinit(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+ if (drv->events) {
+ eloop_unregister_event(drv->event_avail,
+ sizeof(drv->event_avail));
+ ndis_events_deinit(drv->events);
+ }
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+#ifdef _WIN32_WCE
+ ndisuio_notification_deinit(drv);
+#endif /* _WIN32_WCE */
+
+ eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+ eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+ wpa_driver_ndis_flush_pmkid(drv);
+ wpa_driver_ndis_disconnect(drv);
+ if (wpa_driver_ndis_radio_off(drv) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn "
+ "radio off");
+ }
+
+ wpa_driver_ndis_adapter_close(drv);
+
+ if (drv->wzc_disabled)
+ wpa_driver_ndis_set_wzc(drv, 1);
+
+#ifdef _WIN32_WCE
+ os_free(drv->adapter_name);
+#endif /* _WIN32_WCE */
+ os_free(drv->adapter_desc);
+ os_free(drv);
+}
+
+
+static struct wpa_interface_info *
+wpa_driver_ndis_get_interfaces(void *global_priv)
+{
+ struct wpa_interface_info *iface = NULL, *niface;
+
+#ifdef CONFIG_USE_NDISUIO
+ NDISUIO_QUERY_BINDING *b;
+ size_t blen = sizeof(*b) + 1024;
+ int i, error;
+ DWORD written;
+ char name[256], desc[256];
+ WCHAR *pos;
+ size_t j, len;
+ HANDLE ndisuio;
+
+ ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ INVALID_HANDLE_VALUE);
+ if (ndisuio == INVALID_HANDLE_VALUE) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
+ "NDISUIO: %d", (int) GetLastError());
+ return NULL;
+ }
+
+#ifndef _WIN32_WCE
+ if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
+ NULL, 0, &written, NULL)) {
+ wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
+ "%d", (int) GetLastError());
+ CloseHandle(ndisuio);
+ return NULL;
+ }
+#endif /* _WIN32_WCE */
+
+ b = os_malloc(blen);
+ if (b == NULL) {
+ CloseHandle(ndisuio);
+ return NULL;
+ }
+
+ for (i = 0; ; i++) {
+ os_memset(b, 0, blen);
+ b->BindingIndex = i;
+ if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+ b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
+ &written, NULL)) {
+ error = (int) GetLastError();
+ if (error == ERROR_NO_MORE_ITEMS)
+ break;
+ wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
+ "failed: %d", error);
+ break;
+ }
+
+ pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+ len = b->DeviceNameLength;
+ if (len >= sizeof(name))
+ len = sizeof(name) - 1;
+ for (j = 0; j < len; j++)
+ name[j] = (char) pos[j];
+ name[len] = '\0';
+
+ pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+ len = b->DeviceDescrLength;
+ if (len >= sizeof(desc))
+ len = sizeof(desc) - 1;
+ for (j = 0; j < len; j++)
+ desc[j] = (char) pos[j];
+ desc[len] = '\0';
+
+ wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
+
+ niface = os_zalloc(sizeof(*niface));
+ if (niface == NULL)
+ break;
+ niface->drv_name = "ndis";
+ if (os_strncmp(name, "\\DEVICE\\", 8) == 0)
+ niface->ifname = os_strdup(name + 8);
+ else
+ niface->ifname = os_strdup(name);
+ if (niface->ifname == NULL) {
+ os_free(niface);
+ break;
+ }
+ niface->desc = os_strdup(desc);
+ niface->next = iface;
+ iface = niface;
+ }
+
+ os_free(b);
+ CloseHandle(ndisuio);
+#else /* CONFIG_USE_NDISUIO */
+ PTSTR _names;
+ char *names, *pos, *pos2;
+ ULONG len;
+ BOOLEAN res;
+ char *name[MAX_ADAPTERS];
+ char *desc[MAX_ADAPTERS];
+ int num_name, num_desc, i;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
+ PacketGetVersion());
+
+ len = 8192;
+ _names = os_zalloc(len);
+ if (_names == NULL)
+ return NULL;
+
+ res = PacketGetAdapterNames(_names, &len);
+ if (!res && len > 8192) {
+ os_free(_names);
+ _names = os_zalloc(len);
+ if (_names == NULL)
+ return NULL;
+ res = PacketGetAdapterNames(_names, &len);
+ }
+
+ if (!res) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
+ "(PacketGetAdapterNames)");
+ os_free(_names);
+ return NULL;
+ }
+
+ names = (char *) _names;
+ if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
+ "UNICODE");
+ /* Convert to ASCII */
+ pos2 = pos = names;
+ while (pos2 < names + len) {
+ if (pos2[0] == '\0' && pos2[1] == '\0' &&
+ pos2[2] == '\0' && pos2[3] == '\0') {
+ pos2 += 4;
+ break;
+ }
+ *pos++ = pos2[0];
+ pos2 += 2;
+ }
+ os_memcpy(pos + 2, names, pos - names);
+ pos += 2;
+ } else
+ pos = names;
+
+ num_name = 0;
+ while (pos < names + len) {
+ name[num_name] = pos;
+ while (*pos && pos < names + len)
+ pos++;
+ if (pos + 1 >= names + len) {
+ os_free(names);
+ return NULL;
+ }
+ pos++;
+ num_name++;
+ if (num_name >= MAX_ADAPTERS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
+ os_free(names);
+ return NULL;
+ }
+ if (*pos == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
+ num_name);
+ pos++;
+ break;
+ }
+ }
+
+ num_desc = 0;
+ while (pos < names + len) {
+ desc[num_desc] = pos;
+ while (*pos && pos < names + len)
+ pos++;
+ if (pos + 1 >= names + len) {
+ os_free(names);
+ return NULL;
+ }
+ pos++;
+ num_desc++;
+ if (num_desc >= MAX_ADAPTERS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
+ "descriptions");
+ os_free(names);
+ return NULL;
+ }
+ if (*pos == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
+ "found", num_name);
+ pos++;
+ break;
+ }
+ }
+
+ /*
+ * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
+ * descriptions. Fill in dummy descriptors to work around this.
+ */
+ while (num_desc < num_name)
+ desc[num_desc++] = "dummy description";
+
+ if (num_name != num_desc) {
+ wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
+ "description counts (%d != %d)",
+ num_name, num_desc);
+ os_free(names);
+ return NULL;
+ }
+
+ for (i = 0; i < num_name; i++) {
+ niface = os_zalloc(sizeof(*niface));
+ if (niface == NULL)
+ break;
+ niface->drv_name = "ndis";
+ if (os_strncmp(name[i], "\\Device\\NPF_", 12) == 0)
+ niface->ifname = os_strdup(name[i] + 12);
+ else
+ niface->ifname = os_strdup(name[i]);
+ if (niface->ifname == NULL) {
+ os_free(niface);
+ break;
+ }
+ niface->desc = os_strdup(desc[i]);
+ niface->next = iface;
+ iface = niface;
+ }
+
+#endif /* CONFIG_USE_NDISUIO */
+
+ return iface;
+}
+
+
+const struct wpa_driver_ops wpa_driver_ndis_ops = {
+ "ndis",
+ "Windows NDIS driver",
+ wpa_driver_ndis_get_bssid,
+ wpa_driver_ndis_get_ssid,
+ wpa_driver_ndis_set_key,
+ wpa_driver_ndis_init,
+ wpa_driver_ndis_deinit,
+ NULL /* set_param */,
+ NULL /* set_countermeasures */,
+ wpa_driver_ndis_deauthenticate,
+ wpa_driver_ndis_disassociate,
+ wpa_driver_ndis_associate,
+ wpa_driver_ndis_add_pmkid,
+ wpa_driver_ndis_remove_pmkid,
+ wpa_driver_ndis_flush_pmkid,
+ wpa_driver_ndis_get_capa,
+ wpa_driver_ndis_poll,
+ wpa_driver_ndis_get_ifname,
+ wpa_driver_ndis_get_mac_addr,
+ NULL /* send_eapol */,
+ NULL /* set_operstate */,
+ NULL /* mlme_setprotection */,
+ NULL /* get_hw_feature_data */,
+ NULL /* set_channel */,
+ NULL /* set_ssid */,
+ NULL /* set_bssid */,
+ NULL /* send_mlme */,
+ NULL /* mlme_add_sta */,
+ NULL /* mlme_remove_sta */,
+ NULL /* update_ft_ies */,
+ NULL /* send_ft_action */,
+ wpa_driver_ndis_get_scan_results,
+ NULL /* set_country */,
+ NULL /* global_init */,
+ NULL /* global_deinit */,
+ NULL /* init2 */,
+ wpa_driver_ndis_get_interfaces,
+ wpa_driver_ndis_scan,
+ NULL /* authenticate */,
+ NULL /* set_beacon */,
+ NULL /* hapd_init */,
+ NULL /* hapd_deinit */,
+ NULL /* set_ieee8021x */,
+ NULL /* set_privacy */,
+ NULL /* get_seqnum */,
+ NULL /* flush */,
+ NULL /* set_generic_elem */,
+ NULL /* read_sta_data */,
+ NULL /* hapd_send_eapol */,
+ NULL /* sta_deauth */,
+ NULL /* sta_disassoc */,
+ NULL /* sta_remove */,
+ NULL /* hapd_get_ssid */,
+ NULL /* hapd_set_ssid */,
+ NULL /* hapd_set_countermeasures */,
+ NULL /* sta_add */,
+ NULL /* get_inact_sec */,
+ NULL /* sta_clear_stats */,
+ NULL /* set_freq */,
+ NULL /* set_rts */,
+ NULL /* set_frag */,
+ NULL /* sta_set_flags */,
+ NULL /* set_rate_sets */,
+ NULL /* set_cts_protect */,
+ NULL /* set_preamble */,
+ NULL /* set_short_slot_time */,
+ NULL /* set_tx_queue_params */,
+ NULL /* valid_bss_mask */,
+ NULL /* if_add */,
+ NULL /* if_remove */,
+ NULL /* set_sta_vlan */,
+ NULL /* commit */,
+ NULL /* send_ether */,
+ NULL /* set_radius_acl_auth */,
+ NULL /* set_radius_acl_expire */,
+ NULL /* set_ht_params */,
+ NULL /* set_ap_wps_ie */,
+ NULL /* set_supp_port */,
+ NULL /* set_wds_sta */,
+ NULL /* send_action */,
+ NULL /* send_action_cancel_wait */,
+ NULL /* remain_on_channel */,
+ NULL /* cancel_remain_on_channel */,
+ NULL /* probe_req_report */,
+ NULL /* disable_11b_rates */,
+ NULL /* deinit_ap */,
+ NULL /* suspend */,
+ NULL /* resume */,
+ NULL /* signal_monitor */,
+ NULL /* send_frame */,
+ NULL /* shared_freq */,
+ NULL /* get_noa */,
+ NULL /* set_noa */,
+ NULL /* set_p2p_powersave */,
+ NULL /* ampdu */,
+ NULL /* set_intra_bss */,
+ NULL /* get_radio_name */,
+ NULL /* p2p_find */,
+ NULL /* p2p_stop_find */,
+ NULL /* p2p_listen */,
+ NULL /* p2p_connect */,
+ NULL /* wps_success_cb */,
+ NULL /* p2p_group_formation_failed */,
+ NULL /* p2p_set_params */,
+ NULL /* p2p_prov_disc_req */,
+ NULL /* p2p_sd_request */,
+ NULL /* p2p_sd_cancel_request */,
+ NULL /* p2p_sd_response */,
+ NULL /* p2p_service_update */,
+ NULL /* p2p_reject */,
+ NULL /* p2p_invite */,
+ NULL /* send_tdls_mgmt */,
+ NULL /* tdls_oper */,
+ NULL /* signal_poll */
+};
diff --git a/src/drivers/driver_ndis.h b/src/drivers/driver_ndis.h
new file mode 100644
index 0000000..f263f0e
--- /dev/null
+++ b/src/drivers/driver_ndis.h
@@ -0,0 +1,65 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef DRIVER_NDIS_H
+#define DRIVER_NDIS_H
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+struct ndis_events_data;
+struct ndis_events_data * ndis_events_init(HANDLE *read_pipe, HANDLE *event,
+ const char *ifname,
+ const char *desc);
+void ndis_events_deinit(struct ndis_events_data *events);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+struct ndis_pmkid_entry {
+ struct ndis_pmkid_entry *next;
+ u8 bssid[ETH_ALEN];
+ u8 pmkid[16];
+};
+
+struct wpa_driver_ndis_data {
+ void *ctx;
+ char ifname[100]; /* GUID: {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D} */
+#ifdef _WIN32_WCE
+ TCHAR *adapter_name;
+ HANDLE event_queue; /* NDISUIO notifier MsgQueue */
+ HANDLE connected_event; /* WpaSupplicantConnected event */
+#endif /* _WIN32_WCE */
+ u8 own_addr[ETH_ALEN];
+#ifdef CONFIG_USE_NDISUIO
+ HANDLE ndisuio;
+#else /* CONFIG_USE_NDISUIO */
+ LPADAPTER adapter;
+#endif /* CONFIG_USE_NDISUIO */
+ u8 bssid[ETH_ALEN];
+
+ int has_capability;
+ int no_of_pmkid;
+ int radio_enabled;
+ struct wpa_driver_capa capa;
+ struct ndis_pmkid_entry *pmkid;
+ char *adapter_desc;
+ int wired;
+ int native80211;
+ int mode;
+ int wzc_disabled;
+ int oid_bssid_set;
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+ HANDLE events_pipe, event_avail;
+ struct ndis_events_data *events;
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+};
+
+#endif /* DRIVER_NDIS_H */
diff --git a/src/drivers/driver_ndis_.c b/src/drivers/driver_ndis_.c
new file mode 100644
index 0000000..4bee9aa
--- /dev/null
+++ b/src/drivers/driver_ndis_.c
@@ -0,0 +1,105 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface - event processing
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+
+/* Keep this event processing in a separate file and without WinPcap headers to
+ * avoid conflicts with some of the header files. */
+struct _ADAPTER;
+typedef struct _ADAPTER * LPADAPTER;
+#include "driver_ndis.h"
+
+
+void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv,
+ const u8 *data, size_t data_len);
+void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv);
+
+
+enum event_types { EVENT_CONNECT, EVENT_DISCONNECT,
+ EVENT_MEDIA_SPECIFIC, EVENT_ADAPTER_ARRIVAL,
+ EVENT_ADAPTER_REMOVAL };
+
+/* Event data:
+ * enum event_types (as int, i.e., 4 octets)
+ * data length (2 octets (big endian), optional)
+ * data (variable len, optional)
+ */
+
+
+static void wpa_driver_ndis_event_process(struct wpa_driver_ndis_data *drv,
+ u8 *buf, size_t len)
+{
+ u8 *pos, *data = NULL;
+ enum event_types type;
+ size_t data_len = 0;
+
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: received event data", buf, len);
+ if (len < sizeof(int))
+ return;
+ type = *((int *) buf);
+ pos = buf + sizeof(int);
+ wpa_printf(MSG_DEBUG, "NDIS: event - type %d", type);
+
+ if (buf + len - pos > 2) {
+ data_len = (int) *pos++ << 8;
+ data_len += *pos++;
+ if (data_len > (size_t) (buf + len - pos)) {
+ wpa_printf(MSG_DEBUG, "NDIS: event data overflow");
+ return;
+ }
+ data = pos;
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: event data", data, data_len);
+ }
+
+ switch (type) {
+ case EVENT_CONNECT:
+ wpa_driver_ndis_event_connect(drv);
+ break;
+ case EVENT_DISCONNECT:
+ wpa_driver_ndis_event_disconnect(drv);
+ break;
+ case EVENT_MEDIA_SPECIFIC:
+ wpa_driver_ndis_event_media_specific(drv, data, data_len);
+ break;
+ case EVENT_ADAPTER_ARRIVAL:
+ wpa_driver_ndis_event_adapter_arrival(drv);
+ break;
+ case EVENT_ADAPTER_REMOVAL:
+ wpa_driver_ndis_event_adapter_removal(drv);
+ break;
+ }
+}
+
+
+void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data)
+{
+ struct wpa_driver_ndis_data *drv = eloop_data;
+ u8 buf[512];
+ DWORD len;
+
+ ResetEvent(drv->event_avail);
+ if (ReadFile(drv->events_pipe, buf, sizeof(buf), &len, NULL))
+ wpa_driver_ndis_event_process(drv, buf, len);
+ else {
+ wpa_printf(MSG_DEBUG, "%s: ReadFile() failed: %d", __func__,
+ (int) GetLastError());
+ }
+}
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
new file mode 100644
index 0000000..90b3f20
--- /dev/null
+++ b/src/drivers/driver_nl80211.c
@@ -0,0 +1,6550 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <linux/rtnetlink.h>
+#include <netpacket/packet.h>
+#include <linux/filter.h>
+#include "nl80211_copy.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "radiotap.h"
+#include "radiotap_iter.h"
+#include "rfkill.h"
+#include "driver.h"
+
+#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 */
+
+
+#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;
+};
+
+struct i802_bss {
+ struct wpa_driver_nl80211_data *drv;
+ struct i802_bss *next;
+ int ifindex;
+ char ifname[IFNAMSIZ + 1];
+ char brname[IFNAMSIZ];
+ unsigned int beacon_set:1;
+ unsigned int added_if_into_bridge:1;
+ unsigned int added_bridge:1;
+};
+
+struct wpa_driver_nl80211_data {
+ struct nl80211_global *global;
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ char phyname[32];
+ void *ctx;
+ struct netlink_data *netlink;
+ int ioctl_sock; /* socket for ioctl() use */
+ int ifindex;
+ int if_removed;
+ int if_disabled;
+ struct rfkill_data *rfkill;
+ struct wpa_driver_capa capa;
+ int has_capability;
+
+ int operstate;
+
+ int scan_complete_events;
+
+ struct nl_handle *nl_handle;
+ struct nl_handle *nl_handle_event;
+ struct nl_handle *nl_handle_preq;
+ struct nl_cache *nl_cache;
+ struct nl_cache *nl_cache_event;
+ struct nl_cache *nl_cache_preq;
+ struct nl_cb *nl_cb;
+ struct genl_family *nl80211;
+
+ u8 auth_bssid[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ int associated;
+ u8 ssid[32];
+ size_t ssid_len;
+ int nlmode;
+ int ap_scan_as_station;
+ unsigned int assoc_freq;
+
+ int monitor_sock;
+ int monitor_ifidx;
+ int disable_11b_rates;
+
+ unsigned int pending_remain_on_chan: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;
+
+#ifdef HOSTAPD
+ int eapol_sock; /* socket for EAPOL frames */
+
+ int default_if_indices[16];
+ int *if_indices;
+ int num_if_indices;
+
+ int last_freq;
+ int last_freq_ht;
+#endif /* HOSTAPD */
+};
+
+
+static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+static int wpa_driver_nl80211_set_mode(void *priv, int mode);
+static int
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);
+static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+ const u8 *addr, int cmd, u16 reason_code,
+ int local_state_change);
+static void nl80211_remove_monitor_interface(
+ struct wpa_driver_nl80211_data *drv);
+static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
+ unsigned int freq, unsigned int wait,
+ const u8 *buf, size_t buf_len, u64 *cookie);
+static int wpa_driver_nl80211_probe_req_report(void *priv, int report);
+
+#ifdef HOSTAPD
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
+static int wpa_driver_nl80211_if_remove(void *priv,
+ enum wpa_driver_if_type type,
+ const char *ifname);
+#else /* HOSTAPD */
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+ return 0;
+}
+#endif /* HOSTAPD */
+
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
+static 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);
+
+
+/* nl80211 code */
+static int ack_handler(struct nl_msg *msg, void *arg)
+{
+ int *err = arg;
+ *err = 0;
+ return NL_STOP;
+}
+
+static int finish_handler(struct nl_msg *msg, void *arg)
+{
+ int *ret = arg;
+ *ret = 0;
+ return NL_SKIP;
+}
+
+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
+ void *arg)
+{
+ int *ret = arg;
+ *ret = err->error;
+ return NL_SKIP;
+}
+
+
+static int no_seq_check(struct nl_msg *msg, void *arg)
+{
+ return NL_OK;
+}
+
+
+static int send_and_recv(struct wpa_driver_nl80211_data *drv,
+ struct nl_handle *nl_handle, struct nl_msg *msg,
+ int (*valid_handler)(struct nl_msg *, void *),
+ void *valid_data)
+{
+ struct nl_cb *cb;
+ int err = -ENOMEM;
+
+ cb = nl_cb_clone(drv->nl_cb);
+ if (!cb)
+ goto out;
+
+ err = nl_send_auto_complete(nl_handle, msg);
+ if (err < 0)
+ goto out;
+
+ err = 1;
+
+ nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
+ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+
+ if (valid_handler)
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
+ valid_handler, valid_data);
+
+ while (err > 0)
+ nl_recvmsgs(nl_handle, cb);
+ out:
+ nl_cb_put(cb);
+ nlmsg_free(msg);
+ return err;
+}
+
+
+static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg,
+ int (*valid_handler)(struct nl_msg *, void *),
+ void *valid_data)
+{
+ return send_and_recv(drv, drv->nl_handle, msg, valid_handler,
+ valid_data);
+}
+
+
+struct family_data {
+ const char *group;
+ int id;
+};
+
+
+static int family_handler(struct nl_msg *msg, void *arg)
+{
+ struct family_data *res = arg;
+ struct nlattr *tb[CTRL_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *mcgrp;
+ int i;
+
+ nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb[CTRL_ATTR_MCAST_GROUPS])
+ return NL_SKIP;
+
+ nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) {
+ struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1];
+ nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp),
+ nla_len(mcgrp), NULL);
+ if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] ||
+ !tb2[CTRL_ATTR_MCAST_GRP_ID] ||
+ os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]),
+ res->group,
+ nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
+ continue;
+ res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]);
+ break;
+ };
+
+ return NL_SKIP;
+}
+
+
+static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv,
+ const char *family, const char *group)
+{
+ struct nl_msg *msg;
+ int ret = -1;
+ struct family_data res = { group, -ENOENT };
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+ genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"),
+ 0, 0, CTRL_CMD_GETFAMILY, 0);
+ NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
+
+ ret = send_and_recv_msgs(drv, msg, family_handler, &res);
+ msg = NULL;
+ if (ret == 0)
+ ret = res.id;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (!drv->associated)
+ return -1;
+ os_memcpy(bssid, drv->bssid, ETH_ALEN);
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (!drv->associated)
+ return -1;
+ os_memcpy(ssid, drv->ssid, drv->ssid_len);
+ return drv->ssid_len;
+}
+
+
+static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
+ char *buf, size_t len, int del)
+{
+ union wpa_event_data event;
+
+ os_memset(&event, 0, sizeof(event));
+ if (len > sizeof(event.interface_status.ifname))
+ len = sizeof(event.interface_status.ifname) - 1;
+ os_memcpy(event.interface_status.ifname, buf, len);
+ event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED :
+ EVENT_INTERFACE_ADDED;
+
+ wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s",
+ del ? "DEL" : "NEW",
+ event.interface_status.ifname,
+ del ? "removed" : "added");
+
+ if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) {
+ if (del)
+ drv->if_removed = 1;
+ else
+ drv->if_removed = 0;
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
+ u8 *buf, size_t len)
+{
+ int attrlen, rta_len;
+ struct rtattr *attr;
+
+ attrlen = len;
+ attr = (struct rtattr *) buf;
+
+ rta_len = RTA_ALIGN(sizeof(struct rtattr));
+ while (RTA_OK(attr, attrlen)) {
+ if (attr->rta_type == IFLA_IFNAME) {
+ if (os_strcmp(((char *) attr) + rta_len, drv->first_bss.ifname)
+ == 0)
+ return 1;
+ else
+ break;
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
+ int ifindex, u8 *buf, size_t len)
+{
+ if (drv->ifindex == ifindex)
+ return 1;
+
+ if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
+ drv->first_bss.ifindex = if_nametoindex(drv->first_bss.ifname);
+ wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
+ "interface");
+ wpa_driver_nl80211_finish_drv_init(drv);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+ struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
+{
+ struct wpa_driver_nl80211_data *drv = ctx;
+ int attrlen, rta_len;
+ struct rtattr *attr;
+ u32 brid = 0;
+
+ if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) &&
+ !have_ifidx(drv, ifi->ifi_index)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign "
+ "ifindex %d", ifi->ifi_index);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x "
+ "(%s%s%s%s)",
+ drv->operstate, ifi->ifi_flags,
+ (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+ (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+ (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+ (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+ if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface down");
+ drv->if_disabled = 1;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+ }
+
+ if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+ 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->netlink, drv->ifindex,
+ -1, IF_OPER_UP);
+
+ attrlen = len;
+ attr = (struct rtattr *) buf;
+ rta_len = RTA_ALIGN(sizeof(struct rtattr));
+ while (RTA_OK(attr, attrlen)) {
+ if (attr->rta_type == IFLA_IFNAME) {
+ wpa_driver_nl80211_event_link(
+ drv,
+ ((char *) attr) + rta_len,
+ attr->rta_len - rta_len, 0);
+ } else if (attr->rta_type == IFLA_MASTER)
+ brid = nla_get_u32((struct nlattr *) attr);
+ attr = RTA_NEXT(attr, attrlen);
+ }
+
+#ifdef HOSTAPD
+ if (ifi->ifi_family == AF_BRIDGE && brid) {
+ /* device has been added to bridge */
+ char namebuf[IFNAMSIZ];
+ if_indextoname(brid, namebuf);
+ wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
+ brid, namebuf);
+ add_ifidx(drv, brid);
+ }
+#endif /* HOSTAPD */
+}
+
+
+static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
+ struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
+{
+ struct wpa_driver_nl80211_data *drv = ctx;
+ int attrlen, rta_len;
+ struct rtattr *attr;
+ u32 brid = 0;
+
+ attrlen = len;
+ attr = (struct rtattr *) buf;
+
+ rta_len = RTA_ALIGN(sizeof(struct rtattr));
+ while (RTA_OK(attr, attrlen)) {
+ if (attr->rta_type == IFLA_IFNAME) {
+ wpa_driver_nl80211_event_link(
+ drv,
+ ((char *) attr) + rta_len,
+ attr->rta_len - rta_len, 1);
+ } else if (attr->rta_type == IFLA_MASTER)
+ brid = nla_get_u32((struct nlattr *) attr);
+ attr = RTA_NEXT(attr, attrlen);
+ }
+
+#ifdef HOSTAPD
+ if (ifi->ifi_family == AF_BRIDGE && brid) {
+ /* device has been removed from bridge */
+ char namebuf[IFNAMSIZ];
+ if_indextoname(brid, namebuf);
+ wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge "
+ "%s", brid, namebuf);
+ del_ifidx(drv, brid);
+ }
+#endif /* HOSTAPD */
+}
+
+
+static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
+ const u8 *frame, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+ if (len < 24 + sizeof(mgmt->u.auth)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+ "frame");
+ return;
+ }
+
+ os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN);
+ os_memset(&event, 0, sizeof(event));
+ os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+ event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+ event.auth.status_code = le_to_host16(mgmt->u.auth.status_code);
+ if (len > 24 + sizeof(mgmt->u.auth)) {
+ event.auth.ies = mgmt->u.auth.variable;
+ event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event);
+}
+
+
+static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+ const u8 *frame, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+ u16 status;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+ if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+ "frame");
+ return;
+ }
+
+ status = le_to_host16(mgmt->u.assoc_resp.status_code);
+ if (status != WLAN_STATUS_SUCCESS) {
+ os_memset(&event, 0, sizeof(event));
+ 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_memset(&event, 0, sizeof(event));
+ if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+ event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
+ event.assoc_info.resp_ies_len =
+ len - 24 - sizeof(mgmt->u.assoc_resp);
+ }
+
+ event.assoc_info.freq = drv->assoc_freq;
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
+ enum nl80211_commands cmd, struct nlattr *status,
+ struct nlattr *addr, struct nlattr *req_ie,
+ struct nlattr *resp_ie)
+{
+ union wpa_event_data event;
+
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+ /*
+ * Avoid reporting two association events that would confuse
+ * the core code.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) "
+ "when using userspace SME", cmd);
+ return;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ if (cmd == NL80211_CMD_CONNECT &&
+ nla_get_u16(status) != WLAN_STATUS_SUCCESS) {
+ if (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);
+
+ if (req_ie) {
+ event.assoc_info.req_ies = nla_data(req_ie);
+ event.assoc_info.req_ies_len = nla_len(req_ie);
+ }
+ if (resp_ie) {
+ event.assoc_info.resp_ies = nla_data(resp_ie);
+ event.assoc_info.resp_ies_len = nla_len(resp_ie);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
+ enum nl80211_commands cmd, struct nlattr *addr)
+{
+ union wpa_event_data event;
+ enum wpa_event_type ev;
+
+ if (nla_len(addr) != ETH_ALEN)
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR,
+ cmd, MAC2STR((u8 *) nla_data(addr)));
+
+ if (cmd == NL80211_CMD_AUTHENTICATE)
+ ev = EVENT_AUTH_TIMED_OUT;
+ else if (cmd == NL80211_CMD_ASSOCIATE)
+ ev = EVENT_ASSOC_TIMED_OUT;
+ else
+ return;
+
+ os_memset(&event, 0, sizeof(event));
+ os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, ev, &event);
+}
+
+
+static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *freq, const u8 *frame, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+ u16 fc, stype;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+ if (len < 24) {
+ wpa_printf(MSG_DEBUG, "nl80211: Too short action frame");
+ return;
+ }
+
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ os_memset(&event, 0, sizeof(event));
+ if (freq) {
+ event.rx_action.freq = nla_get_u32(freq);
+ drv->last_mgmt_freq = event.rx_action.freq;
+ }
+ 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;
+ wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+ }
+}
+
+
+static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *cookie, const u8 *frame,
+ size_t len, struct nlattr *ack)
+{
+ union wpa_event_data event;
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+ u64 cookie_val;
+
+ if (!cookie)
+ return;
+
+ cookie_val = nla_get_u64(cookie);
+ wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s "
+ "(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;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+ if (len >= 24) {
+ bssid = mgmt->bssid;
+
+ if (drv->associated != 0 &&
+ os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
+ os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
+ /*
+ * We have presumably received this deauth as a
+ * response to a clear_state_mismatch() outgoing
+ * deauth. Don't let it take us offline!
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Deauth received "
+ "from Unknown BSSID " MACSTR " -- ignoring",
+ MAC2STR(bssid));
+ return;
+ }
+ }
+
+ drv->associated = 0;
+ os_memset(&event, 0, sizeof(event));
+
+ /* Note: Same offset for Reason Code in both frame subtypes */
+ if (len >= 24 + sizeof(mgmt->u.deauth))
+ reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+ if (type == EVENT_DISASSOC) {
+ event.disassoc_info.addr = bssid;
+ event.disassoc_info.reason_code = reason_code;
+ 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.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 (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 wpa_driver_nl80211_data *drv,
+ enum nl80211_commands cmd, struct nlattr *frame,
+ struct nlattr *addr, struct nlattr *timed_out,
+ struct nlattr *freq, struct nlattr *ack,
+ struct nlattr *cookie)
+{
+ if (timed_out && addr) {
+ mlme_timeout_event(drv, cmd, addr);
+ return;
+ }
+
+ if (frame == NULL) {
+ wpa_printf(MSG_DEBUG, "nl80211: MLME event %d without frame "
+ "data", cmd);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: MLME event %d", cmd);
+ wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame",
+ nla_data(frame), nla_len(frame));
+
+ switch (cmd) {
+ case NL80211_CMD_AUTHENTICATE:
+ mlme_event_auth(drv, nla_data(frame), nla_len(frame));
+ break;
+ case NL80211_CMD_ASSOCIATE:
+ mlme_event_assoc(drv, nla_data(frame), nla_len(frame));
+ break;
+ case NL80211_CMD_DEAUTHENTICATE:
+ mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
+ nla_data(frame), nla_len(frame));
+ break;
+ case NL80211_CMD_DISASSOCIATE:
+ mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
+ nla_data(frame), nla_len(frame));
+ break;
+ case NL80211_CMD_FRAME:
+ mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame));
+ break;
+ case NL80211_CMD_FRAME_TX_STATUS:
+ mlme_event_action_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 wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ union wpa_event_data data;
+
+ wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure");
+ os_memset(&data, 0, sizeof(data));
+ if (tb[NL80211_ATTR_MAC]) {
+ wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address",
+ nla_data(tb[NL80211_ATTR_MAC]),
+ nla_len(tb[NL80211_ATTR_MAC]));
+ data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]);
+ }
+ if (tb[NL80211_ATTR_KEY_SEQ]) {
+ wpa_hexdump(MSG_DEBUG, "nl80211: TSC",
+ nla_data(tb[NL80211_ATTR_KEY_SEQ]),
+ nla_len(tb[NL80211_ATTR_KEY_SEQ]));
+ }
+ if (tb[NL80211_ATTR_KEY_TYPE]) {
+ enum nl80211_key_type key_type =
+ nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]);
+ wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type);
+ if (key_type == NL80211_KEYTYPE_PAIRWISE)
+ data.michael_mic_failure.unicast = 1;
+ } else
+ data.michael_mic_failure.unicast = 1;
+
+ if (tb[NL80211_ATTR_KEY_IDX]) {
+ u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]);
+ wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ if (tb[NL80211_ATTR_MAC] == NULL) {
+ wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
+ "event");
+ return;
+ }
+ os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+ drv->associated = 1;
+ wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined",
+ MAC2STR(drv->bssid));
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+}
+
+
+static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
+ int cancel_event, struct nlattr *tb[])
+{
+ unsigned int freq, chan_type, duration;
+ union wpa_event_data data;
+ u64 cookie;
+
+ if (tb[NL80211_ATTR_WIPHY_FREQ])
+ freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+ else
+ freq = 0;
+
+ if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
+ chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+ else
+ chan_type = 0;
+
+ if (tb[NL80211_ATTR_DURATION])
+ duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
+ else
+ duration = 0;
+
+ if (tb[NL80211_ATTR_COOKIE])
+ cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+ else
+ cookie = 0;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
+ "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
+ cancel_event, freq, chan_type, duration,
+ (long long unsigned int) cookie,
+ cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
+
+ if (cookie != drv->remain_on_chan_cookie)
+ return; /* not for us */
+
+ drv->pending_remain_on_chan = !cancel_event;
+
+ os_memset(&data, 0, sizeof(data));
+ data.remain_on_channel.freq = freq;
+ data.remain_on_channel.duration = duration;
+ wpa_supplicant_event(drv->ctx, cancel_event ?
+ EVENT_CANCEL_REMAIN_ON_CHANNEL :
+ EVENT_REMAIN_ON_CHANNEL, &data);
+}
+
+
+static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
+ struct nlattr *tb[])
+{
+ union wpa_event_data event;
+ struct nlattr *nl;
+ int rem;
+ struct scan_info *info;
+#define MAX_REPORT_FREQS 50
+ int freqs[MAX_REPORT_FREQS];
+ int num_freqs = 0;
+
+ os_memset(&event, 0, sizeof(event));
+ info = &event.scan_info;
+ info->aborted = aborted;
+
+ if (tb[NL80211_ATTR_SCAN_SSIDS]) {
+ nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
+ struct wpa_driver_scan_ssid *s =
+ &info->ssids[info->num_ssids];
+ s->ssid = nla_data(nl);
+ s->ssid_len = nla_len(nl);
+ info->num_ssids++;
+ if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+ break;
+ }
+ }
+ if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
+ {
+ freqs[num_freqs] = nla_get_u32(nl);
+ num_freqs++;
+ if (num_freqs == MAX_REPORT_FREQS - 1)
+ break;
+ }
+ info->freqs = freqs;
+ info->num_freqs = num_freqs;
+ }
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
+static 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 },
+ };
+ 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_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;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_GET_STATION, 0);
+
+ 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:
+ 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;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ NLM_F_DUMP, NL80211_CMD_GET_SURVEY, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
+ nla_put_failure:
+ 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 (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 (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 int process_event(struct nl_msg *msg, void *arg)
+{
+ struct wpa_driver_nl80211_data *drv = arg;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ union wpa_event_data data;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_IFINDEX]) {
+ int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+ if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
+ " for foreign interface (ifindex %d)",
+ gnlh->cmd, ifindex);
+ return NL_SKIP;
+ }
+ }
+
+ if (drv->ap_scan_as_station &&
+ (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
+ gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) {
+ wpa_driver_nl80211_set_mode(&drv->first_bss,
+ IEEE80211_MODE_AP);
+ drv->ap_scan_as_station = 0;
+ }
+
+ switch (gnlh->cmd) {
+ case NL80211_CMD_TRIGGER_SCAN:
+ wpa_printf(MSG_DEBUG, "nl80211: Scan trigger");
+ break;
+ case NL80211_CMD_NEW_SCAN_RESULTS:
+ wpa_printf(MSG_DEBUG, "nl80211: New scan results available");
+ drv->scan_complete_events = 1;
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+ drv->ctx);
+ send_scan_event(drv, 0, tb);
+ break;
+ case NL80211_CMD_SCAN_ABORTED:
+ wpa_printf(MSG_DEBUG, "nl80211: Scan aborted");
+ /*
+ * Need to indicate that scan results are available in order
+ * not to make wpa_supplicant stop its scanning.
+ */
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+ drv->ctx);
+ send_scan_event(drv, 1, tb);
+ break;
+ case NL80211_CMD_AUTHENTICATE:
+ case NL80211_CMD_ASSOCIATE:
+ case NL80211_CMD_DEAUTHENTICATE:
+ case NL80211_CMD_DISASSOCIATE:
+ case NL80211_CMD_FRAME:
+ case NL80211_CMD_FRAME_TX_STATUS:
+ case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+ case NL80211_CMD_UNPROT_DISASSOCIATE:
+ mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
+ tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
+ tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+ tb[NL80211_ATTR_COOKIE]);
+ break;
+ case NL80211_CMD_CONNECT:
+ case NL80211_CMD_ROAM:
+ mlme_event_connect(drv, gnlh->cmd,
+ tb[NL80211_ATTR_STATUS_CODE],
+ tb[NL80211_ATTR_MAC],
+ tb[NL80211_ATTR_REQ_IE],
+ tb[NL80211_ATTR_RESP_IE]);
+ break;
+ case NL80211_CMD_DISCONNECT:
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+ /*
+ * Avoid reporting two disassociation events that could
+ * confuse the core code.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+ "event when using userspace SME");
+ break;
+ }
+ drv->associated = 0;
+ os_memset(&data, 0, sizeof(data));
+ if (tb[NL80211_ATTR_REASON_CODE])
+ data.disassoc_info.reason_code =
+ nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
+ break;
+ case NL80211_CMD_MICHAEL_MIC_FAILURE:
+ mlme_event_michael_mic_failure(drv, tb);
+ break;
+ case NL80211_CMD_JOIN_IBSS:
+ mlme_event_join_ibss(drv, tb);
+ break;
+ case NL80211_CMD_REMAIN_ON_CHANNEL:
+ mlme_event_remain_on_channel(drv, 0, tb);
+ break;
+ case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+ mlme_event_remain_on_channel(drv, 1, tb);
+ break;
+ case NL80211_CMD_NOTIFY_CQM:
+ nl80211_cqm_event(drv, tb);
+ break;
+ case NL80211_CMD_REG_CHANGE:
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
+ wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
+ NULL);
+ 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;
+ 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;
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Event message available");
+
+ cb = nl_cb_clone(drv->nl_cb);
+ if (!cb)
+ return;
+ nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv);
+ nl_recvmsgs(handle, cb);
+ nl_cb_put(cb);
+}
+
+
+/**
+ * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
+ * @priv: driver_nl80211 private data
+ * @alpha2_arg: country to which to switch to
+ * Returns: 0 on success, -1 on failure
+ *
+ * This asks nl80211 to set the regulatory domain for given
+ * country ISO / IEC alpha2.
+ */
+static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ char alpha2[3];
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ alpha2[0] = alpha2_arg[0];
+ alpha2[1] = alpha2_arg[1];
+ alpha2[2] = '\0';
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_REQ_SET_REG, 0);
+
+ NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2);
+ if (send_and_recv_msgs(drv, msg, NULL, NULL))
+ return -EINVAL;
+ return 0;
+nla_put_failure:
+ return -EINVAL;
+}
+
+
+struct wiphy_info_data {
+ int max_scan_ssids;
+ int ap_supported;
+ int p2p_supported;
+ int auth_supported;
+ int connect_supported;
+ int offchan_tx_supported;
+ int max_remain_on_chan;
+};
+
+
+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;
+ int p2p_go_supported = 0, p2p_client_supported = 0;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
+ info->max_scan_ssids =
+ nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
+
+ if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) {
+ struct nlattr *nl_mode;
+ int i;
+ nla_for_each_nested(nl_mode,
+ tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) {
+ switch (nla_type(nl_mode)) {
+ case NL80211_IFTYPE_AP:
+ info->ap_supported = 1;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ p2p_go_supported = 1;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ p2p_client_supported = 1;
+ break;
+ }
+ }
+ }
+
+ info->p2p_supported = p2p_go_supported && p2p_client_supported;
+
+ if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
+ struct nlattr *nl_cmd;
+ int i;
+
+ nla_for_each_nested(nl_cmd,
+ tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) {
+ u32 cmd = nla_get_u32(nl_cmd);
+ if (cmd == NL80211_CMD_AUTHENTICATE)
+ info->auth_supported = 1;
+ else if (cmd == NL80211_CMD_CONNECT)
+ info->connect_supported = 1;
+ }
+ }
+
+ if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK])
+ info->offchan_tx_supported = 1;
+
+ if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION])
+ info->max_remain_on_chan =
+ nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+
+ return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
+ struct wiphy_info_data *info)
+{
+ struct nl_msg *msg;
+
+ os_memset(info, 0, sizeof(*info));
+
+ /* default to 5000 since early versions of mac80211 don't set it */
+ info->max_remain_on_chan = 5000;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_GET_WIPHY, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex);
+
+ if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info) == 0)
+ return 0;
+ msg = NULL;
+nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
+{
+ struct wiphy_info_data info;
+ if (wpa_driver_nl80211_get_info(drv, &info))
+ return -1;
+ drv->has_capability = 1;
+ /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
+ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 |
+ WPA_DRIVER_CAPA_ENC_WEP104 |
+ WPA_DRIVER_CAPA_ENC_TKIP |
+ WPA_DRIVER_CAPA_ENC_CCMP;
+ drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+ WPA_DRIVER_AUTH_SHARED |
+ WPA_DRIVER_AUTH_LEAP;
+
+ drv->capa.max_scan_ssids = info.max_scan_ssids;
+ if (info.ap_supported)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+
+ if (info.auth_supported)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
+ else if (!info.connect_supported) {
+ wpa_printf(MSG_INFO, "nl80211: Driver does not support "
+ "authentication/association or connect commands");
+ return -1;
+ }
+
+ if (info.offchan_tx_supported) {
+ wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
+ "off-channel TX");
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+ }
+
+ drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
+ if (info.p2p_supported)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+ drv->capa.max_remain_on_chan = info.max_remain_on_chan;
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
+{
+ int ret;
+
+ /* Initialize generic netlink and nl80211 */
+
+ drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (drv->nl_cb == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+ "callbacks");
+ goto err1;
+ }
+
+ drv->nl_handle = nl80211_handle_alloc(drv->nl_cb);
+ if (drv->nl_handle == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+ "callbacks");
+ goto err2;
+ }
+
+ drv->nl_handle_event = nl80211_handle_alloc(drv->nl_cb);
+ if (drv->nl_handle_event == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+ "callbacks (event)");
+ goto err2b;
+ }
+
+ if (genl_connect(drv->nl_handle)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
+ "netlink");
+ goto err3;
+ }
+
+ if (genl_connect(drv->nl_handle_event)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
+ "netlink (event)");
+ goto err3;
+ }
+
+#ifdef CONFIG_LIBNL20
+ if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache");
+ goto err3;
+ }
+ if (genl_ctrl_alloc_cache(drv->nl_handle_event, &drv->nl_cache_event) <
+ 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache (event)");
+ goto err3b;
+ }
+#else /* CONFIG_LIBNL20 */
+ drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle);
+ if (drv->nl_cache == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache");
+ goto err3;
+ }
+ drv->nl_cache_event = genl_ctrl_alloc_cache(drv->nl_handle_event);
+ if (drv->nl_cache_event == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache (event)");
+ goto err3b;
+ }
+#endif /* CONFIG_LIBNL20 */
+
+ drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211");
+ if (drv->nl80211 == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
+ "found");
+ goto err4;
+ }
+
+ ret = nl_get_multicast_id(drv, "nl80211", "scan");
+ if (ret >= 0)
+ ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+ "membership for scan events: %d (%s)",
+ ret, strerror(-ret));
+ goto err4;
+ }
+
+ ret = nl_get_multicast_id(drv, "nl80211", "mlme");
+ if (ret >= 0)
+ ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+ "membership for mlme events: %d (%s)",
+ ret, strerror(-ret));
+ goto err4;
+ }
+
+ ret = nl_get_multicast_id(drv, "nl80211", "regulatory");
+ if (ret >= 0)
+ ret = nl_socket_add_membership(drv->nl_handle_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 */
+ }
+
+ eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event),
+ wpa_driver_nl80211_event_receive, drv,
+ drv->nl_handle_event);
+
+ return 0;
+
+err4:
+ nl_cache_free(drv->nl_cache_event);
+err3b:
+ nl_cache_free(drv->nl_cache);
+err3:
+ nl80211_handle_destroy(drv->nl_handle_event);
+err2b:
+ nl80211_handle_destroy(drv->nl_handle);
+err2:
+ nl_cb_put(drv->nl_cb);
+err1:
+ return -1;
+}
+
+
+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 (linux_set_iface_flags(drv->ioctl_sock, drv->first_bss.ifname, 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 nl80211_get_phy_name(struct wpa_driver_nl80211_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->first_bss.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, "nl80211: interface %s in phy %s",
+ drv->first_bss.ifname, drv->phyname);
+}
+
+
+/**
+ * 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)
+{
+ struct wpa_driver_nl80211_data *drv;
+ struct netlink_config *cfg;
+ struct rfkill_config *rcfg;
+ struct i802_bss *bss;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->global = global_priv;
+ drv->ctx = ctx;
+ bss = &drv->first_bss;
+ bss->drv = drv;
+ os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
+ drv->monitor_ifidx = -1;
+ drv->monitor_sock = -1;
+ drv->ioctl_sock = -1;
+
+ if (wpa_driver_nl80211_init_nl(drv)) {
+ os_free(drv);
+ return NULL;
+ }
+
+ nl80211_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 failed;
+ }
+
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL)
+ goto failed;
+ cfg->ctx = drv;
+ cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+ cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+ drv->netlink = netlink_init(cfg);
+ if (drv->netlink == NULL) {
+ os_free(cfg);
+ goto failed;
+ }
+
+ 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 (wpa_driver_nl80211_finish_drv_init(drv))
+ goto failed;
+
+ if (drv->global)
+ dl_list_add(&drv->global->interfaces, &drv->list);
+
+ return bss;
+
+failed:
+ rfkill_deinit(drv->rfkill);
+ netlink_deinit(drv->netlink);
+ if (drv->ioctl_sock >= 0)
+ close(drv->ioctl_sock);
+
+ genl_family_put(drv->nl80211);
+ nl_cache_free(drv->nl_cache);
+ nl80211_handle_destroy(drv->nl_handle);
+ nl_cb_put(drv->nl_cb);
+ eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
+
+ os_free(drv);
+ return NULL;
+}
+
+
+static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv,
+ struct nl_handle *nl_handle,
+ u16 type, const u8 *match, size_t match_len)
+{
+ struct nl_msg *msg;
+ int ret = -1;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_REGISTER_ACTION, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type);
+ NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match);
+
+ ret = send_and_recv(drv, 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_register_action_frame(struct wpa_driver_nl80211_data *drv,
+ const u8 *match, size_t match_len)
+{
+ u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
+ return nl80211_register_frame(drv, drv->nl_handle_event,
+ type, match, match_len);
+}
+
+
+static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv)
+{
+#ifdef CONFIG_P2P
+ /* GAS Initial Request */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0)
+ return -1;
+ /* GAS Initial Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0)
+ return -1;
+ /* GAS Comeback Request */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0)
+ return -1;
+ /* GAS Comeback Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0)
+ return -1;
+ /* P2P Public Action */
+ if (nl80211_register_action_frame(drv,
+ (u8 *) "\x04\x09\x50\x6f\x9a\x09",
+ 6) < 0)
+ return -1;
+ /* P2P Action */
+ if (nl80211_register_action_frame(drv,
+ (u8 *) "\x7f\x50\x6f\x9a\x09",
+ 5) < 0)
+ return -1;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_IEEE80211W
+ /* SA Query Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x08\x01", 2) < 0)
+ return -1;
+#endif /* CONFIG_IEEE80211W */
+
+ /* FT Action frames */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0)
+ return -1;
+ else
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+
+ return 0;
+}
+
+
+static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
+static int
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
+{
+ struct i802_bss *bss = &drv->first_bss;
+ int send_rfkill_event = 0;
+
+ drv->ifindex = if_nametoindex(bss->ifname);
+ drv->first_bss.ifindex = drv->ifindex;
+
+#ifndef HOSTAPD
+ if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA) < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to "
+ "use managed mode");
+ }
+
+ if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+ 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;
+ }
+ }
+
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+ 1, IF_OPER_DORMANT);
+#endif /* HOSTAPD */
+
+ if (wpa_driver_nl80211_capa(drv))
+ return -1;
+
+ if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, drv->addr))
+ return -1;
+
+ if (nl80211_register_action_frames(drv) < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
+ "frame processing - ignore for now");
+ /*
+ * Older kernel versions did not support this, so ignore the
+ * error for now. Some functionality may not be available
+ * because of this.
+ */
+ }
+
+ 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;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_DEL_BEACON, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+/**
+ * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface
+ * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init()
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in wpa_driver_nl80211_init().
+ */
+static void wpa_driver_nl80211_deinit(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (drv->nl_handle_preq)
+ wpa_driver_nl80211_probe_req_report(bss, 0);
+ if (bss->added_if_into_bridge) {
+ if (linux_br_del_if(drv->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->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 (drv->nlmode == NL80211_IFTYPE_AP)
+ wpa_driver_nl80211_del_beacon(drv);
+
+#ifdef HOSTAPD
+ if (drv->last_freq_ht) {
+ /* Clear HT flags from the driver */
+ struct hostapd_freq_params freq;
+ os_memset(&freq, 0, sizeof(freq));
+ freq.freq = drv->last_freq;
+ i802_set_freq(priv, &freq);
+ }
+
+ if (drv->eapol_sock >= 0) {
+ eloop_unregister_read_sock(drv->eapol_sock);
+ close(drv->eapol_sock);
+ }
+
+ if (drv->if_indices != drv->default_if_indices)
+ os_free(drv->if_indices);
+#endif /* HOSTAPD */
+
+ if (drv->disable_11b_rates)
+ nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
+ netlink_deinit(drv->netlink);
+ rfkill_deinit(drv->rfkill);
+
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+
+ (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0);
+ wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA);
+
+ if (drv->ioctl_sock >= 0)
+ close(drv->ioctl_sock);
+
+ eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
+ genl_family_put(drv->nl80211);
+ nl_cache_free(drv->nl_cache);
+ nl_cache_free(drv->nl_cache_event);
+ nl80211_handle_destroy(drv->nl_handle);
+ nl80211_handle_destroy(drv->nl_handle_event);
+ nl_cb_put(drv->nl_cb);
+
+ os_free(drv->filter_ssids);
+
+ if (drv->global)
+ dl_list_del(&drv->list);
+
+ os_free(drv);
+}
+
+
+/**
+ * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
+ * @eloop_ctx: Driver private data
+ * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init()
+ *
+ * This function can be used as registered timeout when starting a scan to
+ * generate a scan completed event if the driver does not report this.
+ */
+static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ if (drv->ap_scan_as_station) {
+ wpa_driver_nl80211_set_mode(&drv->first_bss,
+ IEEE80211_MODE_AP);
+ drv->ap_scan_as_station = 0;
+ }
+ wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+/**
+ * wpa_driver_nl80211_scan - Request the driver to initiate scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+static int wpa_driver_nl80211_scan(void *priv,
+ struct wpa_driver_scan_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = 0, timeout;
+ struct nl_msg *msg, *ssids, *freqs;
+ size_t i;
+
+ msg = nlmsg_alloc();
+ ssids = nlmsg_alloc();
+ freqs = nlmsg_alloc();
+ if (!msg || !ssids || !freqs) {
+ nlmsg_free(msg);
+ nlmsg_free(ssids);
+ nlmsg_free(freqs);
+ return -1;
+ }
+
+ os_free(drv->filter_ssids);
+ drv->filter_ssids = params->filter_ssids;
+ params->filter_ssids = NULL;
+ drv->num_filter_ssids = params->num_filter_ssids;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_TRIGGER_SCAN, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ for (i = 0; i < params->num_ssids; i++) {
+ wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
+ params->ssids[i].ssid,
+ params->ssids[i].ssid_len);
+ NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len,
+ params->ssids[i].ssid);
+ }
+ if (params->num_ssids)
+ nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
+
+ if (params->extra_ies) {
+ wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+ params->extra_ies, params->extra_ies_len);
+ NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len,
+ params->extra_ies);
+ }
+
+ if (params->freqs) {
+ for (i = 0; params->freqs[i]; i++) {
+ wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+ "MHz", params->freqs[i]);
+ NLA_PUT_U32(freqs, i + 1, params->freqs[i]);
+ }
+ nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+#ifdef HOSTAPD
+ if (drv->nlmode == NL80211_IFTYPE_AP) {
+ /*
+ * mac80211 does not allow scan requests in AP mode, so
+ * try to do this in station mode.
+ */
+ if (wpa_driver_nl80211_set_mode(bss,
+ IEEE80211_MODE_INFRA))
+ goto nla_put_failure;
+
+ if (wpa_driver_nl80211_scan(drv, params)) {
+ wpa_driver_nl80211_set_mode(bss,
+ IEEE80211_MODE_AP);
+ goto nla_put_failure;
+ }
+
+ /* Restore AP mode when processing scan results */
+ drv->ap_scan_as_station = 1;
+ ret = 0;
+ } else
+ goto nla_put_failure;
+#else /* HOSTAPD */
+ goto nla_put_failure;
+#endif /* HOSTAPD */
+ }
+
+ /* Not all drivers generate "scan completed" wireless event, so try to
+ * read results after a timeout. */
+ timeout = 10;
+ if (drv->scan_complete_events) {
+ /*
+ * The driver seems to deliver events to notify when scan is
+ * complete, so use longer timeout to avoid race conditions
+ * with scanning and following association request.
+ */
+ timeout = 30;
+ }
+ wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
+ "seconds", ret, timeout);
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
+ drv, drv->ctx);
+
+nla_put_failure:
+ nlmsg_free(ssids);
+ nlmsg_free(msg);
+ nlmsg_free(freqs);
+ return ret;
+}
+
+
+static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie)
+{
+ const u8 *end, *pos;
+
+ if (ies == NULL)
+ return NULL;
+
+ pos = ies;
+ end = ies + ies_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == ie)
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+
+static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
+ const u8 *ie, size_t ie_len)
+{
+ const u8 *ssid;
+ size_t i;
+
+ if (drv->filter_ssids == NULL)
+ return 0;
+
+ ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID);
+ if (ssid == NULL)
+ return 1;
+
+ for (i = 0; i < drv->num_filter_ssids; i++) {
+ if (ssid[1] == drv->filter_ssids[i].ssid_len &&
+ os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) ==
+ 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+struct nl80211_bss_info_arg {
+ struct wpa_driver_nl80211_data *drv;
+ struct wpa_scan_results *res;
+};
+
+static int bss_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *bss[NL80211_BSS_MAX + 1];
+ static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+ [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+ [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_BSS_TSF] = { .type = NLA_U64 },
+ [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+ [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+ [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
+ [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
+ [NL80211_BSS_STATUS] = { .type = NLA_U32 },
+ [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
+ [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
+ };
+ struct nl80211_bss_info_arg *_arg = arg;
+ struct wpa_scan_results *res = _arg->res;
+ struct wpa_scan_res **tmp;
+ struct wpa_scan_res *r;
+ const u8 *ie, *beacon_ie;
+ size_t ie_len, beacon_ie_len;
+ u8 *pos;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb[NL80211_ATTR_BSS])
+ return NL_SKIP;
+ if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+ bss_policy))
+ return NL_SKIP;
+ if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+ ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+ ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+ } else {
+ ie = NULL;
+ ie_len = 0;
+ }
+ if (bss[NL80211_BSS_BEACON_IES]) {
+ beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]);
+ beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]);
+ } else {
+ beacon_ie = NULL;
+ beacon_ie_len = 0;
+ }
+
+ if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie,
+ ie ? ie_len : beacon_ie_len))
+ return NL_SKIP;
+
+ r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
+ if (r == NULL)
+ return NL_SKIP;
+ if (bss[NL80211_BSS_BSSID])
+ os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
+ ETH_ALEN);
+ if (bss[NL80211_BSS_FREQUENCY])
+ r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+ if (bss[NL80211_BSS_BEACON_INTERVAL])
+ r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]);
+ if (bss[NL80211_BSS_CAPABILITY])
+ r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
+ r->flags |= WPA_SCAN_NOISE_INVALID;
+ if (bss[NL80211_BSS_SIGNAL_MBM]) {
+ r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
+ r->level /= 100; /* mBm to dBm */
+ r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
+ } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
+ r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
+ r->flags |= WPA_SCAN_LEVEL_INVALID;
+ } else
+ r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
+ if (bss[NL80211_BSS_TSF])
+ r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
+ if (bss[NL80211_BSS_SEEN_MS_AGO])
+ r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
+ r->ie_len = ie_len;
+ pos = (u8 *) (r + 1);
+ if (ie) {
+ os_memcpy(pos, ie, ie_len);
+ pos += ie_len;
+ }
+ r->beacon_ie_len = beacon_ie_len;
+ if (beacon_ie)
+ os_memcpy(pos, beacon_ie, beacon_ie_len);
+
+ if (bss[NL80211_BSS_STATUS]) {
+ enum nl80211_bss_status status;
+ status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+ switch (status) {
+ case NL80211_BSS_STATUS_AUTHENTICATED:
+ r->flags |= WPA_SCAN_AUTHENTICATED;
+ break;
+ case NL80211_BSS_STATUS_ASSOCIATED:
+ r->flags |= WPA_SCAN_ASSOCIATED;
+ break;
+ default:
+ break;
+ }
+ }
+
+ tmp = os_realloc(res->res,
+ (res->num + 1) * sizeof(struct wpa_scan_res *));
+ if (tmp == NULL) {
+ os_free(r);
+ return NL_SKIP;
+ }
+ tmp[res->num++] = r;
+ res->res = tmp;
+
+ return NL_SKIP;
+}
+
+
+static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
+ const u8 *addr)
+{
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+ wpa_printf(MSG_DEBUG, "nl80211: Clear possible state "
+ "mismatch (" MACSTR ")", MAC2STR(addr));
+ wpa_driver_nl80211_mlme(drv, addr,
+ NL80211_CMD_DEAUTHENTICATE,
+ WLAN_REASON_PREV_AUTH_NOT_VALID, 1);
+ }
+}
+
+
+static void wpa_driver_nl80211_check_bss_status(
+ struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res)
+{
+ size_t i;
+
+ for (i = 0; i < res->num; i++) {
+ struct wpa_scan_res *r = res->res[i];
+ if (r->flags & WPA_SCAN_AUTHENTICATED) {
+ wpa_printf(MSG_DEBUG, "nl80211: Scan results "
+ "indicates BSS status with " MACSTR
+ " as authenticated",
+ MAC2STR(r->bssid));
+ if (drv->nlmode == NL80211_IFTYPE_STATION &&
+ os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 &&
+ os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) !=
+ 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Unknown BSSID"
+ " in local state (auth=" MACSTR
+ " assoc=" MACSTR ")",
+ MAC2STR(drv->auth_bssid),
+ MAC2STR(drv->bssid));
+ clear_state_mismatch(drv, r->bssid);
+ }
+ }
+
+ if (r->flags & WPA_SCAN_ASSOCIATED) {
+ wpa_printf(MSG_DEBUG, "nl80211: Scan results "
+ "indicate BSS status with " MACSTR
+ " as associated",
+ MAC2STR(r->bssid));
+ if (drv->nlmode == NL80211_IFTYPE_STATION &&
+ !drv->associated) {
+ wpa_printf(MSG_DEBUG, "nl80211: Local state "
+ "(not associated) does not match "
+ "with BSS state");
+ clear_state_mismatch(drv, r->bssid);
+ } else if (drv->nlmode == NL80211_IFTYPE_STATION &&
+ os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
+ 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Local state "
+ "(associated with " MACSTR ") does "
+ "not match with BSS state",
+ MAC2STR(drv->bssid));
+ clear_state_mismatch(drv, r->bssid);
+ clear_state_mismatch(drv, drv->bssid);
+ }
+ }
+ }
+}
+
+
+static void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+ size_t i;
+
+ if (res == NULL)
+ return;
+
+ for (i = 0; i < res->num; i++)
+ os_free(res->res[i]);
+ os_free(res->res);
+ os_free(res);
+}
+
+
+static struct wpa_scan_results *
+nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+ struct wpa_scan_results *res;
+ int ret;
+ struct nl80211_bss_info_arg arg;
+
+ res = os_zalloc(sizeof(*res));
+ if (res == NULL)
+ return NULL;
+ msg = nlmsg_alloc();
+ if (!msg)
+ goto nla_put_failure;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
+ NL80211_CMD_GET_SCAN, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ arg.drv = drv;
+ arg.res = res;
+ ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+ msg = NULL;
+ if (ret == 0) {
+ wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)",
+ (unsigned long) res->num);
+ return res;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+nla_put_failure:
+ nlmsg_free(msg);
+ wpa_scan_results_free(res);
+ return NULL;
+}
+
+
+/**
+ * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
+ * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
+ * Returns: Scan results on success, -1 on failure
+ */
+static struct wpa_scan_results *
+wpa_driver_nl80211_get_scan_results(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct wpa_scan_results *res;
+
+ res = nl80211_get_scan_results(drv);
+ if (res)
+ wpa_driver_nl80211_check_bss_status(drv, res);
+ return res;
+}
+
+
+static void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+{
+ struct wpa_scan_results *res;
+ size_t i;
+
+ res = nl80211_get_scan_results(drv);
+ if (res == NULL) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
+ for (i = 0; i < res->num; i++) {
+ struct wpa_scan_res *r = res->res[i];
+ wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s%s",
+ (int) i, (int) res->num, MAC2STR(r->bssid),
+ r->flags & WPA_SCAN_AUTHENTICATED ? " [auth]" : "",
+ r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
+ }
+
+ wpa_scan_results_free(res);
+}
+
+
+static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ifindex = if_nametoindex(ifname);
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "%s: ifindex=%d alg=%d addr=%p key_idx=%d "
+ "set_tx=%d seq_len=%lu key_len=%lu",
+ __func__, ifindex, alg, addr, key_idx, set_tx,
+ (unsigned long) seq_len, (unsigned long) key_len);
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ if (alg == WPA_ALG_NONE) {
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_DEL_KEY, 0);
+ } else {
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_NEW_KEY, 0);
+ NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key);
+ switch (alg) {
+ case WPA_ALG_WEP:
+ if (key_len == 5)
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_WEP40);
+ else
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_WEP104);
+ break;
+ case WPA_ALG_TKIP:
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_TKIP);
+ break;
+ case WPA_ALG_CCMP:
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_CCMP);
+ break;
+ case WPA_ALG_IGTK:
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_AES_CMAC);
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
+ "algorithm %d", __func__, alg);
+ nlmsg_free(msg);
+ return -1;
+ }
+ }
+
+ if (seq && seq_len)
+ NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq);
+
+ if (addr && !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 nl_msg *types;
+ int err;
+ wpa_printf(MSG_DEBUG, " broadcast key");
+ types = nlmsg_alloc();
+ if (!types)
+ goto nla_put_failure;
+ NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+ err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+ types);
+ nlmsg_free(types);
+ if (err)
+ goto nla_put_failure;
+ }
+ NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
+ ret = 0;
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s)",
+ ret, strerror(-ret));
+
+ /*
+ * If we failed or don't need to set the default TX key (below),
+ * we're done here.
+ */
+ if (ret || !set_tx || alg == WPA_ALG_NONE)
+ return ret;
+ if (drv->nlmode == NL80211_IFTYPE_AP && addr &&
+ !is_broadcast_ether_addr(addr))
+ return ret;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_KEY, 0);
+ NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, 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 nl_msg *types;
+ int err;
+ types = nlmsg_alloc();
+ if (!types)
+ goto nla_put_failure;
+ NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+ err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+ types);
+ nlmsg_free(types);
+ if (err)
+ goto nla_put_failure;
+ } else if (addr) {
+ struct nl_msg *types;
+ int err;
+ types = nlmsg_alloc();
+ if (!types)
+ goto nla_put_failure;
+ NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_UNICAST);
+ err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+ types);
+ nlmsg_free(types);
+ if (err)
+ goto nla_put_failure;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == -ENOENT)
+ ret = 0;
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: set_key default failed; "
+ "err=%d %s)", ret, strerror(-ret));
+ return ret;
+
+nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
+ int key_idx, int defkey,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY);
+ if (!key_attr)
+ return -1;
+
+ if (defkey && alg == WPA_ALG_IGTK)
+ NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_MGMT);
+ else if (defkey)
+ NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT);
+
+ NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx);
+
+ switch (alg) {
+ case WPA_ALG_WEP:
+ if (key_len == 5)
+ NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_WEP40);
+ else
+ NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_WEP104);
+ break;
+ case WPA_ALG_TKIP:
+ NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_TKIP);
+ break;
+ case WPA_ALG_CCMP:
+ NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP);
+ break;
+ case WPA_ALG_IGTK:
+ NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_AES_CMAC);
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
+ "algorithm %d", __func__, alg);
+ return -1;
+ }
+
+ if (seq && seq_len)
+ NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq);
+
+ NLA_PUT(msg, NL80211_KEY_DATA, key_len, key);
+
+ nla_nest_end(msg, key_attr);
+
+ return 0;
+ nla_put_failure:
+ return -1;
+}
+
+
+static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
+ struct nl_msg *msg)
+{
+ int i, privacy = 0;
+ struct nlattr *nl_keys, *nl_key;
+
+ for (i = 0; i < 4; i++) {
+ if (!params->wep_key[i])
+ continue;
+ privacy = 1;
+ break;
+ }
+ if (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;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, cmd, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+ if (local_state_change)
+ NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ goto nla_put_failure;
+ }
+ ret = 0;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
+ const u8 *addr, int reason_code)
+{
+ wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+ __func__, MAC2STR(addr), reason_code);
+ drv->associated = 0;
+ return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT,
+ reason_code, 0);
+}
+
+
+static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
+ return wpa_driver_nl80211_disconnect(drv, addr, reason_code);
+ wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+ __func__, MAC2STR(addr), reason_code);
+ drv->associated = 0;
+ 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 int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
+ return wpa_driver_nl80211_disconnect(drv, addr, reason_code);
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ drv->associated = 0;
+ return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISASSOCIATE,
+ reason_code, 0);
+}
+
+
+static int wpa_driver_nl80211_authenticate(
+ void *priv, struct wpa_driver_auth_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = -1, i;
+ struct nl_msg *msg;
+ enum nl80211_auth_type type;
+ int count = 0;
+
+ drv->associated = 0;
+ os_memset(drv->auth_bssid, 0, ETH_ALEN);
+ /* FIX: IBSS mode */
+ if (drv->nlmode != NL80211_IFTYPE_STATION &&
+ wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0)
+ return -1;
+
+retry:
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
+ drv->ifindex);
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_AUTHENTICATE, 0);
+
+ for (i = 0; i < 4; i++) {
+ if (!params->wep_key[i])
+ continue;
+ wpa_driver_nl80211_set_key(bss->ifname, priv, WPA_ALG_WEP,
+ NULL, i,
+ i == params->wep_tx_keyidx, NULL, 0,
+ params->wep_key[i],
+ params->wep_key_len[i]);
+ if (params->wep_tx_keyidx != i)
+ continue;
+ if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0,
+ params->wep_key[i], params->wep_key_len[i])) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ if (params->bssid) {
+ wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
+ MAC2STR(params->bssid));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+ }
+ if (params->freq) {
+ wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+ }
+ if (params->ssid) {
+ wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
+ params->ssid, params->ssid_len);
+ NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+ params->ssid);
+ }
+ wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len);
+ if (params->ie)
+ NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie);
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+ type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+ type = NL80211_AUTHTYPE_SHARED_KEY;
+ else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+ type = NL80211_AUTHTYPE_NETWORK_EAP;
+ else if (params->auth_alg & WPA_AUTH_ALG_FT)
+ type = NL80211_AUTHTYPE_FT;
+ else
+ goto nla_put_failure;
+ wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
+ NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
+ if (params->local_state_change) {
+ wpa_printf(MSG_DEBUG, " * Local state change only");
+ NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE);
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ count++;
+ if (ret == -EALREADY && count == 1 && params->bssid &&
+ !params->local_state_change) {
+ /*
+ * mac80211 does not currently accept new
+ * authentication if we are already authenticated. As a
+ * workaround, force deauthentication and try again.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Retry authentication "
+ "after forced deauthentication");
+ wpa_driver_nl80211_deauthenticate(
+ bss, params->bssid,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ nlmsg_free(msg);
+ goto retry;
+ }
+ goto nla_put_failure;
+ }
+ ret = 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Authentication request send "
+ "successfully");
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+struct phy_info_arg {
+ u16 *num_modes;
+ struct hostapd_hw_modes *modes;
+};
+
+static int phy_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct phy_info_arg *phy_info = arg;
+
+ struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
+
+ struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
+ static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+ [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
+ [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
+ };
+
+ struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
+ static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
+ [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
+ [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG },
+ };
+
+ struct nlattr *nl_band;
+ struct nlattr *nl_freq;
+ struct nlattr *nl_rate;
+ int rem_band, rem_freq, rem_rate;
+ struct hostapd_hw_modes *mode;
+ int idx, mode_is_set;
+
+ nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
+ return NL_SKIP;
+
+ nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
+ mode = os_realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode));
+ if (!mode)
+ return NL_SKIP;
+ phy_info->modes = mode;
+
+ mode_is_set = 0;
+
+ mode = &phy_info->modes[*(phy_info->num_modes)];
+ memset(mode, 0, sizeof(*mode));
+ *(phy_info->num_modes) += 1;
+
+ nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
+ nla_len(nl_band), NULL);
+
+ if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
+ mode->ht_capab = nla_get_u16(
+ tb_band[NL80211_BAND_ATTR_HT_CAPA]);
+ }
+
+ if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) {
+ mode->a_mpdu_params |= nla_get_u8(
+ tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) &
+ 0x03;
+ }
+
+ if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) {
+ mode->a_mpdu_params |= nla_get_u8(
+ tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) <<
+ 2;
+ }
+
+ if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] &&
+ nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET])) {
+ u8 *mcs;
+ mcs = nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
+ os_memcpy(mode->mcs_set, mcs, 16);
+ }
+
+ nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
+ nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
+ nla_len(nl_freq), freq_policy);
+ if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+ continue;
+ mode->num_channels++;
+ }
+
+ mode->channels = os_zalloc(mode->num_channels * sizeof(struct hostapd_channel_data));
+ if (!mode->channels)
+ return NL_SKIP;
+
+ idx = 0;
+
+ nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
+ nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
+ nla_len(nl_freq), freq_policy);
+ if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+ continue;
+
+ mode->channels[idx].freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+ mode->channels[idx].flag = 0;
+
+ if (!mode_is_set) {
+ /* crude heuristic */
+ if (mode->channels[idx].freq < 4000)
+ mode->mode = HOSTAPD_MODE_IEEE80211B;
+ else
+ mode->mode = HOSTAPD_MODE_IEEE80211A;
+ mode_is_set = 1;
+ }
+
+ /* crude heuristic */
+ if (mode->channels[idx].freq < 4000)
+ if (mode->channels[idx].freq == 2484)
+ mode->channels[idx].chan = 14;
+ else
+ mode->channels[idx].chan = (mode->channels[idx].freq - 2407) / 5;
+ else
+ mode->channels[idx].chan = mode->channels[idx].freq/5 - 1000;
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+ mode->channels[idx].flag |=
+ HOSTAPD_CHAN_DISABLED;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN])
+ mode->channels[idx].flag |=
+ HOSTAPD_CHAN_PASSIVE_SCAN;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS])
+ mode->channels[idx].flag |=
+ HOSTAPD_CHAN_NO_IBSS;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
+ mode->channels[idx].flag |=
+ HOSTAPD_CHAN_RADAR;
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] &&
+ !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+ mode->channels[idx].max_tx_power =
+ nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100;
+
+ idx++;
+ }
+
+ nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
+ nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
+ nla_len(nl_rate), rate_policy);
+ if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+ continue;
+ mode->num_rates++;
+ }
+
+ mode->rates = os_zalloc(mode->num_rates * sizeof(int));
+ if (!mode->rates)
+ return NL_SKIP;
+
+ idx = 0;
+
+ nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
+ nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
+ nla_len(nl_rate), rate_policy);
+ if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+ continue;
+ mode->rates[idx] = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
+
+ /* crude heuristic */
+ if (mode->mode == HOSTAPD_MODE_IEEE80211B &&
+ mode->rates[idx] > 200)
+ mode->mode = HOSTAPD_MODE_IEEE80211G;
+
+ idx++;
+ }
+ }
+
+ return NL_SKIP;
+}
+
+static struct hostapd_hw_modes *
+wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes)
+{
+ u16 m;
+ struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode;
+ int i, mode11g_idx = -1;
+
+ /* If only 802.11g mode is included, use it to construct matching
+ * 802.11b mode data. */
+
+ for (m = 0; m < *num_modes; m++) {
+ if (modes[m].mode == HOSTAPD_MODE_IEEE80211B)
+ return modes; /* 802.11b already included */
+ if (modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+ mode11g_idx = m;
+ }
+
+ if (mode11g_idx < 0)
+ return modes; /* 2.4 GHz band not supported at all */
+
+ nmodes = os_realloc(modes, (*num_modes + 1) * sizeof(*nmodes));
+ if (nmodes == NULL)
+ return modes; /* Could not add 802.11b mode */
+
+ mode = &nmodes[*num_modes];
+ os_memset(mode, 0, sizeof(*mode));
+ (*num_modes)++;
+ modes = nmodes;
+
+ mode->mode = HOSTAPD_MODE_IEEE80211B;
+
+ mode11g = &modes[mode11g_idx];
+ mode->num_channels = mode11g->num_channels;
+ mode->channels = os_malloc(mode11g->num_channels *
+ sizeof(struct hostapd_channel_data));
+ if (mode->channels == NULL) {
+ (*num_modes)--;
+ return modes; /* Could not add 802.11b mode */
+ }
+ os_memcpy(mode->channels, mode11g->channels,
+ mode11g->num_channels * sizeof(struct hostapd_channel_data));
+
+ mode->num_rates = 0;
+ mode->rates = os_malloc(4 * sizeof(int));
+ if (mode->rates == NULL) {
+ os_free(mode->channels);
+ (*num_modes)--;
+ return modes; /* Could not add 802.11b mode */
+ }
+
+ for (i = 0; i < mode11g->num_rates; i++) {
+ if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 &&
+ mode11g->rates[i] != 55 && mode11g->rates[i] != 110)
+ continue;
+ mode->rates[mode->num_rates] = mode11g->rates[i];
+ mode->num_rates++;
+ if (mode->num_rates == 4)
+ break;
+ }
+
+ if (mode->num_rates == 0) {
+ os_free(mode->channels);
+ os_free(mode->rates);
+ (*num_modes)--;
+ return modes; /* No 802.11b rates */
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g "
+ "information");
+
+ return modes;
+}
+
+
+static 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_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 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);
+ }
+
+ 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);
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_set_ht40_flags(struct wpa_driver_nl80211_data *drv,
+ struct phy_info_arg *results)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_GET_REG, 0);
+ 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)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct phy_info_arg result = {
+ .num_modes = num_modes,
+ .modes = NULL,
+ };
+
+ *num_modes = 0;
+ *flags = 0;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return NULL;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_GET_WIPHY, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
+ nl80211_set_ht40_flags(drv, &result);
+ return wpa_driver_nl80211_add_11b(result.modes, num_modes);
+ }
+ nla_put_failure:
+ return NULL;
+}
+
+
+static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv,
+ const void *data, size_t len,
+ int encrypt)
+{
+ __u8 rtap_hdr[] = {
+ 0x00, 0x00, /* radiotap version */
+ 0x0e, 0x00, /* radiotap length */
+ 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
+ IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
+ 0x00, /* padding */
+ 0x00, 0x00, /* RX and TX flags to indicate that */
+ 0x00, 0x00, /* this is the injected frame directly */
+ };
+ struct iovec iov[2] = {
+ {
+ .iov_base = &rtap_hdr,
+ .iov_len = sizeof(rtap_hdr),
+ },
+ {
+ .iov_base = (void *) data,
+ .iov_len = len,
+ }
+ };
+ struct msghdr msg = {
+ .msg_name = NULL,
+ .msg_namelen = 0,
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0,
+ };
+ int res;
+
+ if (encrypt)
+ rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
+
+ 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_mlme(void *priv, const u8 *data,
+ size_t data_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ieee80211_mgmt *mgmt;
+ int encrypt = 1;
+ u16 fc;
+
+ mgmt = (struct ieee80211_mgmt *) data;
+ fc = le_to_host16(mgmt->frame_control);
+
+ if (drv->nlmode == NL80211_IFTYPE_STATION &&
+ 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.
+ */
+ return nl80211_send_frame_cmd(drv, drv->last_mgmt_freq, 0,
+ data, data_len, NULL);
+ }
+
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
+ /*
+ * Only one of the authentication frame types is encrypted.
+ * In order for static WEP encryption to work properly (i.e.,
+ * to not encrypt the frame), we need to tell mac80211 about
+ * the frames that must not be encrypted.
+ */
+ u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+ u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction);
+ if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3)
+ encrypt = 0;
+ }
+
+ return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt);
+}
+
+
+static int wpa_driver_nl80211_set_beacon(void *priv,
+ const u8 *head, size_t head_len,
+ const u8 *tail, size_t tail_len,
+ int dtim_period, int beacon_int)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ u8 cmd = NL80211_CMD_NEW_BEACON;
+ int ret;
+ int beacon_set;
+ int ifindex = if_nametoindex(bss->ifname);
+
+ beacon_set = bss->beacon_set;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
+ beacon_set);
+ if (beacon_set)
+ cmd = NL80211_CMD_SET_BEACON;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, cmd, 0);
+ NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head);
+ NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, beacon_int);
+ NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
+ ret, strerror(-ret));
+ } else {
+ bss->beacon_set = 1;
+ }
+ return ret;
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv,
+ int freq, int ht_enabled,
+ int sec_channel_offset)
+{
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_SET_WIPHY, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+ if (ht_enabled) {
+ switch (sec_channel_offset) {
+ case -1:
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ NL80211_CHAN_HT40MINUS);
+ break;
+ case 1:
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ NL80211_CHAN_HT40PLUS);
+ break;
+ default:
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ NL80211_CHAN_HT20);
+ break;
+ }
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == 0)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
+ "%d (%s)", freq, ret, strerror(-ret));
+nla_put_failure:
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_sta_add(void *priv,
+ struct hostapd_sta_add_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_NEW_STATION, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr);
+ NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
+ NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len,
+ params->supp_rates);
+ NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+ params->listen_interval);
+ if (params->ht_capabilities) {
+ NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY,
+ sizeof(*params->ht_capabilities),
+ params->ht_capabilities);
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION "
+ "result: %d (%s)", ret, strerror(-ret));
+ if (ret == -EEXIST)
+ ret = 0;
+ nla_put_failure:
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_DEL_STATION, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+ if_nametoindex(bss->ifname));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == -ENOENT)
+ return 0;
+ return ret;
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
+ int ifidx)
+{
+ struct nl_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
+
+#ifdef HOSTAPD
+ /* stop listening for EAPOL on this interface */
+ del_ifidx(drv, ifidx);
+#endif /* HOSTAPD */
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ goto nla_put_failure;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_DEL_INTERFACE, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx);
+
+ if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+ return;
+ nla_put_failure:
+ wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
+}
+
+
+static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
+ const char *ifname,
+ enum nl80211_iftype iftype,
+ const u8 *addr, int wds)
+{
+ struct nl_msg *msg, *flags = NULL;
+ int ifidx;
+ int ret = -ENOBUFS;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_NEW_INTERFACE, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype);
+
+ if (iftype == NL80211_IFTYPE_MONITOR) {
+ int err;
+
+ flags = nlmsg_alloc();
+ if (!flags)
+ goto nla_put_failure;
+
+ NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES);
+
+ err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags);
+
+ nlmsg_free(flags);
+
+ if (err)
+ goto nla_put_failure;
+ } else if (wds) {
+ NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds);
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret) {
+ nla_put_failure:
+ wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
+ ifname, ret, strerror(-ret));
+ return ret;
+ }
+
+ ifidx = if_nametoindex(ifname);
+ wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d",
+ ifname, ifidx);
+
+ if (ifidx <= 0)
+ return -1;
+
+#ifdef HOSTAPD
+ /* start listening for EAPOL on this interface */
+ add_ifidx(drv, ifidx);
+#endif /* HOSTAPD */
+
+ if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+ linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) {
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+
+ return ifidx;
+}
+
+
+static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+ const char *ifname, enum nl80211_iftype iftype,
+ const u8 *addr, int wds)
+{
+ int ret;
+
+ ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds);
+
+ /* if error occured and interface exists already */
+ if (ret == -ENFILE && if_nametoindex(ifname)) {
+ wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname);
+
+ /* Try to remove the interface that was already there. */
+ nl80211_remove_iface(drv, if_nametoindex(ifname));
+
+ /* Try to create the interface again */
+ ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
+ wds);
+ }
+
+ if (ret >= 0 && drv->disable_11b_rates)
+ nl80211_disable_11b_rates(drv, ret, 1);
+
+ return ret;
+}
+
+
+static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
+{
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+ union wpa_event_data event;
+
+ hdr = (struct ieee80211_hdr *) buf;
+ fc = le_to_host16(hdr->frame_control);
+
+ os_memset(&event, 0, sizeof(event));
+ event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+ event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+ event.tx_status.dst = hdr->addr1;
+ event.tx_status.data = buf;
+ event.tx_status.data_len = len;
+ event.tx_status.ack = ok;
+ wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
+ u8 *buf, size_t len)
+{
+ union wpa_event_data event;
+ os_memset(&event, 0, sizeof(event));
+ event.rx_from_unknown.frame = buf;
+ event.rx_from_unknown.len = len;
+ wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+}
+
+
+static void handle_frame(struct wpa_driver_nl80211_data *drv,
+ u8 *buf, size_t len, int datarate, int ssi_signal)
+{
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+ union wpa_event_data event;
+
+ hdr = (struct ieee80211_hdr *) buf;
+ fc = le_to_host16(hdr->frame_control);
+
+ switch (WLAN_FC_GET_TYPE(fc)) {
+ case WLAN_FC_TYPE_MGMT:
+ os_memset(&event, 0, sizeof(event));
+ event.rx_mgmt.frame = buf;
+ event.rx_mgmt.frame_len = len;
+ event.rx_mgmt.datarate = datarate;
+ event.rx_mgmt.ssi_signal = ssi_signal;
+ wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+ break;
+ case WLAN_FC_TYPE_CTRL:
+ /* can only get here with PS-Poll frames */
+ wpa_printf(MSG_DEBUG, "CTRL");
+ from_unknown_sta(drv, buf, len);
+ break;
+ case WLAN_FC_TYPE_DATA:
+ from_unknown_sta(drv, buf, len);
+ break;
+ }
+}
+
+
+static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ int len;
+ unsigned char buf[3000];
+ struct ieee80211_radiotap_iterator iter;
+ int ret;
+ int datarate = 0, ssi_signal = 0;
+ int injected = 0, failed = 0, rxflags = 0;
+
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ perror("recv");
+ return;
+ }
+
+ if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
+ printf("received invalid radiotap frame\n");
+ return;
+ }
+
+ while (1) {
+ ret = ieee80211_radiotap_iterator_next(&iter);
+ if (ret == -ENOENT)
+ break;
+ if (ret) {
+ printf("received invalid radiotap frame (%d)\n", ret);
+ return;
+ }
+ switch (iter.this_arg_index) {
+ case IEEE80211_RADIOTAP_FLAGS:
+ if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
+ len -= 4;
+ break;
+ case IEEE80211_RADIOTAP_RX_FLAGS:
+ rxflags = 1;
+ break;
+ case IEEE80211_RADIOTAP_TX_FLAGS:
+ injected = 1;
+ failed = le_to_host16((*(uint16_t *) iter.this_arg)) &
+ IEEE80211_RADIOTAP_F_TX_FAIL;
+ break;
+ case IEEE80211_RADIOTAP_DATA_RETRIES:
+ break;
+ case IEEE80211_RADIOTAP_CHANNEL:
+ /* TODO: convert from freq/flags to channel number */
+ break;
+ case IEEE80211_RADIOTAP_RATE:
+ datarate = *iter.this_arg * 5;
+ break;
+ case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
+ ssi_signal = *iter.this_arg;
+ break;
+ }
+ }
+
+ if (rxflags && injected)
+ return;
+
+ if (!injected)
+ handle_frame(drv, buf + iter.max_length,
+ len - iter.max_length, datarate, ssi_signal);
+ else
+ handle_tx_callback(drv->ctx, buf + iter.max_length,
+ len - iter.max_length, !failed);
+}
+
+
+/*
+ * we post-process the filter code later and rewrite
+ * this to the offset to the last instruction
+ */
+#define PASS 0xFF
+#define FAIL 0xFE
+
+static struct sock_filter msock_filter_insns[] = {
+ /*
+ * do a little-endian load of the radiotap length field
+ */
+ /* load lower byte into A */
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2),
+ /* put it into X (== index register) */
+ BPF_STMT(BPF_MISC| BPF_TAX, 0),
+ /* load upper byte into A */
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3),
+ /* left-shift it by 8 */
+ BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8),
+ /* or with X */
+ BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0),
+ /* put result into X */
+ BPF_STMT(BPF_MISC| BPF_TAX, 0),
+
+ /*
+ * Allow management frames through, this also gives us those
+ * management frames that we sent ourselves with status
+ */
+ /* load the lower byte of the IEEE 802.11 frame control field */
+ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
+ /* mask off frame type and version */
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF),
+ /* accept frame if it's both 0, fall through otherwise */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0),
+
+ /*
+ * TODO: add a bit to radiotap RX flags that indicates
+ * that the sending station is not associated, then
+ * add a filter here that filters on our DA and that flag
+ * to allow us to deauth frames to that bad station.
+ *
+ * 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 = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]),
+ .filter = msock_filter_insns,
+};
+
+
+static int add_monitor_filter(int s)
+{
+ int idx;
+
+ /* rewrite all PASS/FAIL jump offsets */
+ for (idx = 0; idx < msock_filter.len; idx++) {
+ struct sock_filter *insn = &msock_filter_insns[idx];
+
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
+ if (insn->code == (BPF_JMP|BPF_JA)) {
+ if (insn->k == PASS)
+ insn->k = msock_filter.len - idx - 2;
+ else if (insn->k == FAIL)
+ insn->k = msock_filter.len - idx - 3;
+ }
+
+ if (insn->jt == PASS)
+ insn->jt = msock_filter.len - idx - 2;
+ else if (insn->jt == FAIL)
+ insn->jt = msock_filter.len - idx - 3;
+
+ if (insn->jf == PASS)
+ insn->jf = msock_filter.len - idx - 2;
+ else if (insn->jf == FAIL)
+ insn->jf = msock_filter.len - idx - 3;
+ }
+ }
+
+ if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
+ &msock_filter, sizeof(msock_filter))) {
+ perror("SO_ATTACH_FILTER");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void nl80211_remove_monitor_interface(
+ struct wpa_driver_nl80211_data *drv)
+{
+ if (drv->monitor_ifidx >= 0) {
+ nl80211_remove_iface(drv, drv->monitor_ifidx);
+ drv->monitor_ifidx = -1;
+ }
+ if (drv->monitor_sock >= 0) {
+ eloop_unregister_read_sock(drv->monitor_sock);
+ close(drv->monitor_sock);
+ drv->monitor_sock = -1;
+ }
+}
+
+
+static int
+nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+ char buf[IFNAMSIZ];
+ struct sockaddr_ll ll;
+ int optval;
+ socklen_t optlen;
+
+ snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname);
+ buf[IFNAMSIZ - 1] = '\0';
+
+ drv->monitor_ifidx =
+ nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
+ 0);
+
+ if (drv->monitor_ifidx < 0)
+ return -1;
+
+ if (linux_set_iface_flags(drv->ioctl_sock, buf, 1))
+ goto error;
+
+ memset(&ll, 0, sizeof(ll));
+ ll.sll_family = AF_PACKET;
+ ll.sll_ifindex = drv->monitor_ifidx;
+ drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (drv->monitor_sock < 0) {
+ perror("socket[PF_PACKET,SOCK_RAW]");
+ goto error;
+ }
+
+ if (add_monitor_filter(drv->monitor_sock)) {
+ wpa_printf(MSG_INFO, "Failed to set socket filter for monitor "
+ "interface; do filtering in user space");
+ /* This works, but will cost in performance. */
+ }
+
+ if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+ perror("monitor socket bind");
+ goto error;
+ }
+
+ optlen = sizeof(optval);
+ optval = 20;
+ if (setsockopt
+ (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
+ perror("Failed to set socket priority");
+ goto error;
+ }
+
+ if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read,
+ drv, NULL)) {
+ printf("Could not register monitor read socket\n");
+ goto error;
+ }
+
+ return 0;
+ error:
+ nl80211_remove_monitor_interface(drv);
+ return -1;
+}
+
+
+static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+static int wpa_driver_nl80211_hapd_send_eapol(
+ void *priv, const u8 *addr, const u8 *data,
+ size_t data_len, int encrypt, const u8 *own_addr, 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;
+
+ len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
+ data_len;
+ hdr = os_zalloc(len);
+ if (hdr == NULL) {
+ printf("malloc() failed for i802_send_data(len=%lu)\n",
+ (unsigned long) len);
+ return -1;
+ }
+
+ hdr->frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
+ hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
+ if (encrypt)
+ hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+ if (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) {
+ /* add an empty QoS header if needed */
+ pos[0] = 0;
+ 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(drv, (u8 *) hdr, len, encrypt);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
+ "failed: %d (%s)",
+ (unsigned long) len, errno, strerror(errno));
+ }
+ os_free(hdr);
+
+ return res;
+}
+
+
+static u32 sta_flags_nl80211(int flags)
+{
+ u32 f = 0;
+
+ if (flags & WPA_STA_AUTHORIZED)
+ f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+ if (flags & WPA_STA_WMM)
+ f |= BIT(NL80211_STA_FLAG_WME);
+ if (flags & WPA_STA_SHORT_PREAMBLE)
+ f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
+ if (flags & WPA_STA_MFP)
+ f |= BIT(NL80211_STA_FLAG_MFP);
+
+ return f;
+}
+
+
+static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
+ int total_flags,
+ int flags_or, int flags_and)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg, *flags = NULL;
+ struct nl80211_sta_flag_update upd;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ flags = nlmsg_alloc();
+ if (!flags) {
+ nlmsg_free(msg);
+ return -ENOMEM;
+ }
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_STATION, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+ if_nametoindex(bss->ifname));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+ /*
+ * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This
+ * can be removed eventually.
+ */
+ if (total_flags & WPA_STA_AUTHORIZED)
+ NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED);
+
+ if (total_flags & WPA_STA_WMM)
+ NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME);
+
+ if (total_flags & WPA_STA_SHORT_PREAMBLE)
+ NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE);
+
+ if (total_flags & WPA_STA_MFP)
+ NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP);
+
+ if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags))
+ goto nla_put_failure;
+
+ os_memset(&upd, 0, sizeof(upd));
+ upd.mask = sta_flags_nl80211(flags_or | ~flags_and);
+ upd.set = sta_flags_nl80211(flags_or);
+ NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+
+ nlmsg_free(flags);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ nlmsg_free(flags);
+ return -ENOBUFS;
+}
+
+
+static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params)
+{
+ if (params->p2p)
+ wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
+ "group (GO)");
+ if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode) ||
+ wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) {
+ nl80211_remove_monitor_interface(drv);
+ return -1;
+ }
+
+ /* TODO: setup monitor interface (and add code somewhere to remove this
+ * when AP mode is stopped; associate with mode != 2 or drv_deinit) */
+
+ return 0;
+}
+
+
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+ int ret = -1;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_LEAVE_IBSS, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ goto nla_put_failure;
+ }
+
+ ret = 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS request sent successfully");
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params)
+{
+ struct nl_msg *msg;
+ int ret = -1;
+ int count = 0;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
+
+ if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode)) {
+ wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
+ "IBSS mode");
+ return -1;
+ }
+
+retry:
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_JOIN_IBSS, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
+ goto nla_put_failure;
+
+ wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
+ params->ssid, params->ssid_len);
+ NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+ params->ssid);
+ os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+ drv->ssid_len = params->ssid_len;
+
+ wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+
+ ret = nl80211_set_conn_keys(params, msg);
+ if (ret)
+ goto nla_put_failure;
+
+ if (params->wpa_ie) {
+ wpa_hexdump(MSG_DEBUG,
+ " * Extra IEs for Beacon/Probe Response frames",
+ params->wpa_ie, params->wpa_ie_len);
+ NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+ params->wpa_ie);
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ count++;
+ if (ret == -EALREADY && count == 1) {
+ wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after "
+ "forced leave");
+ nl80211_leave_ibss(drv);
+ nlmsg_free(msg);
+ goto retry;
+ }
+
+ goto nla_put_failure;
+ }
+ ret = 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Join IBSS request sent successfully");
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_connect(
+ struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params)
+{
+ struct nl_msg *msg;
+ enum nl80211_auth_type type;
+ int ret = 0;
+ int algs;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_CONNECT, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ if (params->bssid) {
+ wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
+ MAC2STR(params->bssid));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+ }
+ if (params->freq) {
+ wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+ }
+ if (params->ssid) {
+ wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
+ params->ssid, params->ssid_len);
+ NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+ params->ssid);
+ if (params->ssid_len > sizeof(drv->ssid))
+ goto nla_put_failure;
+ os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+ drv->ssid_len = params->ssid_len;
+ }
+ wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len);
+ if (params->wpa_ie)
+ NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+ params->wpa_ie);
+
+ 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_ie && params->wpa_ie_len) {
+ enum nl80211_wpa_versions ver;
+
+ if (params->wpa_ie[0] == WLAN_EID_RSN)
+ ver = NL80211_WPA_VERSION_2;
+ else
+ ver = NL80211_WPA_VERSION_1;
+
+ wpa_printf(MSG_DEBUG, " * WPA Version %d", ver);
+ NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
+ }
+
+ if (params->pairwise_suite != CIPHER_NONE) {
+ int cipher;
+
+ switch (params->pairwise_suite) {
+ case CIPHER_WEP40:
+ cipher = WLAN_CIPHER_SUITE_WEP40;
+ break;
+ case CIPHER_WEP104:
+ cipher = WLAN_CIPHER_SUITE_WEP104;
+ break;
+ case CIPHER_CCMP:
+ cipher = WLAN_CIPHER_SUITE_CCMP;
+ break;
+ case CIPHER_TKIP:
+ default:
+ cipher = WLAN_CIPHER_SUITE_TKIP;
+ break;
+ }
+ NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
+ }
+
+ if (params->group_suite != CIPHER_NONE) {
+ int cipher;
+
+ switch (params->group_suite) {
+ case CIPHER_WEP40:
+ cipher = WLAN_CIPHER_SUITE_WEP40;
+ break;
+ case CIPHER_WEP104:
+ cipher = WLAN_CIPHER_SUITE_WEP104;
+ break;
+ case CIPHER_CCMP:
+ cipher = WLAN_CIPHER_SUITE_CCMP;
+ break;
+ case CIPHER_TKIP:
+ default:
+ cipher = WLAN_CIPHER_SUITE_TKIP;
+ break;
+ }
+ NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
+ }
+
+ if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
+ params->key_mgmt_suite == KEY_MGMT_PSK) {
+ int mgmt = WLAN_AKM_SUITE_PSK;
+
+ switch (params->key_mgmt_suite) {
+ case KEY_MGMT_802_1X:
+ mgmt = WLAN_AKM_SUITE_8021X;
+ break;
+ case KEY_MGMT_PSK:
+ default:
+ mgmt = WLAN_AKM_SUITE_PSK;
+ break;
+ }
+ NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
+ }
+
+ ret = nl80211_set_conn_keys(params, msg);
+ if (ret)
+ goto nla_put_failure;
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ goto nla_put_failure;
+ }
+ ret = 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully");
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+
+}
+
+
+static int wpa_driver_nl80211_associate(
+ void *priv, struct wpa_driver_associate_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = -1;
+ struct nl_msg *msg;
+
+ if (params->mode == IEEE80211_MODE_AP)
+ return wpa_driver_nl80211_ap(drv, params);
+
+ if (params->mode == IEEE80211_MODE_IBSS)
+ return wpa_driver_nl80211_ibss(drv, params);
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+ if (wpa_driver_nl80211_set_mode(priv, params->mode) < 0)
+ return -1;
+ return wpa_driver_nl80211_connect(drv, params);
+ }
+
+ drv->associated = 0;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
+ drv->ifindex);
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_ASSOCIATE, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ if (params->bssid) {
+ wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
+ MAC2STR(params->bssid));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+ }
+ if (params->freq) {
+ wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+ drv->assoc_freq = params->freq;
+ } else
+ drv->assoc_freq = 0;
+ if (params->ssid) {
+ wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
+ params->ssid, params->ssid_len);
+ NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+ params->ssid);
+ if (params->ssid_len > sizeof(drv->ssid))
+ goto nla_put_failure;
+ os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+ drv->ssid_len = params->ssid_len;
+ }
+ wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len);
+ if (params->wpa_ie)
+ NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+ params->wpa_ie);
+
+ if (params->pairwise_suite != CIPHER_NONE) {
+ int cipher;
+
+ switch (params->pairwise_suite) {
+ case CIPHER_WEP40:
+ cipher = WLAN_CIPHER_SUITE_WEP40;
+ break;
+ case CIPHER_WEP104:
+ cipher = WLAN_CIPHER_SUITE_WEP104;
+ break;
+ case CIPHER_CCMP:
+ cipher = WLAN_CIPHER_SUITE_CCMP;
+ break;
+ case CIPHER_TKIP:
+ default:
+ cipher = WLAN_CIPHER_SUITE_TKIP;
+ break;
+ }
+ 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_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->p2p)
+ wpa_printf(MSG_DEBUG, " * P2P group");
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ nl80211_dump_scan(drv);
+ goto nla_put_failure;
+ }
+ ret = 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Association request send "
+ "successfully");
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
+ int ifindex, int mode)
+{
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_INTERFACE, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (!ret)
+ return 0;
+nla_put_failure:
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
+ " %d (%s)", ifindex, mode, ret, strerror(-ret));
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_set_mode(void *priv, int mode)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = -1;
+ int nlmode;
+ int i;
+
+ switch (mode) {
+ case 0:
+ nlmode = NL80211_IFTYPE_STATION;
+ break;
+ case 1:
+ nlmode = NL80211_IFTYPE_ADHOC;
+ break;
+ case 2:
+ nlmode = NL80211_IFTYPE_AP;
+ break;
+ default:
+ return -1;
+ }
+
+ if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) {
+ drv->nlmode = nlmode;
+ ret = 0;
+ goto done;
+ }
+
+ if (nlmode == drv->nlmode) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
+ "requested mode - ignore error");
+ ret = 0;
+ goto done; /* Already in the requested mode */
+ }
+
+ /* mac80211 doesn't allow mode changes while the device is up, so
+ * take the device down, try to set the mode again, and bring the
+ * device back up.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
+ "interface down");
+ for (i = 0; i < 10; i++) {
+ if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) ==
+ 0) {
+ /* Try to set the mode again while the interface is
+ * down */
+ ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
+ if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname,
+ 1))
+ ret = -1;
+ if (!ret)
+ 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;
+ }
+
+done:
+ if (!ret && nlmode == NL80211_IFTYPE_AP) {
+ /* Setup additional AP mode functionality if needed */
+ if (drv->monitor_ifidx < 0 &&
+ nl80211_create_monitor_interface(drv))
+ return -1;
+ } else if (!ret && nlmode != NL80211_IFTYPE_AP) {
+ /* Remove additional AP mode functionality */
+ nl80211_remove_monitor_interface(drv);
+ bss->beacon_set = 0;
+ }
+
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
+ "from %d failed", nlmode, drv->nlmode);
+
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_get_capa(void *priv,
+ struct wpa_driver_capa *capa)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (!drv->has_capability)
+ return -1;
+ os_memcpy(capa, &drv->capa, sizeof(*capa));
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_set_operstate(void *priv, int state)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
+ __func__, drv->operstate, state, state ? "UP" : "DORMANT");
+ drv->operstate = state;
+ return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+ state ? IF_OPER_UP : IF_OPER_DORMANT);
+}
+
+
+static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nl80211_sta_flag_update upd;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_STATION, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+ if_nametoindex(bss->ifname));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
+
+ os_memset(&upd, 0, sizeof(upd));
+ upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
+ if (authorized)
+ upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
+ NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+#ifdef HOSTAPD
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+ int i;
+ int *old;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
+ ifidx);
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if (drv->if_indices[i] == 0) {
+ drv->if_indices[i] = ifidx;
+ return;
+ }
+ }
+
+ if (drv->if_indices != drv->default_if_indices)
+ old = drv->if_indices;
+ else
+ old = NULL;
+
+ drv->if_indices = os_realloc(old,
+ sizeof(int) * (drv->num_if_indices + 1));
+ if (!drv->if_indices) {
+ if (!old)
+ drv->if_indices = drv->default_if_indices;
+ else
+ drv->if_indices = old;
+ wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
+ "interfaces");
+ wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
+ return;
+ } else if (!old)
+ os_memcpy(drv->if_indices, drv->default_if_indices,
+ sizeof(drv->default_if_indices));
+ drv->if_indices[drv->num_if_indices] = ifidx;
+ drv->num_if_indices++;
+}
+
+
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+ int i;
+
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if (drv->if_indices[i] == ifidx) {
+ drv->if_indices[i] = 0;
+ break;
+ }
+ }
+}
+
+
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+ int i;
+
+ for (i = 0; i < drv->num_if_indices; i++)
+ if (drv->if_indices[i] == ifidx)
+ return 1;
+
+ return 0;
+}
+
+
+static inline int min_int(int a, int b)
+{
+ if (a < b)
+ return a;
+ return b;
+}
+
+
+static int get_key_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ /*
+ * TODO: validate the key index and mac address!
+ * Otherwise, there's a race condition as soon as
+ * the kernel starts sending key notifications.
+ */
+
+ if (tb[NL80211_ATTR_KEY_SEQ])
+ memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]),
+ min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6));
+ return NL_SKIP;
+}
+
+
+static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
+ int idx, u8 *seq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_GET_KEY, 0);
+
+ if (addr)
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+ NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface));
+
+ memset(seq, 0, 6);
+
+ return send_and_recv_msgs(drv, msg, get_key_handler, seq);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates,
+ int mode)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ u8 rates[NL80211_MAX_SUPP_RATES];
+ u8 rates_len = 0;
+ int i;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_SET_BSS, 0);
+
+ for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
+ rates[rates_len++] = basic_rates[i] / 5;
+
+ NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+#endif /* HOSTAPD */
+
+
+/* Set kernel driver on given frequency (MHz) */
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled,
+ freq->sec_channel_offset);
+}
+
+
+#ifdef HOSTAPD
+
+static int i802_set_rts(void *priv, int rts)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+ u32 val;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ if (rts >= 2347)
+ val = (u32) -1;
+ else
+ val = rts;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_WIPHY, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (!ret)
+ return 0;
+nla_put_failure:
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
+ "%d (%s)", rts, ret, strerror(-ret));
+ return ret;
+}
+
+
+static int i802_set_frag(void *priv, int frag)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+ u32 val;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ if (frag >= 2346)
+ val = (u32) -1;
+ else
+ val = frag;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_WIPHY, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (!ret)
+ return 0;
+nla_put_failure:
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
+ "%d: %d (%s)", frag, ret, strerror(-ret));
+ return ret;
+}
+
+
+static int i802_flush(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_DEL_STATION, 0);
+
+ /*
+ * XXX: FIX! this needs to flush all VLANs too
+ */
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+ if_nametoindex(bss->ifname));
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int get_sta_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct hostap_sta_driver_data *data = arg;
+ struct nlattr *stats[NL80211_STA_INFO_MAX + 1];
+ static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+ [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+ };
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ /*
+ * TODO: validate the interface and mac address!
+ * Otherwise, there's a race condition as soon as
+ * the kernel starts sending station notifications.
+ */
+
+ if (!tb[NL80211_ATTR_STA_INFO]) {
+ wpa_printf(MSG_DEBUG, "sta stats missing!");
+ return NL_SKIP;
+ }
+ if (nla_parse_nested(stats, NL80211_STA_INFO_MAX,
+ tb[NL80211_ATTR_STA_INFO],
+ stats_policy)) {
+ wpa_printf(MSG_DEBUG, "failed to parse nested attributes!");
+ return NL_SKIP;
+ }
+
+ if (stats[NL80211_STA_INFO_INACTIVE_TIME])
+ data->inactive_msec =
+ nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]);
+ if (stats[NL80211_STA_INFO_RX_BYTES])
+ data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]);
+ if (stats[NL80211_STA_INFO_TX_BYTES])
+ data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]);
+ if (stats[NL80211_STA_INFO_RX_PACKETS])
+ data->rx_packets =
+ nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
+ if (stats[NL80211_STA_INFO_TX_PACKETS])
+ data->tx_packets =
+ nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]);
+
+ return NL_SKIP;
+}
+
+static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ os_memset(data, 0, sizeof(*data));
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_GET_STATION, 0);
+
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+
+ return send_and_recv_msgs(drv, msg, get_sta_handler, data);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
+ int cw_min, int cw_max, int burst_time)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *txq, *params;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_WIPHY, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+
+ txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS);
+ if (!txq)
+ goto nla_put_failure;
+
+ /* We are only sending parameters for a single TXQ at a time */
+ params = nla_nest_start(msg, 1);
+ if (!params)
+ goto nla_put_failure;
+
+ 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;
+ nla_put_failure:
+ return -1;
+}
+
+
+static int i802_set_bss(void *priv, int cts, int preamble, int slot,
+ int ht_opmode)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_SET_BSS, 0);
+
+ if (cts >= 0)
+ NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts);
+ if (preamble >= 0)
+ NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble);
+ if (slot >= 0)
+ NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
+ if (ht_opmode >= 0)
+ NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int i802_set_cts_protect(void *priv, int value)
+{
+ return i802_set_bss(priv, value, -1, -1, -1);
+}
+
+
+static int i802_set_preamble(void *priv, int value)
+{
+ return i802_set_bss(priv, -1, value, -1, -1);
+}
+
+
+static int i802_set_short_slot_time(void *priv, int value)
+{
+ return i802_set_bss(priv, -1, -1, value, -1);
+}
+
+
+static int i802_set_sta_vlan(void *priv, const u8 *addr,
+ const char *ifname, int vlan_id)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_STATION, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+ if_nametoindex(bss->ifname));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+ NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN,
+ if_nametoindex(ifname));
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
+ MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
+ MAC2STR(addr), ifname, vlan_id, ret,
+ strerror(-ret));
+ }
+ nla_put_failure:
+ return ret;
+}
+
+
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+ const char *bridge_ifname)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ char name[IFNAMSIZ + 1];
+
+ os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+ wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+ " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+ if (val) {
+ if (!if_nametoindex(name)) {
+ if (nl80211_create_iface(drv, name,
+ NL80211_IFTYPE_AP_VLAN,
+ NULL, 1) < 0)
+ return -1;
+ if (bridge_ifname &&
+ linux_br_add_if(drv->ioctl_sock, bridge_ifname,
+ name) < 0)
+ return -1;
+ }
+ linux_set_iface_flags(drv->ioctl_sock, name, 1);
+ return i802_set_sta_vlan(priv, addr, name, 0);
+ } else {
+ i802_set_sta_vlan(priv, addr, bss->ifname, 0);
+ return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
+ name);
+ }
+}
+
+
+static int i802_set_ht_params(void *priv, const u8 *ht_capab,
+ size_t ht_capab_len, const u8 *ht_oper,
+ size_t ht_oper_len)
+{
+ if (ht_oper_len >= 6) {
+ /* ht opmode uses 16bit in octet 5 & 6 */
+ u16 ht_opmode = le_to_host16(((u16 *) ht_oper)[2]);
+ return i802_set_bss(priv, -1, -1, -1, ht_opmode);
+ } else
+ return -1;
+}
+
+
+static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ struct sockaddr_ll lladdr;
+ unsigned char buf[3000];
+ int len;
+ socklen_t fromlen = sizeof(lladdr);
+
+ len = recvfrom(sock, buf, sizeof(buf), 0,
+ (struct sockaddr *)&lladdr, &fromlen);
+ if (len < 0) {
+ perror("recv");
+ return;
+ }
+
+ if (have_ifidx(drv, lladdr.sll_ifindex))
+ drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+}
+
+
+static int i802_get_inact_sec(void *priv, const u8 *addr)
+{
+ struct hostap_sta_driver_data data;
+ int ret;
+
+ data.inactive_msec = (unsigned long) -1;
+ ret = i802_read_sta_data(priv, &data, addr);
+ if (ret || data.inactive_msec == (unsigned long) -1)
+ return -1;
+ return data.inactive_msec / 1000;
+}
+
+
+static int i802_sta_clear_stats(void *priv, const u8 *addr)
+{
+#if 0
+ /* TODO */
+#endif
+ return 0;
+}
+
+#endif /* HOSTAPD */
+
+#if defined(HOSTAPD) || defined(CONFIG_AP)
+
+static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason)
+{
+ struct i802_bss *bss = priv;
+ struct ieee80211_mgmt mgmt;
+
+ memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_DEAUTH);
+ memcpy(mgmt.da, addr, ETH_ALEN);
+ memcpy(mgmt.sa, own_addr, ETH_ALEN);
+ memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+ mgmt.u.deauth.reason_code = host_to_le16(reason);
+ return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+ IEEE80211_HDRLEN +
+ sizeof(mgmt.u.deauth));
+}
+
+
+static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason)
+{
+ struct i802_bss *bss = priv;
+ struct ieee80211_mgmt mgmt;
+
+ memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_DISASSOC);
+ memcpy(mgmt.da, addr, ETH_ALEN);
+ memcpy(mgmt.sa, own_addr, ETH_ALEN);
+ memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+ mgmt.u.disassoc.reason_code = host_to_le16(reason);
+ return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+ IEEE80211_HDRLEN +
+ sizeof(mgmt.u.disassoc));
+}
+
+#endif /* HOSTAPD || CONFIG_AP */
+
+#ifdef HOSTAPD
+
+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->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->ioctl_sock, in_br, ifname) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to "
+ "remove interface %s from bridge "
+ "%s: %s",
+ ifname, brname, strerror(errno));
+ return -1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
+ ifname, brname);
+ if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
+ "into bridge %s: %s",
+ ifname, brname, strerror(errno));
+ return -1;
+ }
+ 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_init(hapd, params->ifname, NULL);
+ if (bss == NULL)
+ return NULL;
+
+ drv = bss->drv;
+ drv->nlmode = NL80211_IFTYPE_AP;
+ if (linux_br_get(brname, params->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
+ params->ifname, brname);
+ br_ifindex = if_nametoindex(brname);
+ } else {
+ brname[0] = '\0';
+ br_ifindex = 0;
+ }
+
+ drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
+ drv->if_indices = drv->default_if_indices;
+ for (i = 0; i < params->num_bridge; i++) {
+ if (params->bridge[i]) {
+ ifindex = if_nametoindex(params->bridge[i]);
+ if (ifindex)
+ add_ifidx(drv, ifindex);
+ if (ifindex == br_ifindex)
+ br_added = 1;
+ }
+ }
+ if (!br_added && br_ifindex &&
+ (params->num_bridge == 0 || !params->bridge[0]))
+ add_ifidx(drv, br_ifindex);
+
+ /* start listening for EAPOL on the default AP interface */
+ add_ifidx(drv, drv->ifindex);
+
+ if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0))
+ goto failed;
+
+ if (params->bssid) {
+ if (linux_set_ifhwaddr(drv->ioctl_sock, bss->ifname,
+ params->bssid))
+ goto failed;
+ }
+
+ if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_AP)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s "
+ "into AP mode", bss->ifname);
+ goto failed;
+ }
+
+ if (params->num_bridge && params->bridge[0] &&
+ i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0)
+ goto failed;
+
+ if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
+ goto failed;
+
+ drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
+ if (drv->eapol_sock < 0) {
+ perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)");
+ goto failed;
+ }
+
+ if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
+ {
+ printf("Could not register read socket for eapol\n");
+ goto failed;
+ }
+
+ if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, params->own_addr))
+ goto failed;
+
+ return bss;
+
+failed:
+ nl80211_remove_monitor_interface(drv);
+ rfkill_deinit(drv->rfkill);
+ netlink_deinit(drv->netlink);
+ if (drv->ioctl_sock >= 0)
+ close(drv->ioctl_sock);
+
+ genl_family_put(drv->nl80211);
+ nl_cache_free(drv->nl_cache);
+ nl80211_handle_destroy(drv->nl_handle);
+ nl_cb_put(drv->nl_cb);
+ eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
+
+ os_free(drv);
+ return NULL;
+}
+
+
+static void i802_deinit(void *priv)
+{
+ wpa_driver_nl80211_deinit(priv);
+}
+
+#endif /* HOSTAPD */
+
+
+static enum nl80211_iftype wpa_driver_nl80211_if_type(
+ enum wpa_driver_if_type type)
+{
+ switch (type) {
+ case WPA_IF_STATION:
+ return NL80211_IFTYPE_STATION;
+ case WPA_IF_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;
+ }
+ 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->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->addr, ETH_ALEN);
+ for (idx = 0; idx < 64; idx++) {
+ new_addr[0] = drv->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 */
+
+
+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)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ifidx;
+#ifdef HOSTAPD
+ struct i802_bss *new_bss = NULL;
+
+ if (type == WPA_IF_AP_BSS) {
+ new_bss = os_zalloc(sizeof(*new_bss));
+ if (new_bss == NULL)
+ return -1;
+ }
+#endif /* HOSTAPD */
+
+ if (addr)
+ os_memcpy(if_addr, addr, ETH_ALEN);
+ ifidx = nl80211_create_iface(drv, ifname,
+ wpa_driver_nl80211_if_type(type), addr,
+ 0);
+ if (ifidx < 0) {
+#ifdef HOSTAPD
+ os_free(new_bss);
+#endif /* HOSTAPD */
+ return -1;
+ }
+
+ if (!addr &&
+ linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0) {
+ 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], own_addr[ETH_ALEN];
+
+ if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr)
+ < 0 ||
+ linux_get_ifhwaddr(drv->ioctl_sock, ifname, new_addr) < 0)
+ {
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ if (os_memcmp(own_addr, new_addr, ETH_ALEN) == 0) {
+ 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->ioctl_sock, ifname,
+ new_addr) < 0) {
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ os_memcpy(if_addr, new_addr, ETH_ALEN);
+ }
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef HOSTAPD
+ 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);
+ nl80211_remove_iface(drv, ifidx);
+ os_free(new_bss);
+ return -1;
+ }
+
+ if (type == WPA_IF_AP_BSS) {
+ if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
+ nl80211_remove_iface(drv, ifidx);
+ os_free(new_bss);
+ return -1;
+ }
+ os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
+ new_bss->ifindex = ifidx;
+ new_bss->drv = drv;
+ new_bss->next = drv->first_bss.next;
+ drv->first_bss.next = new_bss;
+ if (drv_priv)
+ *drv_priv = new_bss;
+ }
+#endif /* HOSTAPD */
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_if_remove(void *priv,
+ enum wpa_driver_if_type type,
+ const char *ifname)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ifindex = if_nametoindex(ifname);
+
+ wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d",
+ __func__, type, ifname, ifindex);
+ if (ifindex <= 0)
+ return -1;
+
+#ifdef HOSTAPD
+ if (bss->added_if_into_bridge) {
+ if (linux_br_del_if(drv->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->ioctl_sock, bss->brname) < 0)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "bridge %s: %s",
+ bss->brname, strerror(errno));
+ }
+#endif /* HOSTAPD */
+
+ nl80211_remove_iface(drv, ifindex);
+
+#ifdef HOSTAPD
+ if (type != WPA_IF_AP_BSS)
+ return 0;
+
+ if (bss != &drv->first_bss) {
+ struct i802_bss *tbss;
+
+ for (tbss = &drv->first_bss; tbss; tbss = tbss->next) {
+ if (tbss->next == bss) {
+ tbss->next = bss->next;
+ os_free(bss);
+ bss = NULL;
+ break;
+ }
+ }
+ if (bss)
+ wpa_printf(MSG_INFO, "nl80211: %s - could not find "
+ "BSS %p in the list", __func__, bss);
+ }
+#endif /* HOSTAPD */
+
+ return 0;
+}
+
+
+static int cookie_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ u64 *cookie = arg;
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (tb[NL80211_ATTR_COOKIE])
+ *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+ return NL_SKIP;
+}
+
+
+static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
+ unsigned int freq, unsigned int wait,
+ const u8 *buf, size_t buf_len,
+ u64 *cookie_out)
+{
+ struct nl_msg *msg;
+ u64 cookie;
+ int ret = -1;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_FRAME, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
+ NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
+ 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)", ret, strerror(-ret));
+ goto nla_put_failure;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted; "
+ "cookie 0x%llx", (long long unsigned int) cookie);
+
+ if (cookie_out)
+ *cookie_out = cookie;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_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)
+{
+ struct i802_bss *bss = priv;
+ 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, "
+ "wait=%d ms)", drv->ifindex, wait_time);
+
+ buf = os_zalloc(24 + data_len);
+ if (buf == NULL)
+ return ret;
+ os_memcpy(buf + 24, data, data_len);
+ hdr = (struct ieee80211_hdr *) buf;
+ hdr->frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
+ os_memcpy(hdr->addr1, dst, ETH_ALEN);
+ os_memcpy(hdr->addr2, src, ETH_ALEN);
+ os_memcpy(hdr->addr3, bssid, ETH_ALEN);
+
+ if (drv->nlmode == NL80211_IFTYPE_AP)
+ ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len);
+ else
+ ret = nl80211_send_frame_cmd(drv, freq, wait_time, buf,
+ 24 + data_len,
+ &drv->send_action_cookie);
+
+ 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;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_FRAME_WAIT_CANCEL, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ 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;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_REMAIN_ON_CHANNEL, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
+
+ cookie = 0;
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ if (ret == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
+ "0x%llx for freq=%u MHz duration=%u",
+ (long long unsigned int) cookie, freq, duration);
+ drv->remain_on_chan_cookie = cookie;
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
+ "(freq=%d duration=%u): %d (%s)",
+ freq, duration, ret, strerror(-ret));
+nla_put_failure:
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ if (!drv->pending_remain_on_chan) {
+ wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel "
+ "to cancel");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie "
+ "0x%llx",
+ (long long unsigned int) drv->remain_on_chan_cookie);
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret == 0)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
+ "%d (%s)", ret, strerror(-ret));
+nla_put_failure:
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (drv->nlmode != NL80211_IFTYPE_STATION) {
+ wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
+ "allowed in station mode (iftype=%d)",
+ drv->nlmode);
+ return -1;
+ }
+
+ if (!report) {
+ if (drv->nl_handle_preq) {
+ eloop_unregister_read_sock(
+ nl_socket_get_fd(drv->nl_handle_preq));
+ nl_cache_free(drv->nl_cache_preq);
+ nl80211_handle_destroy(drv->nl_handle_preq);
+ drv->nl_handle_preq = NULL;
+ }
+ return 0;
+ }
+
+ if (drv->nl_handle_preq) {
+ wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
+ "already on!");
+ return 0;
+ }
+
+ drv->nl_handle_preq = nl80211_handle_alloc(drv->nl_cb);
+ if (drv->nl_handle_preq == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate "
+ "netlink callbacks (preq)");
+ goto out_err1;
+ }
+
+ if (genl_connect(drv->nl_handle_preq)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect to "
+ "generic netlink (preq)");
+ goto out_err2;
+ return -1;
+ }
+
+#ifdef CONFIG_LIBNL20
+ if (genl_ctrl_alloc_cache(drv->nl_handle_preq,
+ &drv->nl_cache_preq) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache (preq)");
+ goto out_err2;
+ }
+#else /* CONFIG_LIBNL20 */
+ drv->nl_cache_preq = genl_ctrl_alloc_cache(drv->nl_handle_preq);
+ if (drv->nl_cache_preq == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache (preq)");
+ goto out_err2;
+ }
+#endif /* CONFIG_LIBNL20 */
+
+ if (nl80211_register_frame(drv, drv->nl_handle_preq,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_PROBE_REQ << 4),
+ NULL, 0) < 0) {
+ goto out_err3;
+ }
+
+ eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_preq),
+ wpa_driver_nl80211_event_receive, drv,
+ drv->nl_handle_preq);
+
+ return 0;
+
+ out_err3:
+ nl_cache_free(drv->nl_cache_preq);
+ out_err2:
+ nl80211_handle_destroy(drv->nl_handle_preq);
+ drv->nl_handle_preq = NULL;
+ out_err1:
+ 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;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_SET_TX_BITRATE_MASK, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+
+ bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+ if (!bands)
+ goto nla_put_failure;
+
+ /*
+ * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
+ * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
+ * rates. All 5 GHz rates are left enabled.
+ */
+ band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+ if (!band)
+ goto nla_put_failure;
+ NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
+ "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+ nla_nest_end(msg, band);
+
+ nla_nest_end(msg, bands);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ }
+
+ return ret;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ drv->disable_11b_rates = disabled;
+ return nl80211_disable_11b_rates(drv, drv->ifindex, disabled);
+}
+
+
+static int wpa_driver_nl80211_deinit_ap(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (drv->nlmode != NL80211_IFTYPE_AP)
+ return -1;
+ wpa_driver_nl80211_del_beacon(drv);
+ return wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
+}
+
+
+static void wpa_driver_nl80211_resume(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on "
+ "resume event");
+ }
+}
+
+
+static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
+ const u8 *ies, size_t ies_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret;
+ u8 *data, *pos;
+ size_t data_len;
+ u8 own_addr[ETH_ALEN];
+
+ if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr) < 0)
+ return -1;
+
+ if (action != 1) {
+ wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action "
+ "action %d", action);
+ return -1;
+ }
+
+ /*
+ * Action frame payload:
+ * Category[1] = 6 (Fast BSS Transition)
+ * Action[1] = 1 (Fast BSS Transition Request)
+ * STA Address
+ * Target AP Address
+ * FT IEs
+ */
+
+ data_len = 2 + 2 * ETH_ALEN + ies_len;
+ data = os_malloc(data_len);
+ if (data == NULL)
+ return -1;
+ pos = data;
+ *pos++ = 0x06; /* FT Action category */
+ *pos++ = action;
+ os_memcpy(pos, own_addr, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, target_ap, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, ies, ies_len);
+
+ ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0,
+ drv->bssid, own_addr, drv->bssid,
+ data, data_len);
+ os_free(data);
+
+ return ret;
+}
+
+
+static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg, *cqm = NULL;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
+ "hysteresis=%d", threshold, hysteresis);
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_SET_CQM, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+ cqm = nlmsg_alloc();
+ if (cqm == NULL)
+ return -1;
+
+ NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold);
+ NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis);
+ nla_put_nested(msg, NL80211_ATTR_CQM, cqm);
+
+ if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+ return 0;
+ msg = NULL;
+
+nla_put_failure:
+ if (cqm)
+ nlmsg_free(cqm);
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int nl80211_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;
+
+ return nl80211_get_link_noise(drv, si);
+}
+
+
+static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
+ int encrypt)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt);
+}
+
+
+static int nl80211_set_intra_bss(void *priv, int enabled)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_SET_BSS, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+ NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, !enabled);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+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;
+ }
+#endif /* CONFIG_P2P */
+
+ return 0;
+}
+
+
+static void * nl80211_global_init(void)
+{
+ struct nl80211_global *global;
+ global = os_zalloc(sizeof(*global));
+ if (global == NULL)
+ return NULL;
+ dl_list_init(&global->interfaces);
+ return global;
+}
+
+
+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));
+ }
+ 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;
+}
+
+
+const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+ .get_bssid = wpa_driver_nl80211_get_bssid,
+ .get_ssid = wpa_driver_nl80211_get_ssid,
+ .set_key = wpa_driver_nl80211_set_key,
+ .scan2 = wpa_driver_nl80211_scan,
+ .get_scan_results2 = wpa_driver_nl80211_get_scan_results,
+ .deauthenticate = wpa_driver_nl80211_deauthenticate,
+ .disassociate = wpa_driver_nl80211_disassociate,
+ .authenticate = wpa_driver_nl80211_authenticate,
+ .associate = wpa_driver_nl80211_associate,
+ .global_init = nl80211_global_init,
+ .global_deinit = nl80211_global_deinit,
+ .init2 = wpa_driver_nl80211_init,
+ .deinit = wpa_driver_nl80211_deinit,
+ .get_capa = wpa_driver_nl80211_get_capa,
+ .set_operstate = wpa_driver_nl80211_set_operstate,
+ .set_supp_port = wpa_driver_nl80211_set_supp_port,
+ .set_country = wpa_driver_nl80211_set_country,
+ .set_beacon = wpa_driver_nl80211_set_beacon,
+ .if_add = wpa_driver_nl80211_if_add,
+ .if_remove = wpa_driver_nl80211_if_remove,
+ .send_mlme = wpa_driver_nl80211_send_mlme,
+ .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data,
+ .sta_add = wpa_driver_nl80211_sta_add,
+ .sta_remove = wpa_driver_nl80211_sta_remove,
+ .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
+ .sta_set_flags = wpa_driver_nl80211_sta_set_flags,
+#ifdef HOSTAPD
+ .hapd_init = i802_init,
+ .hapd_deinit = i802_deinit,
+ .get_seqnum = i802_get_seqnum,
+ .flush = i802_flush,
+ .read_sta_data = i802_read_sta_data,
+ .get_inact_sec = i802_get_inact_sec,
+ .sta_clear_stats = i802_sta_clear_stats,
+ .set_rts = i802_set_rts,
+ .set_frag = i802_set_frag,
+ .set_rate_sets = i802_set_rate_sets,
+ .set_cts_protect = i802_set_cts_protect,
+ .set_preamble = i802_set_preamble,
+ .set_short_slot_time = i802_set_short_slot_time,
+ .set_tx_queue_params = i802_set_tx_queue_params,
+ .set_sta_vlan = i802_set_sta_vlan,
+ .set_wds_sta = i802_set_wds_sta,
+ .set_ht_params = i802_set_ht_params,
+#endif /* HOSTAPD */
+#if defined(HOSTAPD) || defined(CONFIG_AP)
+ .sta_deauth = i802_sta_deauth,
+ .sta_disassoc = i802_sta_disassoc,
+#endif /* HOSTAPD || CONFIG_AP */
+ .set_freq = i802_set_freq,
+ .send_action = wpa_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 = wpa_driver_nl80211_probe_req_report,
+ .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates,
+ .deinit_ap = wpa_driver_nl80211_deinit_ap,
+ .resume = wpa_driver_nl80211_resume,
+ .send_ft_action = nl80211_send_ft_action,
+ .signal_monitor = nl80211_signal_monitor,
+ .signal_poll = nl80211_signal_poll,
+ .send_frame = nl80211_send_frame,
+ .set_intra_bss = nl80211_set_intra_bss,
+ .set_param = nl80211_set_param,
+ .get_radio_name = nl80211_get_radio_name,
+};
diff --git a/src/drivers/driver_none.c b/src/drivers/driver_none.c
new file mode 100644
index 0000000..aaeacd6
--- /dev/null
+++ b/src/drivers/driver_none.c
@@ -0,0 +1,99 @@
+/*
+ * Driver interface for RADIUS server or WPS ER only (no driver)
+ * Copyright (c) 2008, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "driver.h"
+
+
+struct none_driver_data {
+ struct hostapd_data *hapd;
+ void *ctx;
+};
+
+
+static void * none_driver_hapd_init(struct hostapd_data *hapd,
+ struct wpa_init_params *params)
+{
+ struct none_driver_data *drv;
+
+ drv = os_zalloc(sizeof(struct none_driver_data));
+ if (drv == NULL) {
+ wpa_printf(MSG_ERROR, "Could not allocate memory for none "
+ "driver data");
+ return NULL;
+ }
+ drv->hapd = hapd;
+
+ return drv;
+}
+
+
+static void none_driver_hapd_deinit(void *priv)
+{
+ struct none_driver_data *drv = priv;
+
+ os_free(drv);
+}
+
+
+static int none_driver_send_ether(void *priv, const u8 *dst, const u8 *src,
+ u16 proto, const u8 *data, size_t data_len)
+{
+ return 0;
+}
+
+
+static void * none_driver_init(void *ctx, const char *ifname)
+{
+ struct none_driver_data *drv;
+
+ drv = os_zalloc(sizeof(struct none_driver_data));
+ if (drv == NULL) {
+ wpa_printf(MSG_ERROR, "Could not allocate memory for none "
+ "driver data");
+ return NULL;
+ }
+ drv->ctx = ctx;
+
+ return drv;
+}
+
+
+static void none_driver_deinit(void *priv)
+{
+ struct none_driver_data *drv = priv;
+
+ os_free(drv);
+}
+
+
+static int none_driver_send_eapol(void *priv, const u8 *dest, u16 proto,
+ const u8 *data, size_t data_len)
+{
+ return -1;
+}
+
+
+const struct wpa_driver_ops wpa_driver_none_ops = {
+ .name = "none",
+ .desc = "no driver (RADIUS server/WPS ER)",
+ .hapd_init = none_driver_hapd_init,
+ .hapd_deinit = none_driver_hapd_deinit,
+ .send_ether = none_driver_send_ether,
+ .init = none_driver_init,
+ .deinit = none_driver_deinit,
+ .send_eapol = none_driver_send_eapol,
+};
diff --git a/src/drivers/driver_osx.m b/src/drivers/driver_osx.m
new file mode 100644
index 0000000..69ca4b5
--- /dev/null
+++ b/src/drivers/driver_osx.m
@@ -0,0 +1,459 @@
+/*
+ * WPA Supplicant - Mac OS X Apple80211 driver interface
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#define Boolean __DummyBoolean
+#include <CoreFoundation/CoreFoundation.h>
+#undef Boolean
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+
+#include "Apple80211.h"
+
+struct wpa_driver_osx_data {
+ void *ctx;
+ WirelessRef wireless_ctx;
+ CFArrayRef scan_results;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+extern int wpa_debug_level;
+
+static void dump_dict_cb(const void *key, const void *value, void *context)
+{
+ if (MSG_DEBUG < wpa_debug_level)
+ return;
+
+ wpa_printf(MSG_DEBUG, "Key:");
+ CFShow(key);
+ wpa_printf(MSG_DEBUG, "Value:");
+ CFShow(value);
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void wpa_driver_osx_dump_dict(CFDictionaryRef dict, const char *title)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ wpa_printf(MSG_DEBUG, "OSX: Dump dictionary %s - %u entries",
+ title, (unsigned int) CFDictionaryGetCount(dict));
+ CFDictionaryApplyFunction(dict, dump_dict_cb, NULL);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+static int wpa_driver_osx_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_osx_data *drv = priv;
+ WirelessError err;
+ WirelessInfo info;
+ int len;
+
+ err = WirelessGetInfo(drv->wireless_ctx, &info);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d",
+ (int) err);
+ return -1;
+ }
+ if (!info.power) {
+ wpa_printf(MSG_DEBUG, "OSX: Wireless device power off");
+ return -1;
+ }
+
+ for (len = 0; len < 32; len++)
+ if (info.ssid[len] == 0)
+ break;
+
+ os_memcpy(ssid, info.ssid, len);
+ return len;
+}
+
+
+static int wpa_driver_osx_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_osx_data *drv = priv;
+ WirelessError err;
+ WirelessInfo info;
+
+ err = WirelessGetInfo(drv->wireless_ctx, &info);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d",
+ (int) err);
+ return -1;
+ }
+ if (!info.power) {
+ wpa_printf(MSG_DEBUG, "OSX: Wireless device power off");
+ return -1;
+ }
+
+ os_memcpy(bssid, info.bssID, ETH_ALEN);
+ return 0;
+}
+
+
+static void wpa_driver_osx_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static int wpa_driver_osx_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_osx_data *drv = priv;
+ WirelessError err;
+ const u8 *ssid = params->ssids[0].ssid;
+ size_t ssid_len = params->ssids[0].ssid_len;
+
+ if (drv->scan_results) {
+ CFRelease(drv->scan_results);
+ drv->scan_results = NULL;
+ }
+
+ if (ssid) {
+ CFStringRef data;
+ data = CFStringCreateWithBytes(kCFAllocatorDefault,
+ ssid, ssid_len,
+ kCFStringEncodingISOLatin1,
+ FALSE);
+ if (data == NULL) {
+ wpa_printf(MSG_DEBUG, "CFStringCreateWithBytes "
+ "failed");
+ return -1;
+ }
+
+ err = WirelessDirectedScan(drv->wireless_ctx,
+ &drv->scan_results, 0, data);
+ CFRelease(data);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessDirectedScan "
+ "failed: 0x%08x", (unsigned int) err);
+ return -1;
+ }
+ } else {
+ err = WirelessScan(drv->wireless_ctx, &drv->scan_results, 0);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessScan failed: "
+ "0x%08x", (unsigned int) err);
+ return -1;
+ }
+ }
+
+ eloop_register_timeout(0, 0, wpa_driver_osx_scan_timeout, drv,
+ drv->ctx);
+ return 0;
+}
+
+
+static void wpa_driver_osx_add_scan_entry(struct wpa_scan_results *res,
+ WirelessNetworkInfo *info)
+{
+ struct wpa_scan_res *result, **tmp;
+ size_t extra_len;
+ u8 *pos;
+
+ extra_len = 2 + info->ssid_len;
+
+ result = os_zalloc(sizeof(*result) + extra_len);
+ if (result == NULL)
+ return;
+ os_memcpy(result->bssid, info->bssid, ETH_ALEN);
+ result->freq = 2407 + info->channel * 5;
+ //result->beacon_int =;
+ result->caps = info->capability;
+ //result->qual = info->signal;
+ result->noise = info->noise;
+
+ pos = (u8 *)(result + 1);
+
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = info->ssid_len;
+ os_memcpy(pos, info->ssid, info->ssid_len);
+ pos += info->ssid_len;
+
+ result->ie_len = pos - (u8 *)(result + 1);
+
+ tmp = os_realloc(res->res,
+ (res->num + 1) * sizeof(struct wpa_scan_res *));
+ if (tmp == NULL) {
+ os_free(result);
+ return;
+ }
+ tmp[res->num++] = result;
+ res->res = tmp;
+}
+
+
+static struct wpa_scan_results * wpa_driver_osx_get_scan_results(void *priv)
+{
+ struct wpa_driver_osx_data *drv = priv;
+ struct wpa_scan_results *res;
+ size_t i, num;
+
+ if (drv->scan_results == NULL)
+ return 0;
+
+ num = CFArrayGetCount(drv->scan_results);
+
+ res = os_zalloc(sizeof(*res));
+ if (res == NULL)
+ return NULL;
+
+ for (i = 0; i < num; i++)
+ wpa_driver_osx_add_scan_entry(res, (WirelessNetworkInfo *)
+ CFDataGetBytePtr(CFArrayGetValueAtIndex(
+ drv->scan_results, i)));
+
+ return res;
+}
+
+
+static void wpa_driver_osx_assoc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_osx_data *drv = eloop_ctx;
+ u8 bssid[ETH_ALEN];
+ CFDictionaryRef ai;
+
+ if (wpa_driver_osx_get_bssid(drv, bssid) != 0) {
+ eloop_register_timeout(1, 0, wpa_driver_osx_assoc_timeout,
+ drv, drv->ctx);
+ return;
+ }
+
+ ai = WirelessGetAssociationInfo(drv->wireless_ctx);
+ if (ai) {
+ wpa_driver_osx_dump_dict(ai, "WirelessGetAssociationInfo");
+ CFRelease(ai);
+ } else {
+ wpa_printf(MSG_DEBUG, "OSX: Failed to get association info");
+ }
+
+ wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL);
+}
+
+
+static int wpa_driver_osx_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_osx_data *drv = priv;
+ WirelessError err;
+ CFDataRef ssid;
+ CFStringRef key;
+ int assoc_type;
+
+ ssid = CFDataCreate(kCFAllocatorDefault, params->ssid,
+ params->ssid_len);
+ if (ssid == NULL)
+ return -1;
+
+ /* TODO: support for WEP */
+ if (params->key_mgmt_suite == KEY_MGMT_PSK) {
+ if (params->passphrase == NULL)
+ return -1;
+ key = CFStringCreateWithCString(kCFAllocatorDefault,
+ params->passphrase,
+ kCFStringEncodingISOLatin1);
+ if (key == NULL) {
+ CFRelease(ssid);
+ return -1;
+ }
+ } else
+ key = NULL;
+
+ if (params->key_mgmt_suite == KEY_MGMT_NONE)
+ assoc_type = 0;
+ else
+ assoc_type = 4;
+
+ wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate(type=%d key=%p)",
+ assoc_type, key);
+ err = WirelessAssociate(drv->wireless_ctx, assoc_type, ssid, key);
+ CFRelease(ssid);
+ if (key)
+ CFRelease(key);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate failed: 0x%08x",
+ (unsigned int) err);
+ return -1;
+ }
+
+ /*
+ * Driver is actually already associated; report association from an
+ * eloop callback.
+ */
+ eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx);
+ eloop_register_timeout(0, 0, wpa_driver_osx_assoc_timeout, drv,
+ drv->ctx);
+
+ return 0;
+}
+
+
+static int wpa_driver_osx_set_key(const char *ifname, void *priv,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx, const u8 *seq,
+ size_t seq_len, const u8 *key,
+ size_t key_len)
+{
+ struct wpa_driver_osx_data *drv = priv;
+ WirelessError err;
+
+ if (alg == WPA_ALG_WEP) {
+ err = WirelessSetKey(drv->wireless_ctx, 1, key_idx, key_len,
+ key);
+ if (err != 0) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessSetKey failed: "
+ "0x%08x", (unsigned int) err);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ if (alg == WPA_ALG_PMK) {
+ err = WirelessSetWPAKey(drv->wireless_ctx, 1, key_len, key);
+ if (err != 0) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessSetWPAKey failed: "
+ "0x%08x", (unsigned int) err);
+ return -1;
+ }
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "OSX: Unsupported set_key alg %d", alg);
+ return -1;
+}
+
+
+static int wpa_driver_osx_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ os_memset(capa, 0, sizeof(*capa));
+
+ capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 |
+ WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP;
+ capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED |
+ WPA_DRIVER_AUTH_LEAP;
+ capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+
+ return 0;
+}
+
+
+static void * wpa_driver_osx_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_osx_data *drv;
+ WirelessError err;
+ u8 enabled, power;
+
+ if (!WirelessIsAvailable()) {
+ wpa_printf(MSG_ERROR, "OSX: No wireless interface available");
+ return NULL;
+ }
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->ctx = ctx;
+ err = WirelessAttach(&drv->wireless_ctx, 0);
+ if (err) {
+ wpa_printf(MSG_ERROR, "OSX: WirelessAttach failed: %d",
+ (int) err);
+ os_free(drv);
+ return NULL;
+ }
+
+ err = WirelessGetEnabled(drv->wireless_ctx, &enabled);
+ if (err)
+ wpa_printf(MSG_DEBUG, "OSX: WirelessGetEnabled failed: 0x%08x",
+ (unsigned int) err);
+ err = WirelessGetPower(drv->wireless_ctx, &power);
+ if (err)
+ wpa_printf(MSG_DEBUG, "OSX: WirelessGetPower failed: 0x%08x",
+ (unsigned int) err);
+
+ wpa_printf(MSG_DEBUG, "OSX: Enabled=%d Power=%d", enabled, power);
+
+ if (!enabled) {
+ err = WirelessSetEnabled(drv->wireless_ctx, 1);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessSetEnabled failed:"
+ " 0x%08x", (unsigned int) err);
+ WirelessDetach(drv->wireless_ctx);
+ os_free(drv);
+ return NULL;
+ }
+ }
+
+ if (!power) {
+ err = WirelessSetPower(drv->wireless_ctx, 1);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower failed: "
+ "0x%08x", (unsigned int) err);
+ WirelessDetach(drv->wireless_ctx);
+ os_free(drv);
+ return NULL;
+ }
+ }
+
+ return drv;
+}
+
+
+static void wpa_driver_osx_deinit(void *priv)
+{
+ struct wpa_driver_osx_data *drv = priv;
+ WirelessError err;
+
+ eloop_cancel_timeout(wpa_driver_osx_scan_timeout, drv, drv->ctx);
+ eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx);
+
+ err = WirelessSetPower(drv->wireless_ctx, 0);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower(0) failed: "
+ "0x%08x", (unsigned int) err);
+ }
+
+ err = WirelessDetach(drv->wireless_ctx);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessDetach failed: 0x%08x",
+ (unsigned int) err);
+ }
+
+ if (drv->scan_results)
+ CFRelease(drv->scan_results);
+
+ os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_osx_ops = {
+ .name = "osx",
+ .desc = "Mac OS X Apple80211 driver",
+ .get_ssid = wpa_driver_osx_get_ssid,
+ .get_bssid = wpa_driver_osx_get_bssid,
+ .init = wpa_driver_osx_init,
+ .deinit = wpa_driver_osx_deinit,
+ .scan2 = wpa_driver_osx_scan,
+ .get_scan_results2 = wpa_driver_osx_get_scan_results,
+ .associate = wpa_driver_osx_associate,
+ .set_key = wpa_driver_osx_set_key,
+ .get_capa = wpa_driver_osx_get_capa,
+};
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
new file mode 100644
index 0000000..2848521
--- /dev/null
+++ b/src/drivers/driver_privsep.c
@@ -0,0 +1,758 @@
+/*
+ * WPA Supplicant - privilege separated driver interface
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <sys/un.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/privsep_commands.h"
+
+
+struct wpa_driver_privsep_data {
+ void *ctx;
+ u8 own_addr[ETH_ALEN];
+ int priv_socket;
+ char *own_socket_path;
+ int cmd_socket;
+ char *own_cmd_path;
+ struct sockaddr_un priv_addr;
+ char ifname[16];
+};
+
+
+static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd)
+{
+ int res;
+
+ res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0,
+ (struct sockaddr *) &drv->priv_addr,
+ sizeof(drv->priv_addr));
+ if (res < 0)
+ perror("sendto");
+ return res < 0 ? -1 : 0;
+}
+
+
+static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd,
+ const void *data, size_t data_len,
+ void *reply, size_t *reply_len)
+{
+ struct msghdr msg;
+ struct iovec io[2];
+
+ io[0].iov_base = &cmd;
+ io[0].iov_len = sizeof(cmd);
+ io[1].iov_base = (u8 *) data;
+ io[1].iov_len = data_len;
+
+ os_memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = data ? 2 : 1;
+ msg.msg_name = &drv->priv_addr;
+ msg.msg_namelen = sizeof(drv->priv_addr);
+
+ if (sendmsg(drv->cmd_socket, &msg, 0) < 0) {
+ perror("sendmsg(cmd_socket)");
+ return -1;
+ }
+
+ if (reply) {
+ fd_set rfds;
+ struct timeval tv;
+ int res;
+
+ FD_ZERO(&rfds);
+ FD_SET(drv->cmd_socket, &rfds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv);
+ if (res < 0 && errno != EINTR) {
+ perror("select");
+ return -1;
+ }
+
+ if (FD_ISSET(drv->cmd_socket, &rfds)) {
+ res = recv(drv->cmd_socket, reply, *reply_len, 0);
+ if (res < 0) {
+ perror("recv");
+ return -1;
+ }
+ *reply_len = res;
+ } else {
+ wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting "
+ "for reply (cmd=%d)", cmd);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_privsep_scan(void *priv,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ const u8 *ssid = params->ssids[0].ssid;
+ size_t ssid_len = params->ssids[0].ssid_len;
+ wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv);
+ return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len,
+ NULL, NULL);
+}
+
+
+static struct wpa_scan_results *
+wpa_driver_privsep_get_scan_results2(void *priv)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ int res, num;
+ u8 *buf, *pos, *end;
+ size_t reply_len = 60000;
+ struct wpa_scan_results *results;
+ struct wpa_scan_res *r;
+
+ buf = os_malloc(reply_len);
+ if (buf == NULL)
+ return NULL;
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS,
+ NULL, 0, buf, &reply_len);
+ if (res < 0) {
+ os_free(buf);
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results",
+ (unsigned long) reply_len);
+ if (reply_len < sizeof(int)) {
+ wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu",
+ (unsigned long) reply_len);
+ os_free(buf);
+ return NULL;
+ }
+
+ pos = buf;
+ end = buf + reply_len;
+ os_memcpy(&num, pos, sizeof(int));
+ if (num < 0 || num > 1000) {
+ os_free(buf);
+ return NULL;
+ }
+ pos += sizeof(int);
+
+ results = os_zalloc(sizeof(*results));
+ if (results == NULL) {
+ os_free(buf);
+ return NULL;
+ }
+
+ results->res = os_zalloc(num * sizeof(struct wpa_scan_res *));
+ if (results->res == NULL) {
+ os_free(results);
+ os_free(buf);
+ return NULL;
+ }
+
+ while (results->num < (size_t) num && pos + sizeof(int) < end) {
+ int len;
+ os_memcpy(&len, pos, sizeof(int));
+ pos += sizeof(int);
+ if (len < 0 || len > 10000 || pos + len > end)
+ break;
+
+ r = os_malloc(len);
+ if (r == NULL)
+ break;
+ os_memcpy(r, pos, len);
+ pos += len;
+ if (sizeof(*r) + r->ie_len > (size_t) len) {
+ os_free(r);
+ break;
+ }
+
+ results->res[results->num++] = r;
+ }
+
+ os_free(buf);
+ return results;
+}
+
+
+static int wpa_driver_privsep_set_key(const char *ifname, void *priv,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ struct privsep_cmd_set_key cmd;
+
+ wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d",
+ __func__, priv, alg, key_idx, set_tx);
+
+ os_memset(&cmd, 0, sizeof(cmd));
+ cmd.alg = alg;
+ if (addr)
+ os_memcpy(cmd.addr, addr, ETH_ALEN);
+ else
+ os_memset(cmd.addr, 0xff, ETH_ALEN);
+ cmd.key_idx = key_idx;
+ cmd.set_tx = set_tx;
+ if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) {
+ os_memcpy(cmd.seq, seq, seq_len);
+ cmd.seq_len = seq_len;
+ }
+ if (key && key_len > 0 && key_len < sizeof(cmd.key)) {
+ os_memcpy(cmd.key, key, key_len);
+ cmd.key_len = key_len;
+ }
+
+ return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd),
+ NULL, NULL);
+}
+
+
+static int wpa_driver_privsep_associate(
+ void *priv, struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ struct privsep_cmd_associate *data;
+ int res;
+ size_t buflen;
+
+ wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
+ "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
+ __func__, priv, params->freq, params->pairwise_suite,
+ params->group_suite, params->key_mgmt_suite,
+ params->auth_alg, params->mode);
+
+ buflen = sizeof(*data) + params->wpa_ie_len;
+ data = os_zalloc(buflen);
+ if (data == NULL)
+ return -1;
+
+ if (params->bssid)
+ os_memcpy(data->bssid, params->bssid, ETH_ALEN);
+ os_memcpy(data->ssid, params->ssid, params->ssid_len);
+ data->ssid_len = params->ssid_len;
+ data->freq = params->freq;
+ data->pairwise_suite = params->pairwise_suite;
+ data->group_suite = params->group_suite;
+ data->key_mgmt_suite = params->key_mgmt_suite;
+ data->auth_alg = params->auth_alg;
+ data->mode = params->mode;
+ data->wpa_ie_len = params->wpa_ie_len;
+ if (params->wpa_ie)
+ os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len);
+ /* TODO: add support for other assoc parameters */
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen,
+ NULL, NULL);
+ os_free(data);
+
+ return res;
+}
+
+
+static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ int res;
+ size_t len = ETH_ALEN;
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len);
+ if (res < 0 || len != ETH_ALEN)
+ return -1;
+ return 0;
+}
+
+
+static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ int res, ssid_len;
+ u8 reply[sizeof(int) + 32];
+ size_t len = sizeof(reply);
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len);
+ if (res < 0 || len < sizeof(int))
+ return -1;
+ os_memcpy(&ssid_len, reply, sizeof(int));
+ if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) {
+ wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply");
+ return -1;
+ }
+ os_memcpy(ssid, &reply[sizeof(int)], ssid_len);
+ return ssid_len;
+}
+
+
+static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ //struct wpa_driver_privsep_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
+ __func__, MAC2STR(addr), reason_code);
+ wpa_printf(MSG_DEBUG, "%s - TODO", __func__);
+ return 0;
+}
+
+
+static int wpa_driver_privsep_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ //struct wpa_driver_privsep_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
+ __func__, MAC2STR(addr), reason_code);
+ wpa_printf(MSG_DEBUG, "%s - TODO", __func__);
+ return 0;
+}
+
+
+static void wpa_driver_privsep_event_assoc(void *ctx,
+ enum wpa_event_type event,
+ u8 *buf, size_t len)
+{
+ union wpa_event_data data;
+ int inc_data = 0;
+ u8 *pos, *end;
+ int ie_len;
+
+ os_memset(&data, 0, sizeof(data));
+
+ pos = buf;
+ end = buf + len;
+
+ if (end - pos < (int) sizeof(int))
+ return;
+ os_memcpy(&ie_len, pos, sizeof(int));
+ pos += sizeof(int);
+ if (ie_len < 0 || ie_len > end - pos)
+ return;
+ if (ie_len) {
+ data.assoc_info.req_ies = pos;
+ data.assoc_info.req_ies_len = ie_len;
+ pos += ie_len;
+ inc_data = 1;
+ }
+
+ wpa_supplicant_event(ctx, event, inc_data ? &data : NULL);
+}
+
+
+static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf,
+ size_t len)
+{
+ union wpa_event_data data;
+ int ievent;
+
+ if (len < sizeof(int) ||
+ len - sizeof(int) > sizeof(data.interface_status.ifname))
+ return;
+
+ os_memcpy(&ievent, buf, sizeof(int));
+
+ os_memset(&data, 0, sizeof(data));
+ data.interface_status.ievent = ievent;
+ os_memcpy(data.interface_status.ifname, buf + sizeof(int),
+ len - sizeof(int));
+ wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data);
+}
+
+
+static void wpa_driver_privsep_event_michael_mic_failure(
+ void *ctx, u8 *buf, size_t len)
+{
+ union wpa_event_data data;
+
+ if (len != sizeof(int))
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int));
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf,
+ size_t len)
+{
+ union wpa_event_data data;
+
+ if (len != sizeof(struct pmkid_candidate))
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(&data.pmkid_candidate, buf, len);
+ wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data);
+}
+
+
+static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len)
+{
+ union wpa_event_data data;
+
+ if (len != ETH_ALEN)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.stkstart.peer, buf, ETH_ALEN);
+ wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
+}
+
+
+static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf,
+ size_t len)
+{
+ union wpa_event_data data;
+
+ if (len < sizeof(int) + ETH_ALEN)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int));
+ os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN);
+ data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN;
+ data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN;
+ wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data);
+}
+
+
+static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len)
+{
+ if (len < ETH_ALEN)
+ return;
+ drv_event_eapol_rx(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN);
+}
+
+
+static void wpa_driver_privsep_receive(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct wpa_driver_privsep_data *drv = eloop_ctx;
+ u8 *buf, *event_buf;
+ size_t event_len;
+ int res, event;
+ enum privsep_event e;
+ struct sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+ const size_t buflen = 2000;
+
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return;
+ res = recvfrom(sock, buf, buflen, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (res < 0) {
+ perror("recvfrom(priv_socket)");
+ os_free(buf);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res);
+
+ if (res < (int) sizeof(int)) {
+ wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res);
+ return;
+ }
+
+ os_memcpy(&event, buf, sizeof(int));
+ event_buf = &buf[sizeof(int)];
+ event_len = res - sizeof(int);
+ wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)",
+ event, (unsigned long) event_len);
+
+ e = event;
+ switch (e) {
+ case PRIVSEP_EVENT_SCAN_RESULTS:
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+ break;
+ case PRIVSEP_EVENT_ASSOC:
+ wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC,
+ event_buf, event_len);
+ break;
+ case PRIVSEP_EVENT_DISASSOC:
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ break;
+ case PRIVSEP_EVENT_ASSOCINFO:
+ wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO,
+ event_buf, event_len);
+ break;
+ case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE:
+ wpa_driver_privsep_event_michael_mic_failure(
+ drv->ctx, event_buf, event_len);
+ break;
+ case PRIVSEP_EVENT_INTERFACE_STATUS:
+ wpa_driver_privsep_event_interface_status(drv->ctx, event_buf,
+ event_len);
+ break;
+ case PRIVSEP_EVENT_PMKID_CANDIDATE:
+ wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf,
+ event_len);
+ break;
+ case PRIVSEP_EVENT_STKSTART:
+ wpa_driver_privsep_event_stkstart(drv->ctx, event_buf,
+ event_len);
+ break;
+ case PRIVSEP_EVENT_FT_RESPONSE:
+ wpa_driver_privsep_event_ft_response(drv->ctx, event_buf,
+ event_len);
+ break;
+ case PRIVSEP_EVENT_RX_EAPOL:
+ wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf,
+ event_len);
+ break;
+ }
+
+ os_free(buf);
+}
+
+
+static void * wpa_driver_privsep_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_privsep_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->ctx = ctx;
+ drv->priv_socket = -1;
+ drv->cmd_socket = -1;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+ return drv;
+}
+
+
+static void wpa_driver_privsep_deinit(void *priv)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+
+ if (drv->priv_socket >= 0) {
+ wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER);
+ eloop_unregister_read_sock(drv->priv_socket);
+ close(drv->priv_socket);
+ }
+
+ if (drv->own_socket_path) {
+ unlink(drv->own_socket_path);
+ os_free(drv->own_socket_path);
+ }
+
+ if (drv->cmd_socket >= 0) {
+ eloop_unregister_read_sock(drv->cmd_socket);
+ close(drv->cmd_socket);
+ }
+
+ if (drv->own_cmd_path) {
+ unlink(drv->own_cmd_path);
+ os_free(drv->own_cmd_path);
+ }
+
+ os_free(drv);
+}
+
+
+static int wpa_driver_privsep_set_param(void *priv, const char *param)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ const char *pos;
+ char *own_dir, *priv_dir;
+ static unsigned int counter = 0;
+ size_t len;
+ struct sockaddr_un addr;
+
+ wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
+ if (param == NULL)
+ pos = NULL;
+ else
+ pos = os_strstr(param, "own_dir=");
+ if (pos) {
+ char *end;
+ own_dir = os_strdup(pos + 8);
+ if (own_dir == NULL)
+ return -1;
+ end = os_strchr(own_dir, ' ');
+ if (end)
+ *end = '\0';
+ } else {
+ own_dir = os_strdup("/tmp");
+ if (own_dir == NULL)
+ return -1;
+ }
+
+ if (param == NULL)
+ pos = NULL;
+ else
+ pos = os_strstr(param, "priv_dir=");
+ if (pos) {
+ char *end;
+ priv_dir = os_strdup(pos + 9);
+ if (priv_dir == NULL) {
+ os_free(own_dir);
+ return -1;
+ }
+ end = os_strchr(priv_dir, ' ');
+ if (end)
+ *end = '\0';
+ } else {
+ priv_dir = os_strdup("/var/run/wpa_priv");
+ if (priv_dir == NULL) {
+ os_free(own_dir);
+ return -1;
+ }
+ }
+
+ len = os_strlen(own_dir) + 50;
+ drv->own_socket_path = os_malloc(len);
+ if (drv->own_socket_path == NULL) {
+ os_free(priv_dir);
+ os_free(own_dir);
+ return -1;
+ }
+ os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d",
+ own_dir, getpid(), counter++);
+
+ len = os_strlen(own_dir) + 50;
+ drv->own_cmd_path = os_malloc(len);
+ if (drv->own_cmd_path == NULL) {
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ os_free(priv_dir);
+ os_free(own_dir);
+ return -1;
+ }
+ os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d",
+ own_dir, getpid(), counter++);
+
+ os_free(own_dir);
+
+ drv->priv_addr.sun_family = AF_UNIX;
+ os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path),
+ "%s/%s", priv_dir, drv->ifname);
+ os_free(priv_dir);
+
+ drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (drv->priv_socket < 0) {
+ perror("socket(PF_UNIX)");
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
+ if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) <
+ 0) {
+ perror("bind(PF_UNIX)");
+ close(drv->priv_socket);
+ drv->priv_socket = -1;
+ unlink(drv->own_socket_path);
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ return -1;
+ }
+
+ eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive,
+ drv, NULL);
+
+ drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (drv->cmd_socket < 0) {
+ perror("socket(PF_UNIX)");
+ os_free(drv->own_cmd_path);
+ drv->own_cmd_path = NULL;
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path));
+ if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ {
+ perror("bind(PF_UNIX)");
+ close(drv->cmd_socket);
+ drv->cmd_socket = -1;
+ unlink(drv->own_cmd_path);
+ os_free(drv->own_cmd_path);
+ drv->own_cmd_path = NULL;
+ return -1;
+ }
+
+ if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to register with wpa_priv");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_privsep_get_capa(void *priv,
+ struct wpa_driver_capa *capa)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ int res;
+ size_t len = sizeof(*capa);
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len);
+ if (res < 0 || len != sizeof(*capa))
+ return -1;
+ return 0;
+}
+
+
+static const u8 * wpa_driver_privsep_get_mac_addr(void *priv)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ return drv->own_addr;
+}
+
+
+static int wpa_driver_privsep_set_country(void *priv, const char *alpha2)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2);
+ return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2,
+ os_strlen(alpha2), NULL, NULL);
+}
+
+
+struct wpa_driver_ops wpa_driver_privsep_ops = {
+ "privsep",
+ "wpa_supplicant privilege separated driver",
+ .get_bssid = wpa_driver_privsep_get_bssid,
+ .get_ssid = wpa_driver_privsep_get_ssid,
+ .set_key = wpa_driver_privsep_set_key,
+ .init = wpa_driver_privsep_init,
+ .deinit = wpa_driver_privsep_deinit,
+ .set_param = wpa_driver_privsep_set_param,
+ .scan2 = wpa_driver_privsep_scan,
+ .deauthenticate = wpa_driver_privsep_deauthenticate,
+ .disassociate = wpa_driver_privsep_disassociate,
+ .associate = wpa_driver_privsep_associate,
+ .get_capa = wpa_driver_privsep_get_capa,
+ .get_mac_addr = wpa_driver_privsep_get_mac_addr,
+ .get_scan_results2 = wpa_driver_privsep_get_scan_results2,
+ .set_country = wpa_driver_privsep_set_country,
+};
+
+
+struct wpa_driver_ops *wpa_drivers[] =
+{
+ &wpa_driver_privsep_ops,
+ NULL
+};
diff --git a/src/drivers/driver_ralink.c b/src/drivers/driver_ralink.c
new file mode 100644
index 0000000..a1e27be
--- /dev/null
+++ b/src/drivers/driver_ralink.c
@@ -0,0 +1,1498 @@
+/*
+ * WPA Supplicant - driver interaction with Ralink Wireless Client
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include "wireless_copy.h"
+#include "common.h"
+#include "driver.h"
+#include "l2_packet/l2_packet.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "driver_ralink.h"
+
+static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+
+#define MAX_SSID_LEN 32
+
+struct wpa_driver_ralink_data {
+ void *ctx;
+ int ioctl_sock;
+ struct netlink_data *netlink;
+ char ifname[IFNAMSIZ + 1];
+ u8 *assoc_req_ies;
+ size_t assoc_req_ies_len;
+ u8 *assoc_resp_ies;
+ size_t assoc_resp_ies_len;
+ int no_of_pmkid;
+ struct ndis_pmkid_entry *pmkid;
+ int we_version_compiled;
+ int ap_scan;
+ int scanning_done;
+ u8 g_driver_down;
+ BOOLEAN bAddWepKey;
+};
+
+static int ralink_set_oid(struct wpa_driver_ralink_data *drv,
+ unsigned short oid, char *data, int len)
+{
+ char *buf;
+ struct iwreq iwr;
+
+ buf = os_zalloc(len);
+ if (buf == NULL)
+ return -1;
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.flags = oid;
+ iwr.u.data.flags |= OID_GET_SET_TOGGLE;
+
+ if (data)
+ os_memcpy(buf, data, len);
+
+ iwr.u.data.pointer = (caddr_t) buf;
+ iwr.u.data.length = len;
+
+ if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+ __func__, oid, len);
+ os_free(buf);
+ return -1;
+ }
+ os_free(buf);
+ return 0;
+}
+
+static int
+ralink_get_new_driver_flag(struct wpa_driver_ralink_data *drv)
+{
+ struct iwreq iwr;
+ UCHAR enabled = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.pointer = (UCHAR*) &enabled;
+ iwr.u.data.flags = RT_OID_NEW_DRIVER;
+
+ if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed", __func__);
+ return 0;
+ }
+
+ return (enabled == 1) ? 1 : 0;
+}
+
+static int wpa_driver_ralink_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+ if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) {
+ perror("ioctl[SIOCGIWAP]");
+ ret = -1;
+ }
+ os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN);
+
+ return ret;
+}
+
+static int wpa_driver_ralink_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+#if 0
+ struct wpa_supplicant *wpa_s = drv->ctx;
+ struct wpa_ssid *entry;
+#endif
+ int ssid_len;
+ u8 bssid[ETH_ALEN];
+ u8 ssid_str[MAX_SSID_LEN];
+ struct iwreq iwr;
+#if 0
+ int result = 0;
+#endif
+ int ret = 0;
+#if 0
+ BOOLEAN ieee8021x_mode = FALSE;
+ BOOLEAN ieee8021x_required_key = FALSE;
+#endif
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.essid.pointer = (caddr_t) ssid;
+ iwr.u.essid.length = 32;
+
+ if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+ perror("ioctl[SIOCGIWESSID]");
+ ret = -1;
+ } else
+ ret = iwr.u.essid.length;
+
+ if (ret <= 0)
+ return ret;
+
+ ssid_len = ret;
+ os_memset(ssid_str, 0, MAX_SSID_LEN);
+ os_memcpy(ssid_str, ssid, ssid_len);
+
+ if (drv->ap_scan == 0) {
+ /* Read BSSID form driver */
+ if (wpa_driver_ralink_get_bssid(priv, bssid) < 0) {
+ wpa_printf(MSG_WARNING, "Could not read BSSID from "
+ "driver.");
+ return ret;
+ }
+
+#if 0
+ entry = wpa_s->conf->ssid;
+ while (entry) {
+ if (!entry->disabled && ssid_len == entry->ssid_len &&
+ os_memcmp(ssid_str, entry->ssid, ssid_len) == 0 &&
+ (!entry->bssid_set ||
+ os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) {
+ /* match the config of driver */
+ result = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+
+ if (result) {
+ wpa_printf(MSG_DEBUG, "Ready to set 802.1x mode and "
+ "ieee_required_keys parameters to driver");
+
+ /* set 802.1x mode and ieee_required_keys parameter */
+ if (entry->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ if ((entry->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST)))
+ ieee8021x_required_key = TRUE;
+ ieee8021x_mode = TRUE;
+ }
+
+ if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, (char *) &ieee8021x_mode, sizeof(BOOLEAN)) < 0)
+ {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set OID_802_11_SET_IEEE8021X(%d)", (int) ieee8021x_mode);
+ }
+ else
+ {
+ wpa_printf(MSG_DEBUG, "ieee8021x_mode is %s", ieee8021x_mode ? "TRUE" : "FALSE");
+ }
+
+ if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0)
+ {
+ wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", (int) ieee8021x_required_key);
+ }
+ else
+ {
+ wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d)", ieee8021x_required_key ? "TRUE" : "FALSE",
+ entry->eapol_flags);
+ }
+ }
+#endif
+ }
+
+ return ret;
+}
+
+static int wpa_driver_ralink_set_ssid(struct wpa_driver_ralink_data *drv,
+ const u8 *ssid, size_t ssid_len)
+{
+ NDIS_802_11_SSID *buf;
+ int ret = 0;
+ struct iwreq iwr;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ buf = os_zalloc(sizeof(NDIS_802_11_SSID));
+ if (buf == NULL)
+ return -1;
+ os_memset(buf, 0, sizeof(buf));
+ buf->SsidLength = ssid_len;
+ os_memcpy(buf->Ssid, ssid, ssid_len);
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+ iwr.u.data.flags = OID_802_11_SSID;
+ iwr.u.data.flags |= OID_GET_SET_TOGGLE;
+ iwr.u.data.pointer = (caddr_t) buf;
+ iwr.u.data.length = sizeof(NDIS_802_11_SSID);
+
+ if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
+ perror("ioctl[RT_PRIV_IOCTL] -- OID_802_11_SSID");
+ ret = -1;
+ }
+ os_free(buf);
+ return ret;
+}
+
+static void wpa_driver_ralink_event_pmkid(struct wpa_driver_ralink_data *drv,
+ const u8 *data, size_t data_len)
+{
+ NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid;
+ size_t i;
+ union wpa_event_data event;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (data_len < 8) {
+ wpa_printf(MSG_DEBUG, "RALINK: Too short PMKID Candidate List "
+ "Event (len=%lu)", (unsigned long) data_len);
+ return;
+ }
+ pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data;
+ wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List Event - Version %d"
+ " NumCandidates %d",
+ (int) pmkid->Version, (int) pmkid->NumCandidates);
+
+ if (pmkid->Version != 1) {
+ wpa_printf(MSG_DEBUG, "RALINK: Unsupported PMKID Candidate "
+ "List Version %d", (int) pmkid->Version);
+ return;
+ }
+
+ if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) {
+ wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List "
+ "underflow");
+
+ return;
+ }
+
+
+
+ os_memset(&event, 0, sizeof(event));
+ for (i = 0; i < pmkid->NumCandidates; i++) {
+ PMKID_CANDIDATE *p = &pmkid->CandidateList[i];
+ wpa_printf(MSG_DEBUG, "RALINK: %lu: " MACSTR " Flags 0x%x",
+ (unsigned long) i, MAC2STR(p->BSSID),
+ (int) p->Flags);
+ os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN);
+ event.pmkid_candidate.index = i;
+ event.pmkid_candidate.preauth =
+ p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED;
+ wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE,
+ &event);
+ }
+}
+
+static int wpa_driver_ralink_set_pmkid(struct wpa_driver_ralink_data *drv)
+{
+ int len, count, i, ret;
+ struct ndis_pmkid_entry *entry;
+ NDIS_802_11_PMKID *p;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ count = 0;
+ entry = drv->pmkid;
+ while (entry) {
+ count++;
+ if (count >= drv->no_of_pmkid)
+ break;
+ entry = entry->next;
+ }
+ len = 8 + count * sizeof(BSSID_INFO);
+ p = os_zalloc(len);
+ if (p == NULL)
+ return -1;
+ p->Length = len;
+ p->BSSIDInfoCount = count;
+ entry = drv->pmkid;
+ for (i = 0; i < count; i++) {
+ os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN);
+ os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16);
+ entry = entry->next;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID",
+ (const u8 *) p, len);
+ ret = ralink_set_oid(drv, OID_802_11_PMKID, (char *) p, len);
+ os_free(p);
+ return ret;
+}
+
+static int wpa_driver_ralink_add_pmkid(void *priv, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ struct ndis_pmkid_entry *entry, *prev;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (drv->no_of_pmkid == 0)
+ return 0;
+
+ prev = NULL;
+ entry = drv->pmkid;
+ while (entry) {
+ if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0)
+ break;
+ prev = entry;
+ entry = entry->next;
+ }
+
+ if (entry) {
+ /* Replace existing entry for this BSSID and move it into the
+ * beginning of the list. */
+ os_memcpy(entry->pmkid, pmkid, 16);
+ if (prev) {
+ prev->next = entry->next;
+ entry->next = drv->pmkid;
+ drv->pmkid = entry;
+ }
+ } else {
+ entry = os_malloc(sizeof(*entry));
+ if (entry) {
+ os_memcpy(entry->bssid, bssid, ETH_ALEN);
+ os_memcpy(entry->pmkid, pmkid, 16);
+ entry->next = drv->pmkid;
+ drv->pmkid = entry;
+ }
+ }
+
+ return wpa_driver_ralink_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ralink_remove_pmkid(void *priv, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ struct ndis_pmkid_entry *entry, *prev;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (drv->no_of_pmkid == 0)
+ return 0;
+
+ entry = drv->pmkid;
+ prev = NULL;
+ drv->pmkid = NULL;
+ while (entry) {
+ if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 &&
+ os_memcmp(entry->pmkid, pmkid, 16) == 0) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ drv->pmkid = entry->next;
+ os_free(entry);
+ break;
+ }
+ prev = entry;
+ entry = entry->next;
+ }
+ return wpa_driver_ralink_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ralink_flush_pmkid(void *priv)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ NDIS_802_11_PMKID p;
+ struct ndis_pmkid_entry *pmkid, *prev;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (drv->no_of_pmkid == 0)
+ return 0;
+
+ pmkid = drv->pmkid;
+ drv->pmkid = NULL;
+ while (pmkid) {
+ prev = pmkid;
+ pmkid = pmkid->next;
+ os_free(prev);
+ }
+
+ os_memset(&p, 0, sizeof(p));
+ p.Length = 8;
+ p.BSSIDInfoCount = 0;
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)",
+ (const u8 *) &p, 8);
+ return ralink_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8);
+}
+
+static void
+wpa_driver_ralink_event_wireless_custom(struct wpa_driver_ralink_data *drv,
+ void *ctx, char *custom)
+{
+ union wpa_event_data data;
+ u8 *req_ies = NULL, *resp_ies = NULL;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
+
+ os_memset(&data, 0, sizeof(data));
+ /* Host AP driver */
+ if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+ /* receive a MICFAILURE report */
+ data.michael_mic_failure.unicast =
+ os_strstr(custom, " unicast") != NULL;
+ /* TODO: parse parameters(?) */
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+ } else if (os_strncmp(custom, "ASSOCINFO_ReqIEs=", 17) == 0) {
+ /* receive assoc. req. IEs */
+ char *spos;
+ int bytes;
+
+ spos = custom + 17;
+ /*get IE's length */
+ /*
+ * bytes = strlen(spos); ==> bug, bytes may less than original
+ * size by using this way to get size. snowpin 20070312
+ * if (!bytes)
+ * return;
+ */
+ bytes = drv->assoc_req_ies_len;
+
+ req_ies = os_malloc(bytes);
+ if (req_ies == NULL)
+ return;
+ os_memcpy(req_ies, spos, bytes);
+ data.assoc_info.req_ies = req_ies;
+ data.assoc_info.req_ies_len = bytes;
+
+ /* skip the '\0' byte */
+ spos += bytes + 1;
+
+ data.assoc_info.resp_ies = NULL;
+ data.assoc_info.resp_ies_len = 0;
+
+ if (os_strncmp(spos, " RespIEs=", 9) == 0) {
+ /* receive assoc. resp. IEs */
+ spos += 9;
+ /* get IE's length */
+ bytes = os_strlen(spos);
+ if (!bytes)
+ goto done;
+
+ resp_ies = os_malloc(bytes);
+ if (resp_ies == NULL)
+ goto done;
+ os_memcpy(resp_ies, spos, bytes);
+ data.assoc_info.resp_ies = resp_ies;
+ data.assoc_info.resp_ies_len = bytes;
+ }
+
+ wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
+
+ done:
+ /* free allocated memory */
+ os_free(resp_ies);
+ os_free(req_ies);
+ }
+}
+
+static void ralink_interface_up(struct wpa_driver_ralink_data *drv)
+{
+ union wpa_event_data event;
+ int enable_wpa_supplicant = 0;
+ drv->g_driver_down = 0;
+ os_memset(&event, 0, sizeof(event));
+ os_snprintf(event.interface_status.ifname,
+ sizeof(event.interface_status.ifname), "%s", drv->ifname);
+
+ event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+
+ if (drv->ap_scan == 1)
+ enable_wpa_supplicant = 1;
+ else
+ enable_wpa_supplicant = 2;
+ /* trigger driver support wpa_supplicant */
+ if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT,
+ (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0)
+ {
+ wpa_printf(MSG_INFO, "RALINK: Failed to set "
+ "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)",
+ (int) enable_wpa_supplicant);
+ wpa_printf(MSG_ERROR, "ralink. Driver does not support "
+ "wpa_supplicant");
+ }
+}
+
+static void
+wpa_driver_ralink_event_wireless(struct wpa_driver_ralink_data *drv,
+ void *ctx, char *data, int len)
+{
+ struct iw_event iwe_buf, *iwe = &iwe_buf;
+ char *pos, *end, *custom, *buf, *assoc_info_buf, *info_pos;
+#if 0
+ BOOLEAN ieee8021x_required_key = FALSE;
+#endif
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ assoc_info_buf = info_pos = NULL;
+ pos = data;
+ end = data + len;
+
+ while (pos + IW_EV_LCP_LEN <= end) {
+ /* Event data may be unaligned, so make a local, aligned copy
+ * before processing. */
+ os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+ wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
+ iwe->cmd, iwe->len);
+ if (iwe->len <= IW_EV_LCP_LEN)
+ return;
+
+ custom = pos + IW_EV_POINT_LEN;
+
+ if (drv->we_version_compiled > 18 && iwe->cmd == IWEVCUSTOM) {
+ /* WE-19 removed the pointer from struct iw_point */
+ char *dpos = (char *) &iwe_buf.u.data.length;
+ int dlen = dpos - (char *) &iwe_buf;
+ os_memcpy(dpos, pos + IW_EV_LCP_LEN,
+ sizeof(struct iw_event) - dlen);
+ } else {
+ os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+ custom += IW_EV_POINT_OFF;
+ }
+
+ switch (iwe->cmd) {
+ case IWEVCUSTOM:
+ if (custom + iwe->u.data.length > end)
+ return;
+ buf = os_malloc(iwe->u.data.length + 1);
+ if (buf == NULL)
+ return;
+ os_memcpy(buf, custom, iwe->u.data.length);
+ buf[iwe->u.data.length] = '\0';
+
+ if (drv->ap_scan == 1) {
+ if ((iwe->u.data.flags == RT_ASSOC_EVENT_FLAG)
+ || (iwe->u.data.flags ==
+ RT_REQIE_EVENT_FLAG) ||
+ (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG)
+ || (iwe->u.data.flags ==
+ RT_ASSOCINFO_EVENT_FLAG)) {
+ if (drv->scanning_done == 0) {
+ os_free(buf);
+ return;
+ }
+ }
+ }
+
+ if (iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) {
+ wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
+ wpa_printf(MSG_DEBUG, "Custom wireless event: "
+ "receive ASSOCIATED_EVENT !!!");
+ } else if (iwe->u.data.flags == RT_REQIE_EVENT_FLAG) {
+ wpa_printf(MSG_DEBUG, "Custom wireless event: "
+ "receive ReqIEs !!!");
+ drv->assoc_req_ies =
+ os_malloc(iwe->u.data.length);
+ if (drv->assoc_req_ies == NULL) {
+ os_free(buf);
+ return;
+ }
+
+ drv->assoc_req_ies_len = iwe->u.data.length;
+ os_memcpy(drv->assoc_req_ies, custom,
+ iwe->u.data.length);
+ } else if (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) {
+ wpa_printf(MSG_DEBUG, "Custom wireless event: "
+ "receive RespIEs !!!");
+ drv->assoc_resp_ies =
+ os_malloc(iwe->u.data.length);
+ if (drv->assoc_resp_ies == NULL) {
+ os_free(drv->assoc_req_ies);
+ drv->assoc_req_ies = NULL;
+ os_free(buf);
+ return;
+ }
+
+ drv->assoc_resp_ies_len = iwe->u.data.length;
+ os_memcpy(drv->assoc_resp_ies, custom,
+ iwe->u.data.length);
+ } else if (iwe->u.data.flags ==
+ RT_ASSOCINFO_EVENT_FLAG) {
+ wpa_printf(MSG_DEBUG, "Custom wireless event: "
+ "receive ASSOCINFO_EVENT !!!");
+
+ assoc_info_buf =
+ os_zalloc(drv->assoc_req_ies_len +
+ drv->assoc_resp_ies_len + 1);
+
+ if (assoc_info_buf == NULL) {
+ os_free(drv->assoc_req_ies);
+ drv->assoc_req_ies = NULL;
+ os_free(drv->assoc_resp_ies);
+ drv->assoc_resp_ies = NULL;
+ os_free(buf);
+ return;
+ }
+
+ if (drv->assoc_req_ies) {
+ os_memcpy(assoc_info_buf,
+ drv->assoc_req_ies,
+ drv->assoc_req_ies_len);
+ }
+ info_pos = assoc_info_buf +
+ drv->assoc_req_ies_len;
+ if (drv->assoc_resp_ies) {
+ os_memcpy(info_pos,
+ drv->assoc_resp_ies,
+ drv->assoc_resp_ies_len);
+ }
+ assoc_info_buf[drv->assoc_req_ies_len +
+ drv->assoc_resp_ies_len] = '\0';
+ wpa_driver_ralink_event_wireless_custom(
+ drv, ctx, assoc_info_buf);
+ os_free(drv->assoc_req_ies);
+ drv->assoc_req_ies = NULL;
+ os_free(drv->assoc_resp_ies);
+ drv->assoc_resp_ies = NULL;
+ os_free(assoc_info_buf);
+ } else if (iwe->u.data.flags == RT_DISASSOC_EVENT_FLAG)
+ {
+ wpa_printf(MSG_DEBUG, "Custom wireless event: "
+ "receive DISASSOCIATED_EVENT !!!");
+ wpa_supplicant_event(ctx, EVENT_DISASSOC,
+ NULL);
+ } else if (iwe->u.data.flags == RT_PMKIDCAND_FLAG) {
+ wpa_printf(MSG_DEBUG, "Custom wireless event: "
+ "receive PMKIDCAND_EVENT !!!");
+ wpa_driver_ralink_event_pmkid(
+ drv, (const u8 *) custom,
+ iwe->u.data.length);
+ } else if (iwe->u.data.flags == RT_INTERFACE_DOWN) {
+ drv->g_driver_down = 1;
+ eloop_terminate();
+ } else if (iwe->u.data.flags == RT_INTERFACE_UP) {
+ ralink_interface_up(drv);
+ } else {
+ wpa_driver_ralink_event_wireless_custom(
+ drv, ctx, buf);
+ }
+ os_free(buf);
+ break;
+ }
+
+ pos += iwe->len;
+ }
+}
+
+static void
+wpa_driver_ralink_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
+{
+ struct wpa_driver_ralink_data *drv = ctx;
+ int attrlen, rta_len;
+ struct rtattr *attr;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ wpa_hexdump(MSG_DEBUG, "ifi: ", (u8 *) ifi, sizeof(struct ifinfomsg));
+
+ attrlen = len;
+ wpa_printf(MSG_DEBUG, "attrlen=%d", attrlen);
+ attr = (struct rtattr *) buf;
+ wpa_hexdump(MSG_DEBUG, "attr1: ", (u8 *) attr, sizeof(struct rtattr));
+ rta_len = RTA_ALIGN(sizeof(struct rtattr));
+ wpa_hexdump(MSG_DEBUG, "attr2: ", (u8 *)attr,rta_len);
+ while (RTA_OK(attr, attrlen)) {
+ wpa_printf(MSG_DEBUG, "rta_type=%02x\n", attr->rta_type);
+ if (attr->rta_type == IFLA_WIRELESS) {
+ wpa_driver_ralink_event_wireless(
+ drv, ctx,
+ ((char *) attr) + rta_len,
+ attr->rta_len - rta_len);
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ wpa_hexdump(MSG_DEBUG, "attr3: ",
+ (u8 *) attr, sizeof(struct rtattr));
+ }
+}
+
+static int
+ralink_get_we_version_compiled(struct wpa_driver_ralink_data *drv)
+{
+ struct iwreq iwr;
+ UINT we_version_compiled = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.pointer = (caddr_t) &we_version_compiled;
+ iwr.u.data.flags = RT_OID_WE_VERSION_COMPILED;
+
+ if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed", __func__);
+ return -1;
+ }
+
+ drv->we_version_compiled = we_version_compiled;
+
+ return 0;
+}
+
+static void * wpa_driver_ralink_init(void *ctx, const char *ifname)
+{
+ int s;
+ struct wpa_driver_ralink_data *drv;
+ struct ifreq ifr;
+ UCHAR enable_wpa_supplicant = 0;
+ struct netlink_config *cfg;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ /* open socket to kernel */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return NULL;
+ }
+ /* do it */
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+ perror(ifr.ifr_name);
+ return NULL;
+ }
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+
+ drv->scanning_done = 1;
+ drv->ap_scan = 1; /* for now - let's assume ap_scan=1 is used */
+ drv->ctx = ctx;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ drv->ioctl_sock = s;
+ drv->g_driver_down = 0;
+
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL) {
+ close(drv->ioctl_sock);
+ os_free(drv);
+ return NULL;
+ }
+ cfg->ctx = drv;
+ cfg->newlink_cb = wpa_driver_ralink_event_rtm_newlink;
+ drv->netlink = netlink_init(cfg);
+ if (drv->netlink == NULL) {
+ os_free(cfg);
+ close(drv->ioctl_sock);
+ os_free(drv);
+ return NULL;
+ }
+
+ drv->no_of_pmkid = 4; /* Number of PMKID saved supported */
+
+ linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1);
+ ralink_get_we_version_compiled(drv);
+ wpa_driver_ralink_flush_pmkid(drv);
+
+ if (drv->ap_scan == 1)
+ enable_wpa_supplicant = 1;
+ else
+ enable_wpa_supplicant = 2;
+ /* trigger driver support wpa_supplicant */
+ if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT,
+ (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0)
+ {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)",
+ (int) enable_wpa_supplicant);
+ wpa_printf(MSG_ERROR, "RALINK: Driver does not support "
+ "wpa_supplicant");
+ close(s);
+ close(drv->ioctl_sock);
+ os_free(drv);
+ return NULL;
+ }
+
+ if (drv->ap_scan == 1)
+ drv->scanning_done = 0;
+
+ return drv;
+}
+
+static void wpa_driver_ralink_deinit(void *priv)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ UCHAR enable_wpa_supplicant;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ enable_wpa_supplicant = 0;
+
+ if (drv->g_driver_down == 0) {
+ /* trigger driver disable wpa_supplicant support */
+ if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT,
+ (char *) &enable_wpa_supplicant,
+ sizeof(BOOLEAN)) < 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)",
+ (int) enable_wpa_supplicant);
+ }
+
+ wpa_driver_ralink_flush_pmkid(drv);
+
+ sleep(1);
+ /* linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); */
+ }
+
+ eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx);
+ netlink_deinit(drv->netlink);
+ close(drv->ioctl_sock);
+ os_free(drv);
+}
+
+static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_ralink_data *drv = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+
+ drv->scanning_done = 1;
+
+}
+
+static int wpa_driver_ralink_scan(void *priv,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+#if 0
+ if (ssid_len > IW_ESSID_MAX_SIZE) {
+ wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)",
+ __FUNCTION__, (unsigned long) ssid_len);
+ return -1;
+ }
+
+ /* wpa_driver_ralink_set_ssid(drv, ssid, ssid_len); */
+#endif
+
+ if (ralink_set_oid(drv, RT_OID_WPS_PROBE_REQ_IE,
+ (char *) params->extra_ies, params->extra_ies_len) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "RT_OID_WPS_PROBE_REQ_IE");
+ }
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
+ perror("ioctl[SIOCSIWSCAN]");
+ ret = -1;
+ }
+
+ /* Not all drivers generate "scan completed" wireless event, so try to
+ * read results after a timeout. */
+ eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(4, 0, wpa_driver_ralink_scan_timeout, drv,
+ drv->ctx);
+
+ drv->scanning_done = 0;
+
+ return ret;
+}
+
+static struct wpa_scan_results *
+wpa_driver_ralink_get_scan_results(void *priv)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ UCHAR *buf = NULL;
+ size_t buf_len;
+ NDIS_802_11_BSSID_LIST_EX *wsr;
+ NDIS_WLAN_BSSID_EX *wbi;
+ struct iwreq iwr;
+ size_t ap_num;
+ u8 *pos;
+ struct wpa_scan_results *res;
+
+ if (drv->g_driver_down == 1)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (drv->we_version_compiled >= 17)
+ buf_len = 8192;
+ else
+ buf_len = 4096;
+
+ for (;;) {
+ buf = os_zalloc(buf_len);
+ iwr.u.data.length = buf_len;
+ if (buf == NULL)
+ return NULL;
+
+ wsr = (NDIS_802_11_BSSID_LIST_EX *) buf;
+
+ wsr->NumberOfItems = 0;
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.pointer = (void *) buf;
+ iwr.u.data.flags = OID_802_11_BSSID_LIST;
+
+ if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) == 0)
+ break;
+
+ if (errno == E2BIG && buf_len < 65535) {
+ os_free(buf);
+ buf = NULL;
+ buf_len *= 2;
+ if (buf_len > 65535)
+ buf_len = 65535; /* 16-bit length field */
+ wpa_printf(MSG_DEBUG, "Scan results did not fit - "
+ "trying larger buffer (%lu bytes)",
+ (unsigned long) buf_len);
+ } else {
+ perror("ioctl[RT_PRIV_IOCTL]");
+ os_free(buf);
+ return NULL;
+ }
+ }
+
+ res = os_zalloc(sizeof(*res));
+ if (res == NULL) {
+ os_free(buf);
+ return NULL;
+ }
+
+ res->res = os_zalloc(wsr->NumberOfItems *
+ sizeof(struct wpa_scan_res *));
+ if (res->res == NULL) {
+ os_free(res);
+ os_free(buf);
+ return NULL;
+ }
+
+ for (ap_num = 0, wbi = wsr->Bssid; ap_num < wsr->NumberOfItems;
+ ++ap_num) {
+ struct wpa_scan_res *r = NULL;
+ size_t extra_len = 0, var_ie_len = 0;
+ u8 *pos2;
+
+ /* SSID data element */
+ extra_len += 2 + wbi->Ssid.SsidLength;
+ var_ie_len = wbi->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+ r = os_zalloc(sizeof(*r) + extra_len + var_ie_len);
+ if (r == NULL)
+ break;
+ res->res[res->num++] = r;
+
+ wpa_printf(MSG_DEBUG, "SSID - %s", wbi->Ssid.Ssid);
+ /* get ie's */
+ wpa_hexdump(MSG_DEBUG, "RALINK: AP IEs",
+ (u8 *) &wbi->IEs[0], wbi->IELength);
+
+ os_memcpy(r->bssid, wbi->MacAddress, ETH_ALEN);
+
+ extra_len += (2 + wbi->Ssid.SsidLength);
+ r->ie_len = extra_len + var_ie_len;
+ pos2 = (u8 *) (r + 1);
+
+ /*
+ * Generate a fake SSID IE since the driver did not report
+ * a full IE list.
+ */
+ *pos2++ = WLAN_EID_SSID;
+ *pos2++ = wbi->Ssid.SsidLength;
+ os_memcpy(pos2, wbi->Ssid.Ssid, wbi->Ssid.SsidLength);
+ pos2 += wbi->Ssid.SsidLength;
+
+ r->freq = (wbi->Configuration.DSConfig / 1000);
+
+ pos = (u8 *) wbi + sizeof(*wbi) - 1;
+
+ pos += sizeof(NDIS_802_11_FIXED_IEs) - 2;
+ os_memcpy(&(r->caps), pos, 2);
+ pos += 2;
+
+ if (wbi->IELength > sizeof(NDIS_802_11_FIXED_IEs))
+ os_memcpy(pos2, pos, var_ie_len);
+
+ wbi = (NDIS_WLAN_BSSID_EX *) ((u8 *) wbi + wbi->Length);
+ }
+
+ os_free(buf);
+ return res;
+}
+
+static int ralink_set_auth_mode(struct wpa_driver_ralink_data *drv,
+ NDIS_802_11_AUTHENTICATION_MODE mode)
+{
+ NDIS_802_11_AUTHENTICATION_MODE auth_mode = mode;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (ralink_set_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+ (char *) &auth_mode, sizeof(auth_mode)) < 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "OID_802_11_AUTHENTICATION_MODE (%d)",
+ (int) auth_mode);
+ return -1;
+ }
+ return 0;
+}
+
+static int ralink_set_encr_type(struct wpa_driver_ralink_data *drv,
+ NDIS_802_11_WEP_STATUS encr_type)
+{
+ NDIS_802_11_WEP_STATUS wep_status = encr_type;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (ralink_set_oid(drv, OID_802_11_WEP_STATUS,
+ (char *) &wep_status, sizeof(wep_status)) < 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "OID_802_11_WEP_STATUS (%d)",
+ (int) wep_status);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wpa_driver_ralink_remove_key(struct wpa_driver_ralink_data *drv,
+ int key_idx, const u8 *addr,
+ const u8 *bssid, int pairwise)
+{
+ NDIS_802_11_REMOVE_KEY rkey;
+ NDIS_802_11_KEY_INDEX _index;
+ int res, res2;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ os_memset(&rkey, 0, sizeof(rkey));
+
+ rkey.Length = sizeof(rkey);
+ rkey.KeyIndex = key_idx;
+
+ if (pairwise)
+ rkey.KeyIndex |= 1 << 30;
+
+ os_memcpy(rkey.BSSID, bssid, ETH_ALEN);
+
+ res = ralink_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey,
+ sizeof(rkey));
+
+ /* AlbertY@20060210 removed it */
+ if (0 /* !pairwise */) {
+ res2 = ralink_set_oid(drv, OID_802_11_REMOVE_WEP,
+ (char *) &_index, sizeof(_index));
+ } else
+ res2 = 0;
+
+ if (res < 0 && res2 < 0)
+ return res;
+ return 0;
+}
+
+static int wpa_driver_ralink_add_wep(struct wpa_driver_ralink_data *drv,
+ int pairwise, int key_idx, int set_tx,
+ const u8 *key, size_t key_len)
+{
+ NDIS_802_11_WEP *wep;
+ size_t len;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ len = 12 + key_len;
+ wep = os_zalloc(len);
+ if (wep == NULL)
+ return -1;
+
+ wep->Length = len;
+ wep->KeyIndex = key_idx;
+
+ if (set_tx)
+ wep->KeyIndex |= 0x80000000;
+
+ wep->KeyLength = key_len;
+ os_memcpy(wep->KeyMaterial, key, key_len);
+
+ wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_WEP",
+ (const u8 *) wep, len);
+ res = ralink_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len);
+
+ os_free(wep);
+
+ return res;
+}
+
+static int wpa_driver_ralink_set_key(const char *ifname, void *priv,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ size_t len, i;
+ NDIS_802_11_KEY *nkey;
+ int res, pairwise;
+ u8 bssid[ETH_ALEN];
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ drv->bAddWepKey = FALSE;
+
+ if (addr == NULL || is_broadcast_ether_addr(addr)) {
+ /* Group Key */
+ pairwise = 0;
+ wpa_driver_ralink_get_bssid(drv, bssid);
+ } else {
+ /* Pairwise Key */
+ pairwise = 1;
+ os_memcpy(bssid, addr, ETH_ALEN);
+ }
+
+ if (alg == WPA_ALG_NONE || key_len == 0) {
+ return wpa_driver_ralink_remove_key(drv, key_idx, addr, bssid,
+ pairwise);
+ }
+
+ if (alg == WPA_ALG_WEP) {
+ drv->bAddWepKey = TRUE;
+ return wpa_driver_ralink_add_wep(drv, pairwise, key_idx,
+ set_tx, key, key_len);
+ }
+
+ len = 12 + 6 + 6 + 8 + key_len;
+
+ nkey = os_zalloc(len);
+ if (nkey == NULL)
+ return -1;
+
+ nkey->Length = len;
+ nkey->KeyIndex = key_idx;
+
+ if (set_tx)
+ nkey->KeyIndex |= 1 << 31;
+
+ if (pairwise)
+ nkey->KeyIndex |= 1 << 30;
+
+ if (seq && seq_len)
+ nkey->KeyIndex |= 1 << 29;
+
+ nkey->KeyLength = key_len;
+ os_memcpy(nkey->BSSID, bssid, ETH_ALEN);
+
+ if (seq && seq_len) {
+ for (i = 0; i < seq_len; i++)
+ nkey->KeyRSC |= seq[i] << (i * 8);
+ }
+ if (alg == WPA_ALG_TKIP && key_len == 32) {
+ os_memcpy(nkey->KeyMaterial, key, 16);
+ os_memcpy(nkey->KeyMaterial + 16, key + 24, 8);
+ os_memcpy(nkey->KeyMaterial + 24, key + 16, 8);
+ } else {
+ os_memcpy(nkey->KeyMaterial, key, key_len);
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu "
+ "key_len=%lu", __FUNCTION__, alg, key_idx, set_tx,
+ (unsigned long) seq_len, (unsigned long) key_len);
+
+ wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_KEY",
+ (const u8 *) nkey, len);
+ res = ralink_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len);
+ os_free(nkey);
+
+ return res;
+}
+
+static int wpa_driver_ralink_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ if (ralink_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4) < 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "OID_802_11_DISASSOCIATE");
+ }
+
+ return 0;
+}
+
+static int wpa_driver_ralink_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "g_driver_down = %d", drv->g_driver_down);
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ if (ralink_get_new_driver_flag(drv) == 0) {
+ return wpa_driver_ralink_disassociate(priv, addr, reason_code);
+ } else {
+ MLME_DEAUTH_REQ_STRUCT mlme;
+ os_memset(&mlme, 0, sizeof(MLME_DEAUTH_REQ_STRUCT));
+ mlme.Reason = reason_code;
+ os_memcpy(mlme.Addr, addr, MAC_ADDR_LEN);
+ return ralink_set_oid(drv, OID_802_11_DEAUTHENTICATION,
+ (char *) &mlme,
+ sizeof(MLME_DEAUTH_REQ_STRUCT));
+ }
+}
+
+static int wpa_driver_ralink_set_gen_ie(void *priv, const u8 *ie,
+ size_t ie_len)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.pointer = (caddr_t) ie;
+ iwr.u.data.length = ie_len;
+
+ wpa_hexdump(MSG_DEBUG, "wpa_driver_ralink_set_gen_ie: ",
+ (u8 *) ie, ie_len);
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) {
+ perror("ioctl[SIOCSIWGENIE]");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int
+wpa_driver_ralink_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+
+ NDIS_802_11_NETWORK_INFRASTRUCTURE mode;
+ NDIS_802_11_AUTHENTICATION_MODE auth_mode;
+ NDIS_802_11_WEP_STATUS encr;
+ BOOLEAN ieee8021xMode;
+ BOOLEAN ieee8021x_required_key = TRUE;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (params->mode == IEEE80211_MODE_IBSS)
+ mode = Ndis802_11IBSS;
+ else
+ mode = Ndis802_11Infrastructure;
+
+ if (ralink_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+ (char *) &mode, sizeof(mode)) < 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+ (int) mode);
+ /* Try to continue anyway */
+ }
+
+ if (params->key_mgmt_suite == KEY_MGMT_WPS) {
+ UCHAR enable_wps = 0x80;
+ /* trigger driver support wpa_supplicant */
+ if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT,
+ (PCHAR) &enable_wps, sizeof(UCHAR)) < 0) {
+ wpa_printf(MSG_INFO, "RALINK: Failed to set "
+ "RT_OID_WPA_SUPPLICANT_SUPPORT (%d)",
+ (int) enable_wps);
+ }
+
+ wpa_driver_ralink_set_gen_ie(priv, params->wpa_ie,
+ params->wpa_ie_len);
+
+ ralink_set_auth_mode(drv, Ndis802_11AuthModeOpen);
+
+ ralink_set_encr_type(drv, Ndis802_11EncryptionDisabled);
+ } else {
+#ifdef CONFIG_WPS
+ UCHAR enable_wpa_supplicant;
+
+ if (drv->ap_scan == 1)
+ enable_wpa_supplicant = 0x01;
+ else
+ enable_wpa_supplicant = 0x02;
+
+ /* trigger driver support wpa_supplicant */
+ if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT,
+ (PCHAR) &enable_wpa_supplicant,
+ sizeof(UCHAR)) < 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "RT_OID_WPA_SUPPLICANT_SUPPORT (%d)",
+ (int) enable_wpa_supplicant);
+ }
+
+ wpa_driver_ralink_set_gen_ie(priv, (u8 *) "", 0);
+#endif /* CONFIG_WPS */
+
+ if (params->wpa_ie == NULL || params->wpa_ie_len == 0) {
+ if (params->auth_alg & WPA_AUTH_ALG_SHARED) {
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+ auth_mode = Ndis802_11AuthModeAutoSwitch;
+ else
+ auth_mode = Ndis802_11AuthModeShared;
+ } else
+ auth_mode = Ndis802_11AuthModeOpen;
+ } else if (params->wpa_ie[0] == WLAN_EID_RSN) {
+ if (params->key_mgmt_suite == KEY_MGMT_PSK)
+ auth_mode = Ndis802_11AuthModeWPA2PSK;
+ else
+ auth_mode = Ndis802_11AuthModeWPA2;
+ } else {
+ if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE)
+ auth_mode = Ndis802_11AuthModeWPANone;
+ else if (params->key_mgmt_suite == KEY_MGMT_PSK)
+ auth_mode = Ndis802_11AuthModeWPAPSK;
+ else
+ auth_mode = Ndis802_11AuthModeWPA;
+ }
+
+ switch (params->pairwise_suite) {
+ case CIPHER_CCMP:
+ encr = Ndis802_11Encryption3Enabled;
+ break;
+ case CIPHER_TKIP:
+ encr = Ndis802_11Encryption2Enabled;
+ break;
+ case CIPHER_WEP40:
+ case CIPHER_WEP104:
+ encr = Ndis802_11Encryption1Enabled;
+ break;
+ case CIPHER_NONE:
+ if (params->group_suite == CIPHER_CCMP)
+ encr = Ndis802_11Encryption3Enabled;
+ else if (params->group_suite == CIPHER_TKIP)
+ encr = Ndis802_11Encryption2Enabled;
+ else
+ encr = Ndis802_11EncryptionDisabled;
+ break;
+ default:
+ encr = Ndis802_11EncryptionDisabled;
+ break;
+ }
+
+ ralink_set_auth_mode(drv, auth_mode);
+
+ /* notify driver that IEEE8021x mode is enabled */
+ if (params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) {
+ ieee8021xMode = TRUE;
+ if (drv->bAddWepKey)
+ ieee8021x_required_key = FALSE;
+ } else
+ ieee8021xMode = FALSE;
+
+ if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY,
+ (char *) &ieee8021x_required_key,
+ sizeof(BOOLEAN)) < 0) {
+ wpa_printf(MSG_DEBUG, "ERROR: Failed to set "
+ "OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)",
+ (int) ieee8021x_required_key);
+ } else {
+ wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s",
+ ieee8021x_required_key ? "TRUE" : "FALSE");
+ }
+
+ if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X,
+ (char *) &ieee8021xMode, sizeof(BOOLEAN)) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "OID_802_11_SET_IEEE8021X(%d)",
+ (int) ieee8021xMode);
+ }
+
+ ralink_set_encr_type(drv, encr);
+
+ if ((ieee8021xMode == FALSE) &&
+ (encr == Ndis802_11Encryption1Enabled)) {
+ /* static WEP */
+ int enabled = 0;
+ if (ralink_set_oid(drv, OID_802_11_DROP_UNENCRYPTED,
+ (char *) &enabled, sizeof(enabled))
+ < 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "OID_802_11_DROP_UNENCRYPTED(%d)",
+ (int) encr);
+ }
+ }
+ }
+
+ return wpa_driver_ralink_set_ssid(drv, params->ssid, params->ssid_len);
+}
+
+static int
+wpa_driver_ralink_set_countermeasures(void *priv, int enabled)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ if (drv->g_driver_down == 1)
+ return -1;
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+ return ralink_set_oid(drv, OID_SET_COUNTERMEASURES, (char *) &enabled,
+ sizeof(int));
+}
+
+const struct wpa_driver_ops wpa_driver_ralink_ops = {
+ .name = "ralink",
+ .desc = "Ralink Wireless Client driver",
+ .get_bssid = wpa_driver_ralink_get_bssid,
+ .get_ssid = wpa_driver_ralink_get_ssid,
+ .set_key = wpa_driver_ralink_set_key,
+ .init = wpa_driver_ralink_init,
+ .deinit = wpa_driver_ralink_deinit,
+ .set_countermeasures = wpa_driver_ralink_set_countermeasures,
+ .scan2 = wpa_driver_ralink_scan,
+ .get_scan_results2 = wpa_driver_ralink_get_scan_results,
+ .deauthenticate = wpa_driver_ralink_deauthenticate,
+ .disassociate = wpa_driver_ralink_disassociate,
+ .associate = wpa_driver_ralink_associate,
+ .add_pmkid = wpa_driver_ralink_add_pmkid,
+ .remove_pmkid = wpa_driver_ralink_remove_pmkid,
+ .flush_pmkid = wpa_driver_ralink_flush_pmkid,
+};
diff --git a/src/drivers/driver_ralink.h b/src/drivers/driver_ralink.h
new file mode 100644
index 0000000..d13df28
--- /dev/null
+++ b/src/drivers/driver_ralink.h
@@ -0,0 +1,383 @@
+/*
+ * WPA Supplicant - driver_ralink exported functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+// Ralink defined OIDs
+#if WIRELESS_EXT <= 11
+#ifndef SIOCDEVPRIVATE
+#define SIOCDEVPRIVATE 0x8BE0
+#endif
+#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
+#endif
+
+#define RT_PRIV_IOCTL (SIOCIWFIRSTPRIV + 0x0E)
+#define RTPRIV_IOCTL_SET (SIOCIWFIRSTPRIV + 0x02)
+
+// IEEE 802.11 OIDs & Ralink defined OIDs ******
+
+// (RaConfig Set/QueryInform) ==>
+#define OID_GET_SET_TOGGLE 0x8000
+
+#define OID_802_11_ADD_WEP 0x0112
+#define OID_802_11_REMOVE_WEP 0x0113
+#define OID_802_11_DISASSOCIATE 0x0114
+#define OID_802_11_PRIVACY_FILTER 0x0118
+#define OID_802_11_ASSOCIATION_INFORMATION 0x011E
+#define OID_802_11_BSSID_LIST_SCAN 0x0508
+#define OID_802_11_SSID 0x0509
+#define OID_802_11_BSSID 0x050A
+#define OID_802_11_WEP_STATUS 0x0510
+#define OID_802_11_AUTHENTICATION_MODE 0x0511
+#define OID_802_11_INFRASTRUCTURE_MODE 0x0512
+#define OID_802_11_TX_POWER_LEVEL 0x0517
+#define OID_802_11_REMOVE_KEY 0x0519
+#define OID_802_11_ADD_KEY 0x0520
+#define OID_802_11_DEAUTHENTICATION 0x0526
+#define OID_802_11_DROP_UNENCRYPTED 0x0527
+#define OID_802_11_BSSID_LIST 0x0609
+#define OID_802_3_CURRENT_ADDRESS 0x060A
+#define OID_SET_COUNTERMEASURES 0x0616
+#define OID_802_11_SET_IEEE8021X 0x0617 // For IEEE8021x mode
+#define OID_802_11_SET_IEEE8021X_REQUIRE_KEY 0x0618 // For DynamicWEP in IEEE802.1x mode
+#define OID_802_11_PMKID 0x0620
+#define RT_OID_WPA_SUPPLICANT_SUPPORT 0x0621 // for trigger driver enable/disable wpa_supplicant support
+#define RT_OID_WE_VERSION_COMPILED 0x0622
+#define RT_OID_NEW_DRIVER 0x0623
+#define RT_OID_WPS_PROBE_REQ_IE 0x0625
+
+#define PACKED __attribute__ ((packed))
+
+//wpa_supplicant event flags
+#define RT_ASSOC_EVENT_FLAG 0x0101
+#define RT_DISASSOC_EVENT_FLAG 0x0102
+#define RT_REQIE_EVENT_FLAG 0x0103
+#define RT_RESPIE_EVENT_FLAG 0x0104
+#define RT_ASSOCINFO_EVENT_FLAG 0x0105
+#define RT_PMKIDCAND_FLAG 0x0106
+#define RT_INTERFACE_DOWN 0x0107
+#define RT_INTERFACE_UP 0x0108
+
+//
+// IEEE 802.11 Structures and definitions
+//
+// new types for Media Specific Indications
+
+#ifndef ULONG
+#define CHAR char
+#define INT int
+#define SHORT int
+#define UINT u32
+#undef ULONG
+//#define ULONG u32
+#define ULONG unsigned long /* 32-bit in 32-bit CPU or 64-bit in 64-bit CPU */
+#define USHORT unsigned short
+#define UCHAR unsigned char
+
+#define uint32 u32
+#define uint8 u8
+
+
+#define BOOLEAN u8
+//#define LARGE_INTEGER s64
+#define VOID void
+#define LONG long
+#define LONGLONG s64
+#define ULONGLONG u64
+typedef VOID *PVOID;
+typedef CHAR *PCHAR;
+typedef UCHAR *PUCHAR;
+typedef USHORT *PUSHORT;
+typedef LONG *PLONG;
+typedef ULONG *PULONG;
+
+typedef union _LARGE_INTEGER {
+ struct {
+ ULONG LowPart;
+ LONG HighPart;
+ }vv;
+ struct {
+ ULONG LowPart;
+ LONG HighPart;
+ } u;
+ s64 QuadPart;
+} LARGE_INTEGER;
+
+#endif
+
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+#define MAX_LEN_OF_SSID 32
+#define MAC_ADDR_LEN 6
+
+typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
+
+// mask for authentication/integrity fields
+#define NDIS_802_11_AUTH_REQUEST_AUTH_FIELDS 0x0f
+
+#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01
+#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02
+#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06
+#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E
+
+// Added new types for OFDM 5G and 2.4G
+typedef enum _NDIS_802_11_NETWORK_TYPE
+{
+ Ndis802_11FH,
+ Ndis802_11DS,
+ Ndis802_11OFDM5,
+ Ndis802_11OFDM24,
+ Ndis802_11Automode,
+ Ndis802_11NetworkTypeMax // not a real type, defined as an upper bound
+} NDIS_802_11_NETWORK_TYPE, *PNDIS_802_11_NETWORK_TYPE;
+
+//
+// Received Signal Strength Indication
+//
+typedef LONG NDIS_802_11_RSSI; // in dBm
+
+typedef struct _NDIS_802_11_CONFIGURATION_FH
+{
+ ULONG Length; // Length of structure
+ ULONG HopPattern; // As defined by 802.11, MSB set
+ ULONG HopSet; // to one if non-802.11
+ ULONG DwellTime; // units are Kusec
+} NDIS_802_11_CONFIGURATION_FH, *PNDIS_802_11_CONFIGURATION_FH;
+
+typedef struct _NDIS_802_11_CONFIGURATION
+{
+ ULONG Length; // Length of structure
+ ULONG BeaconPeriod; // units are Kusec
+ ULONG ATIMWindow; // units are Kusec
+ ULONG DSConfig; // Frequency, units are kHz
+ NDIS_802_11_CONFIGURATION_FH FHConfig;
+} NDIS_802_11_CONFIGURATION, *PNDIS_802_11_CONFIGURATION;
+
+typedef ULONG NDIS_802_11_KEY_INDEX;
+typedef ULONGLONG NDIS_802_11_KEY_RSC;
+
+// Key mapping keys require a BSSID
+typedef struct _NDIS_802_11_KEY
+{
+ UINT Length; // Length of this structure
+ UINT KeyIndex;
+ UINT KeyLength; // length of key in bytes
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_KEY_RSC KeyRSC;
+ UCHAR KeyMaterial[1]; // variable length depending on above field
+} NDIS_802_11_KEY, *PNDIS_802_11_KEY;
+
+typedef struct _NDIS_802_11_REMOVE_KEY
+{
+ UINT Length; // Length of this structure
+ UINT KeyIndex;
+ NDIS_802_11_MAC_ADDRESS BSSID;
+} NDIS_802_11_REMOVE_KEY, *PNDIS_802_11_REMOVE_KEY;
+
+typedef struct PACKED _NDIS_802_11_WEP
+{
+ UINT Length; // Length of this structure
+ UINT KeyIndex; // 0 is the per-client key, 1-N are the
+ // global keys
+ UINT KeyLength; // length of key in bytes
+ UCHAR KeyMaterial[1];// variable length depending on above field
+} NDIS_802_11_WEP, *PNDIS_802_11_WEP;
+
+
+typedef enum _NDIS_802_11_NETWORK_INFRASTRUCTURE
+{
+ Ndis802_11IBSS,
+ Ndis802_11Infrastructure,
+ Ndis802_11AutoUnknown,
+ Ndis802_11InfrastructureMax // Not a real value, defined as upper bound
+} NDIS_802_11_NETWORK_INFRASTRUCTURE, *PNDIS_802_11_NETWORK_INFRASTRUCTURE;
+
+// PMKID Structures
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct _BSSID_INFO
+{
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO, *PBSSID_INFO;
+
+typedef struct _NDIS_802_11_PMKID
+{
+ ULONG Length;
+ ULONG BSSIDInfoCount;
+ BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID, *PNDIS_802_11_PMKID;
+
+//Added new types for PMKID Candidate lists.
+typedef struct _PMKID_CANDIDATE {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ ULONG Flags;
+} PMKID_CANDIDATE, *PPMKID_CANDIDATE;
+
+typedef struct _NDIS_802_11_PMKID_CANDIDATE_LIST
+{
+ ULONG Version; // Version of the structure
+ ULONG NumCandidates; // No. of pmkid candidates
+ PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST, *PNDIS_802_11_PMKID_CANDIDATE_LIST;
+
+//Flags for PMKID Candidate list structure
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+// Add new authentication modes
+typedef enum _NDIS_802_11_AUTHENTICATION_MODE
+{
+ Ndis802_11AuthModeOpen,
+ Ndis802_11AuthModeShared,
+ Ndis802_11AuthModeAutoSwitch,
+ Ndis802_11AuthModeWPA,
+ Ndis802_11AuthModeWPAPSK,
+ Ndis802_11AuthModeWPANone,
+ Ndis802_11AuthModeWPA2,
+ Ndis802_11AuthModeWPA2PSK,
+ Ndis802_11AuthModeMax // Not a real mode, defined as upper bound
+} NDIS_802_11_AUTHENTICATION_MODE, *PNDIS_802_11_AUTHENTICATION_MODE;
+
+typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES]; // Set of 8 data rates
+typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; // Set of 16 data rates
+
+typedef struct PACKED _NDIS_802_11_SSID
+{
+ INT SsidLength; // length of SSID field below, in bytes;
+ // this can be zero.
+ UCHAR Ssid[NDIS_802_11_LENGTH_SSID]; // SSID information field
+} NDIS_802_11_SSID, *PNDIS_802_11_SSID;
+
+
+typedef struct PACKED _NDIS_WLAN_BSSID
+{
+ ULONG Length; // Length of this structure
+ NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID
+ UCHAR Reserved[2];
+ NDIS_802_11_SSID Ssid; // SSID
+ ULONG Privacy; // WEP encryption requirement
+ NDIS_802_11_RSSI Rssi; // receive signal
+ // strength in dBm
+ NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+ NDIS_802_11_CONFIGURATION Configuration;
+ NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+ NDIS_802_11_RATES SupportedRates;
+} NDIS_WLAN_BSSID, *PNDIS_WLAN_BSSID;
+
+typedef struct PACKED _NDIS_802_11_BSSID_LIST
+{
+ UINT NumberOfItems; // in list below, at least 1
+ NDIS_WLAN_BSSID Bssid[1];
+} NDIS_802_11_BSSID_LIST, *PNDIS_802_11_BSSID_LIST;
+
+// Added Capabilities, IELength and IEs for each BSSID
+typedef struct PACKED _NDIS_WLAN_BSSID_EX
+{
+ ULONG Length; // Length of this structure
+ NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID
+ UCHAR Reserved[2];
+ NDIS_802_11_SSID Ssid; // SSID
+ UINT Privacy; // WEP encryption requirement
+ NDIS_802_11_RSSI Rssi; // receive signal
+ // strength in dBm
+ NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+ NDIS_802_11_CONFIGURATION Configuration;
+ NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+ NDIS_802_11_RATES_EX SupportedRates;
+ ULONG IELength;
+ UCHAR IEs[1];
+} NDIS_WLAN_BSSID_EX, *PNDIS_WLAN_BSSID_EX;
+
+typedef struct PACKED _NDIS_802_11_BSSID_LIST_EX
+{
+ UINT NumberOfItems; // in list below, at least 1
+ NDIS_WLAN_BSSID_EX Bssid[1];
+} NDIS_802_11_BSSID_LIST_EX, *PNDIS_802_11_BSSID_LIST_EX;
+
+typedef struct PACKED _NDIS_802_11_FIXED_IEs
+{
+ UCHAR Timestamp[8];
+ USHORT BeaconInterval;
+ USHORT Capabilities;
+} NDIS_802_11_FIXED_IEs, *PNDIS_802_11_FIXED_IEs;
+
+// Added new encryption types
+// Also aliased typedef to new name
+typedef enum _NDIS_802_11_WEP_STATUS
+{
+ Ndis802_11WEPEnabled,
+ Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+ Ndis802_11WEPDisabled,
+ Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+ Ndis802_11WEPKeyAbsent,
+ Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+ Ndis802_11WEPNotSupported,
+ Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+ Ndis802_11Encryption2Enabled,
+ Ndis802_11Encryption2KeyAbsent,
+ Ndis802_11Encryption3Enabled,
+ Ndis802_11Encryption3KeyAbsent
+} NDIS_802_11_WEP_STATUS, *PNDIS_802_11_WEP_STATUS,
+ NDIS_802_11_ENCRYPTION_STATUS, *PNDIS_802_11_ENCRYPTION_STATUS;
+
+typedef enum _NDIS_802_11_RELOAD_DEFAULTS
+{
+ Ndis802_11ReloadWEPKeys
+} NDIS_802_11_RELOAD_DEFAULTS, *PNDIS_802_11_RELOAD_DEFAULTS;
+
+#define NDIS_802_11_AI_REQFI_CAPABILITIES 1
+#define NDIS_802_11_AI_REQFI_LISTENINTERVAL 2
+#define NDIS_802_11_AI_REQFI_CURRENTAPADDRESS 4
+
+#define NDIS_802_11_AI_RESFI_CAPABILITIES 1
+#define NDIS_802_11_AI_RESFI_STATUSCODE 2
+#define NDIS_802_11_AI_RESFI_ASSOCIATIONID 4
+
+typedef struct _NDIS_802_11_AI_REQFI
+{
+ USHORT Capabilities;
+ USHORT ListenInterval;
+ NDIS_802_11_MAC_ADDRESS CurrentAPAddress;
+} NDIS_802_11_AI_REQFI, *PNDIS_802_11_AI_REQFI;
+
+typedef struct _NDIS_802_11_AI_RESFI
+{
+ USHORT Capabilities;
+ USHORT StatusCode;
+ USHORT AssociationId;
+} NDIS_802_11_AI_RESFI, *PNDIS_802_11_AI_RESFI;
+
+typedef struct _NDIS_802_11_ASSOCIATION_INFORMATION
+{
+ ULONG Length;
+ USHORT AvailableRequestFixedIEs;
+ NDIS_802_11_AI_REQFI RequestFixedIEs;
+ ULONG RequestIELength;
+ ULONG OffsetRequestIEs;
+ USHORT AvailableResponseFixedIEs;
+ NDIS_802_11_AI_RESFI ResponseFixedIEs;
+ ULONG ResponseIELength;
+ ULONG OffsetResponseIEs;
+} NDIS_802_11_ASSOCIATION_INFORMATION, *PNDIS_802_11_ASSOCIATION_INFORMATION;
+
+struct ndis_pmkid_entry {
+ struct ndis_pmkid_entry *next;
+ u8 bssid[ETH_ALEN];
+ u8 pmkid[16];
+};
+
+typedef struct _MLME_DEAUTH_REQ_STRUCT {
+ UCHAR Addr[MAC_ADDR_LEN];
+ USHORT Reason;
+} MLME_DEAUTH_REQ_STRUCT, *PMLME_DEAUTH_REQ_STRUCT;
diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c
new file mode 100644
index 0000000..c014b96
--- /dev/null
+++ b/src/drivers/driver_roboswitch.c
@@ -0,0 +1,480 @@
+/*
+ * WPA Supplicant - roboswitch driver interface
+ * Copyright (c) 2008-2009 Jouke Witteveen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <linux/if_ether.h>
+#include <linux/mii.h>
+#include <net/if.h>
+
+#include "common.h"
+#include "driver.h"
+#include "l2_packet/l2_packet.h"
+
+#define ROBO_PHY_ADDR 0x1e /* RoboSwitch PHY address */
+
+/* MII access registers */
+#define ROBO_MII_PAGE 0x10 /* MII page register */
+#define ROBO_MII_ADDR 0x11 /* MII address register */
+#define ROBO_MII_DATA_OFFSET 0x18 /* Start of MII data registers */
+
+#define ROBO_MII_PAGE_ENABLE 0x01 /* MII page op code */
+#define ROBO_MII_ADDR_WRITE 0x01 /* MII address write op code */
+#define ROBO_MII_ADDR_READ 0x02 /* MII address read op code */
+#define ROBO_MII_DATA_MAX 4 /* Consecutive MII data registers */
+#define ROBO_MII_RETRY_MAX 10 /* Read attempts before giving up */
+
+/* Page numbers */
+#define ROBO_ARLCTRL_PAGE 0x04 /* ARL control page */
+#define ROBO_VLAN_PAGE 0x34 /* VLAN page */
+
+/* ARL control page registers */
+#define ROBO_ARLCTRL_CONF 0x00 /* ARL configuration register */
+#define ROBO_ARLCTRL_ADDR_1 0x10 /* Multiport address 1 */
+#define ROBO_ARLCTRL_VEC_1 0x16 /* Multiport vector 1 */
+#define ROBO_ARLCTRL_ADDR_2 0x20 /* Multiport address 2 */
+#define ROBO_ARLCTRL_VEC_2 0x26 /* Multiport vector 2 */
+
+/* VLAN page registers */
+#define ROBO_VLAN_ACCESS 0x08 /* VLAN table access register */
+#define ROBO_VLAN_ACCESS_5350 0x06 /* VLAN table access register (5350) */
+#define ROBO_VLAN_READ 0x0c /* VLAN read register */
+#define ROBO_VLAN_MAX 0xff /* Maximum number of VLANs */
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+struct wpa_driver_roboswitch_data {
+ void *ctx;
+ struct l2_packet_data *l2;
+ char ifname[IFNAMSIZ + 1];
+ u8 own_addr[ETH_ALEN];
+ struct ifreq ifr;
+ int fd, is_5350;
+ u16 ports;
+};
+
+
+/* Copied from the kernel-only part of mii.h. */
+static inline struct mii_ioctl_data *if_mii(struct ifreq *rq)
+{
+ return (struct mii_ioctl_data *) &rq->ifr_ifru;
+}
+
+
+/*
+ * RoboSwitch uses 16-bit Big Endian addresses.
+ * The ordering of the words is reversed in the MII registers.
+ */
+static void wpa_driver_roboswitch_addr_be16(const u8 addr[ETH_ALEN], u16 *be)
+{
+ int i;
+ for (i = 0; i < ETH_ALEN; i += 2)
+ be[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i);
+}
+
+
+static u16 wpa_driver_roboswitch_mdio_read(
+ struct wpa_driver_roboswitch_data *drv, u8 reg)
+{
+ struct mii_ioctl_data *mii = if_mii(&drv->ifr);
+
+ mii->phy_id = ROBO_PHY_ADDR;
+ mii->reg_num = reg;
+
+ if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) {
+ perror("ioctl[SIOCGMIIREG]");
+ return 0x00;
+ }
+ return mii->val_out;
+}
+
+
+static void wpa_driver_roboswitch_mdio_write(
+ struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val)
+{
+ struct mii_ioctl_data *mii = if_mii(&drv->ifr);
+
+ mii->phy_id = ROBO_PHY_ADDR;
+ mii->reg_num = reg;
+ mii->val_in = val;
+
+ if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) {
+ perror("ioctl[SIOCSMIIREG");
+ }
+}
+
+
+static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data *drv,
+ u8 page, u8 reg, u8 op)
+{
+ int i;
+
+ /* set page number */
+ wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_PAGE,
+ (page << 8) | ROBO_MII_PAGE_ENABLE);
+ /* set register address */
+ wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_ADDR, (reg << 8) | op);
+
+ /* check if operation completed */
+ for (i = 0; i < ROBO_MII_RETRY_MAX; ++i) {
+ if ((wpa_driver_roboswitch_mdio_read(drv, ROBO_MII_ADDR) & 3)
+ == 0)
+ return 0;
+ }
+ /* timeout */
+ return -1;
+}
+
+
+static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data *drv,
+ u8 page, u8 reg, u16 *val, int len)
+{
+ int i;
+
+ if (len > ROBO_MII_DATA_MAX ||
+ wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_READ) < 0)
+ return -1;
+
+ for (i = 0; i < len; ++i) {
+ val[i] = wpa_driver_roboswitch_mdio_read(
+ drv, ROBO_MII_DATA_OFFSET + i);
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv,
+ u8 page, u8 reg, u16 *val, int len)
+{
+ int i;
+
+ if (len > ROBO_MII_DATA_MAX) return -1;
+ for (i = 0; i < len; ++i) {
+ wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_DATA_OFFSET + i,
+ val[i]);
+ }
+ return wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_WRITE);
+}
+
+
+static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_driver_roboswitch_data *drv = priv;
+
+ if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL &&
+ os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0)
+ drv_event_eapol_rx(drv->ctx, src_addr, buf + 14, len - 14);
+}
+
+
+static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid)
+{
+ ssid[0] = 0;
+ return 0;
+}
+
+
+static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid)
+{
+ /* Report PAE group address as the "BSSID" for wired connection. */
+ os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+ return 0;
+}
+
+
+static int wpa_driver_roboswitch_get_capa(void *priv,
+ struct wpa_driver_capa *capa)
+{
+ os_memset(capa, 0, sizeof(*capa));
+ capa->flags = WPA_DRIVER_FLAGS_WIRED;
+ return 0;
+}
+
+
+static int wpa_driver_roboswitch_set_param(void *priv, const char *param)
+{
+ struct wpa_driver_roboswitch_data *drv = priv;
+ char *sep;
+
+ if (param == NULL || os_strstr(param, "multicast_only=1") == NULL) {
+ sep = drv->ifname + os_strlen(drv->ifname);
+ *sep = '.';
+ drv->l2 = l2_packet_init(drv->ifname, NULL, ETH_P_ALL,
+ wpa_driver_roboswitch_receive, drv,
+ 1);
+ if (drv->l2 == NULL) {
+ wpa_printf(MSG_INFO, "%s: Unable to listen on %s",
+ __func__, drv->ifname);
+ return -1;
+ }
+ *sep = '\0';
+ l2_packet_get_own_addr(drv->l2, drv->own_addr);
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: Ignoring unicast frames", __func__);
+ drv->l2 = NULL;
+ }
+ return 0;
+}
+
+
+static const char * wpa_driver_roboswitch_get_ifname(void *priv)
+{
+ struct wpa_driver_roboswitch_data *drv = priv;
+ return drv->ifname;
+}
+
+
+static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv,
+ u16 ports, const u8 *addr)
+{
+ u16 read1[3], read2[3], addr_be16[3];
+
+ wpa_driver_roboswitch_addr_be16(addr, addr_be16);
+
+ if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_CONF, read1, 1) < 0)
+ return -1;
+ if (!(read1[0] & (1 << 4))) {
+ /* multiport addresses are not yet enabled */
+ read1[0] |= 1 << 4;
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1, addr_be16, 3);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_1, &ports, 1);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2, addr_be16, 3);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2, &ports, 1);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_CONF, read1, 1);
+ } else {
+ /* if both multiport addresses are the same we can add */
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1, read1, 3);
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2, read2, 3);
+ if (os_memcmp(read1, read2, 6) != 0)
+ return -1;
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_1, read1, 1);
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2, read2, 1);
+ if (read1[0] != read2[0])
+ return -1;
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1, addr_be16, 3);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_1, &ports, 1);
+ }
+ return 0;
+}
+
+
+static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv,
+ u16 ports, const u8 *addr)
+{
+ u16 _read, addr_be16[3], addr_read[3], ports_read;
+
+ wpa_driver_roboswitch_addr_be16(addr, addr_be16);
+
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF,
+ &_read, 1);
+ /* If ARL control is disabled, there is nothing to leave. */
+ if (!(_read & (1 << 4))) return -1;
+
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1, addr_read, 3);
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1,
+ &ports_read, 1);
+ /* check if we occupy multiport address 1 */
+ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) {
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2, addr_read, 3);
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2, &ports_read, 1);
+ /* and multiport address 2 */
+ if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
+ ports_read == ports) {
+ _read &= ~(1 << 4);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_CONF, &_read,
+ 1);
+ } else {
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1,
+ addr_read, 3);
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_1,
+ &ports_read, 1);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2,
+ addr_read, 3);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2,
+ &ports_read, 1);
+ }
+ } else {
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2, addr_read, 3);
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2, &ports_read, 1);
+ /* or multiport address 2 */
+ if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
+ ports_read == ports) {
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1,
+ addr_read, 3);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_1,
+ &ports_read, 1);
+ } else return -1;
+ }
+ return 0;
+}
+
+
+static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_roboswitch_data *drv;
+ char *sep;
+ u16 vlan = 0, _read[2];
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL) return NULL;
+ drv->ctx = ctx;
+ drv->own_addr[0] = '\0';
+
+ /* copy ifname and take a pointer to the second to last character */
+ sep = drv->ifname +
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2;
+ /* find the '.' seperating <interface> and <vlan> */
+ while (sep > drv->ifname && *sep != '.') sep--;
+ if (sep <= drv->ifname) {
+ wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in "
+ "interface name %s", __func__, drv->ifname);
+ os_free(drv);
+ return NULL;
+ }
+ *sep = '\0';
+ while (*++sep) {
+ if (*sep < '0' || *sep > '9') {
+ wpa_printf(MSG_INFO, "%s: Invalid vlan specification "
+ "in interface name %s", __func__, ifname);
+ os_free(drv);
+ return NULL;
+ }
+ vlan *= 10;
+ vlan += *sep - '0';
+ if (vlan > ROBO_VLAN_MAX) {
+ wpa_printf(MSG_INFO, "%s: VLAN out of range in "
+ "interface name %s", __func__, ifname);
+ os_free(drv);
+ return NULL;
+ }
+ }
+
+ drv->fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->fd < 0) {
+ wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__);
+ os_free(drv);
+ return NULL;
+ }
+
+ os_memset(&drv->ifr, 0, sizeof(drv->ifr));
+ os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ);
+ if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) {
+ perror("ioctl[SIOCGMIIPHY]");
+ os_free(drv);
+ return NULL;
+ }
+ if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR) {
+ wpa_printf(MSG_INFO, "%s: Invalid phy address (not a "
+ "RoboSwitch?)", __func__);
+ os_free(drv);
+ return NULL;
+ }
+
+ /* set and read back to see if the register can be used */
+ _read[0] = ROBO_VLAN_MAX;
+ wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350,
+ _read, 1);
+ wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350,
+ _read + 1, 1);
+ drv->is_5350 = _read[0] == _read[1];
+
+ /* set the read bit */
+ vlan |= 1 << 13;
+ wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE,
+ drv->is_5350 ? ROBO_VLAN_ACCESS_5350
+ : ROBO_VLAN_ACCESS,
+ &vlan, 1);
+ wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, _read,
+ drv->is_5350 ? 2 : 1);
+ if (!(drv->is_5350 ? _read[1] & (1 << 4) : _read[0] & (1 << 14))) {
+ wpa_printf(MSG_INFO, "%s: Could not get port information for "
+ "VLAN %d", __func__, vlan & ~(1 << 13));
+ os_free(drv);
+ return NULL;
+ }
+ drv->ports = _read[0] & 0x001F;
+ /* add the MII port */
+ drv->ports |= 1 << 8;
+ if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) {
+ wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__);
+ os_free(drv);
+ return NULL;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: Added PAE group address to "
+ "RoboSwitch ARL", __func__);
+ }
+
+ return drv;
+}
+
+
+static void wpa_driver_roboswitch_deinit(void *priv)
+{
+ struct wpa_driver_roboswitch_data *drv = priv;
+
+ if (drv->l2) {
+ l2_packet_deinit(drv->l2);
+ drv->l2 = NULL;
+ }
+ if (wpa_driver_roboswitch_leave(drv, drv->ports, pae_group_addr) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group",
+ __func__);
+ }
+
+ close(drv->fd);
+ os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_roboswitch_ops = {
+ .name = "roboswitch",
+ .desc = "wpa_supplicant roboswitch driver",
+ .get_ssid = wpa_driver_roboswitch_get_ssid,
+ .get_bssid = wpa_driver_roboswitch_get_bssid,
+ .get_capa = wpa_driver_roboswitch_get_capa,
+ .init = wpa_driver_roboswitch_init,
+ .deinit = wpa_driver_roboswitch_deinit,
+ .set_param = wpa_driver_roboswitch_set_param,
+ .get_ifname = wpa_driver_roboswitch_get_ifname,
+};
diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c
new file mode 100644
index 0000000..6bfa46d
--- /dev/null
+++ b/src/drivers/driver_test.c
@@ -0,0 +1,3391 @@
+/*
+ * Testing driver interface for a simulated network driver
+ * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+/* Make sure we get winsock2.h for Windows build to get sockaddr_storage */
+#include "build_config.h"
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <winsock2.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/un.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#define DRIVER_TEST_UNIX
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "utils/trace.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/sha1.h"
+#include "l2_packet/l2_packet.h"
+#include "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 use_mlme;
+ int associated;
+ u8 *probe_req_ie;
+ size_t probe_req_ie_len;
+ u8 probe_req_ssid[32];
+ size_t probe_req_ssid_len;
+ int ibss;
+ int ap;
+
+ struct test_client_socket *cli;
+ struct dl_list bss;
+ int udp_port;
+
+ int alloc_iface_idx;
+
+ int probe_req_report;
+ unsigned int remain_on_channel_freq;
+ unsigned int remain_on_channel_duration;
+
+ int current_freq;
+
+ 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_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 = &eth;
+ io[1].iov_len = sizeof(eth);
+ io[2].iov_base = (u8 *) data;
+ io[2].iov_len = data_len;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = 3;
+ msg.msg_name = &cli->un;
+ msg.msg_namelen = cli->unlen;
+ return sendmsg(drv->test_socket, &msg, 0);
+}
+
+
+static int test_driver_send_ether(void *priv, const u8 *dst, const u8 *src,
+ u16 proto, const u8 *data, size_t data_len)
+{
+ struct test_driver_bss *dbss = priv;
+ struct wpa_driver_test_data *drv = dbss->drv;
+ struct msghdr msg;
+ struct iovec io[3];
+ struct l2_ethhdr eth;
+ char desttxt[30];
+ struct sockaddr_un addr;
+ struct dirent *dent;
+ DIR *dir;
+ int ret = 0, broadcast = 0, count = 0;
+
+ if (drv->test_socket < 0 || drv->test_dir == NULL) {
+ wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d "
+ "test_dir=%p)",
+ __func__, drv->test_socket, drv->test_dir);
+ return -1;
+ }
+
+ broadcast = memcmp(dst, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0;
+ snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dst));
+
+ memcpy(eth.h_dest, dst, ETH_ALEN);
+ memcpy(eth.h_source, src, ETH_ALEN);
+ eth.h_proto = host_to_be16(proto);
+
+ io[0].iov_base = "ETHER ";
+ io[0].iov_len = 6;
+ io[1].iov_base = &eth;
+ io[1].iov_len = sizeof(eth);
+ io[2].iov_base = (u8 *) data;
+ io[2].iov_len = data_len;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = 3;
+
+ dir = opendir(drv->test_dir);
+ if (dir == NULL) {
+ perror("test_driver: opendir");
+ return -1;
+ }
+ while ((dent = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+ /* Skip the file if it is not a socket. Also accept
+ * DT_UNKNOWN (0) in case the C library or underlying file
+ * system does not support d_type. */
+ if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
+ continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+ if (strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0)
+ continue;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
+ drv->test_dir, dent->d_name);
+
+ if (strcmp(addr.sun_path, drv->own_socket_path) == 0)
+ continue;
+ if (!broadcast && strstr(dent->d_name, desttxt) == NULL)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "%s: Send ether frame to %s",
+ __func__, dent->d_name);
+
+ msg.msg_name = &addr;
+ msg.msg_namelen = sizeof(addr);
+ ret = sendmsg(drv->test_socket, &msg, 0);
+ if (ret < 0)
+ perror("driver_test: sendmsg");
+ count++;
+ }
+ closedir(dir);
+
+ if (!broadcast && count == 0) {
+ wpa_printf(MSG_DEBUG, "%s: Destination " MACSTR " not found",
+ __func__, MAC2STR(dst));
+ return -1;
+ }
+
+ return ret;
+}
+
+
+static int wpa_driver_test_send_mlme(void *priv, const u8 *data,
+ size_t data_len)
+{
+ struct test_driver_bss *dbss = priv;
+ struct wpa_driver_test_data *drv = dbss->drv;
+ struct msghdr msg;
+ struct iovec io[2];
+ const u8 *dest;
+ struct sockaddr_un addr;
+ struct dirent *dent;
+ DIR *dir;
+ int broadcast;
+ int ret = 0;
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+ char cmd[50];
+ int freq;
+#ifdef HOSTAPD
+ char desttxt[30];
+#endif /* HOSTAPD */
+ union wpa_event_data event;
+
+ wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len);
+ if (drv->test_socket < 0 || data_len < 10) {
+ wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d len=%lu"
+ " test_dir=%p)",
+ __func__, drv->test_socket,
+ (unsigned long) data_len,
+ drv->test_dir);
+ return -1;
+ }
+
+ dest = data + 4;
+ broadcast = os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0;
+
+#ifdef HOSTAPD
+ snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dest));
+#endif /* HOSTAPD */
+
+ if (drv->remain_on_channel_freq)
+ freq = drv->remain_on_channel_freq;
+ else
+ freq = drv->current_freq;
+ wpa_printf(MSG_DEBUG, "test_driver(%s): MLME TX on freq %d MHz",
+ dbss->ifname, freq);
+ os_snprintf(cmd, sizeof(cmd), "MLME freq=%d ", freq);
+ io[0].iov_base = cmd;
+ io[0].iov_len = os_strlen(cmd);
+ io[1].iov_base = (void *) data;
+ io[1].iov_len = data_len;
+
+ os_memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = 2;
+
+#ifdef HOSTAPD
+ if (drv->test_dir == NULL) {
+ wpa_printf(MSG_DEBUG, "%s: test_dir == NULL", __func__);
+ return -1;
+ }
+
+ dir = opendir(drv->test_dir);
+ if (dir == NULL) {
+ perror("test_driver: opendir");
+ return -1;
+ }
+ while ((dent = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+ /* Skip the file if it is not a socket. Also accept
+ * DT_UNKNOWN (0) in case the C library or underlying file
+ * system does not support d_type. */
+ if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
+ continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+ if (os_strcmp(dent->d_name, ".") == 0 ||
+ os_strcmp(dent->d_name, "..") == 0)
+ continue;
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
+ drv->test_dir, dent->d_name);
+
+ if (os_strcmp(addr.sun_path, drv->own_socket_path) == 0)
+ continue;
+ if (!broadcast && os_strstr(dent->d_name, desttxt) == NULL)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "%s: Send management frame to %s",
+ __func__, dent->d_name);
+
+ msg.msg_name = &addr;
+ msg.msg_namelen = sizeof(addr);
+ ret = sendmsg(drv->test_socket, &msg, 0);
+ if (ret < 0)
+ perror("driver_test: sendmsg(test_socket)");
+ }
+ closedir(dir);
+#else /* HOSTAPD */
+
+ if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 ||
+ drv->test_dir == NULL) {
+ if (drv->hostapd_addr_udp_set) {
+ msg.msg_name = &drv->hostapd_addr_udp;
+ msg.msg_namelen = sizeof(drv->hostapd_addr_udp);
+ } else {
+#ifdef DRIVER_TEST_UNIX
+ msg.msg_name = &drv->hostapd_addr;
+ msg.msg_namelen = sizeof(drv->hostapd_addr);
+#endif /* DRIVER_TEST_UNIX */
+ }
+ } else if (broadcast) {
+ dir = opendir(drv->test_dir);
+ if (dir == NULL)
+ return -1;
+ while ((dent = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+ /* Skip the file if it is not a socket.
+ * Also accept DT_UNKNOWN (0) in case
+ * the C library or underlying file
+ * system does not support d_type. */
+ if (dent->d_type != DT_SOCK &&
+ dent->d_type != DT_UNKNOWN)
+ continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+ if (os_strcmp(dent->d_name, ".") == 0 ||
+ os_strcmp(dent->d_name, "..") == 0)
+ continue;
+ wpa_printf(MSG_DEBUG, "%s: Send broadcast MLME to %s",
+ __func__, dent->d_name);
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_snprintf(addr.sun_path, sizeof(addr.sun_path),
+ "%s/%s", drv->test_dir, dent->d_name);
+
+ msg.msg_name = &addr;
+ msg.msg_namelen = sizeof(addr);
+
+ ret = sendmsg(drv->test_socket, &msg, 0);
+ if (ret < 0)
+ perror("driver_test: sendmsg(test_socket)");
+ }
+ closedir(dir);
+ return ret;
+ } else {
+ struct stat st;
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_snprintf(addr.sun_path, sizeof(addr.sun_path),
+ "%s/AP-" MACSTR, drv->test_dir, MAC2STR(dest));
+ if (stat(addr.sun_path, &st) < 0) {
+ os_snprintf(addr.sun_path, sizeof(addr.sun_path),
+ "%s/STA-" MACSTR,
+ drv->test_dir, MAC2STR(dest));
+ }
+ msg.msg_name = &addr;
+ msg.msg_namelen = sizeof(addr);
+ }
+
+ if (sendmsg(drv->test_socket, &msg, 0) < 0) {
+ perror("sendmsg(test_socket)");
+ return -1;
+ }
+#endif /* HOSTAPD */
+
+ hdr = (struct ieee80211_hdr *) data;
+ fc = le_to_host16(hdr->frame_control);
+
+ os_memset(&event, 0, sizeof(event));
+ event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+ event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+ event.tx_status.dst = hdr->addr1;
+ event.tx_status.data = data;
+ event.tx_status.data_len = data_len;
+ event.tx_status.ack = ret >= 0;
+ wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+
+#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, 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)
+{
+ 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_valid_bss_mask(void *priv, const u8 *addr,
+ const u8 *mask)
+{
+ return 0;
+}
+
+
+static int test_driver_set_ssid(void *priv, const u8 *buf, int len)
+{
+ struct test_driver_bss *bss = priv;
+
+ wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, bss->ifname);
+ 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);
+
+ bss->bss_ctx = hapd;
+ os_memcpy(bss->bssid, drv->own_addr, ETH_ALEN);
+ os_memcpy(params->own_addr, drv->own_addr, ETH_ALEN);
+
+ if (params->test_socket) {
+ if (os_strlen(params->test_socket) >=
+ sizeof(addr_un.sun_path)) {
+ printf("Too long test_socket path\n");
+ wpa_driver_test_deinit(bss);
+ return NULL;
+ }
+ if (strncmp(params->test_socket, "DIR:", 4) == 0) {
+ size_t len = strlen(params->test_socket) + 30;
+ drv->test_dir = os_strdup(params->test_socket + 4);
+ drv->own_socket_path = os_malloc(len);
+ if (drv->own_socket_path) {
+ snprintf(drv->own_socket_path, len,
+ "%s/AP-" MACSTR,
+ params->test_socket + 4,
+ MAC2STR(params->own_addr));
+ }
+ } else if (strncmp(params->test_socket, "UDP:", 4) == 0) {
+ drv->udp_port = atoi(params->test_socket + 4);
+ } else {
+ drv->own_socket_path = os_strdup(params->test_socket);
+ }
+ if (drv->own_socket_path == NULL && drv->udp_port == 0) {
+ wpa_driver_test_deinit(bss);
+ return NULL;
+ }
+
+ drv->test_socket = socket(drv->udp_port ? PF_INET : PF_UNIX,
+ SOCK_DGRAM, 0);
+ if (drv->test_socket < 0) {
+ perror("socket");
+ wpa_driver_test_deinit(bss);
+ return NULL;
+ }
+
+ if (drv->udp_port) {
+ os_memset(&addr_in, 0, sizeof(addr_in));
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_port = htons(drv->udp_port);
+ addr = (struct sockaddr *) &addr_in;
+ alen = sizeof(addr_in);
+ } else {
+ os_memset(&addr_un, 0, sizeof(addr_un));
+ addr_un.sun_family = AF_UNIX;
+ os_strlcpy(addr_un.sun_path, drv->own_socket_path,
+ sizeof(addr_un.sun_path));
+ addr = (struct sockaddr *) &addr_un;
+ alen = sizeof(addr_un);
+ }
+ if (bind(drv->test_socket, addr, alen) < 0) {
+ perror("bind(PF_UNIX)");
+ close(drv->test_socket);
+ if (drv->own_socket_path)
+ unlink(drv->own_socket_path);
+ wpa_driver_test_deinit(bss);
+ return NULL;
+ }
+ eloop_register_read_sock(drv->test_socket,
+ test_driver_receive_unix, drv, NULL);
+ } else
+ drv->test_socket = -1;
+
+ return bss;
+}
+
+
+static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_test_data *drv = eloop_ctx;
+
+#ifdef DRIVER_TEST_UNIX
+ if (drv->associated && drv->hostapd_addr_set) {
+ struct stat st;
+ if (stat(drv->hostapd_addr.sun_path, &st) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: lost connection to AP: %s",
+ __func__, strerror(errno));
+ drv->associated = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ }
+ }
+#endif /* DRIVER_TEST_UNIX */
+
+ eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL);
+}
+
+
+static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ 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;
+ 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, 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_zalloc(drv->num_scanres * sizeof(struct wpa_scan_res *));
+ if (res->res == NULL) {
+ os_free(res);
+ return NULL;
+ }
+
+ for (i = 0; i < drv->num_scanres; i++) {
+ struct wpa_scan_res *r;
+ if (drv->scanres[i] == NULL)
+ continue;
+ r = os_malloc(sizeof(*r) + drv->scanres[i]->ie_len);
+ if (r == NULL)
+ break;
+ os_memcpy(r, drv->scanres[i],
+ sizeof(*r) + drv->scanres[i]->ie_len);
+ res->res[res->num++] = r;
+ }
+
+ return res;
+}
+
+
+static int wpa_driver_test_set_key(const char *ifname, void *priv,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ wpa_printf(MSG_DEBUG, "%s: ifname=%s priv=%p alg=%d key_idx=%d "
+ "set_tx=%d",
+ __func__, ifname, priv, alg, key_idx, set_tx);
+ if (addr)
+ wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr));
+ if (seq)
+ wpa_hexdump(MSG_DEBUG, " seq", seq, seq_len);
+ if (key)
+ wpa_hexdump_key(MSG_DEBUG, " key", key, key_len);
+ return 0;
+}
+
+
+static int wpa_driver_update_mode(struct wpa_driver_test_data *drv, int ap)
+{
+ if (ap && !drv->ap) {
+ wpa_driver_test_close_test_socket(drv);
+ wpa_driver_test_attach(drv, drv->test_dir, 1);
+ drv->ap = 1;
+ } else if (!ap && drv->ap) {
+ wpa_driver_test_close_test_socket(drv);
+ wpa_driver_test_attach(drv, drv->test_dir, 0);
+ drv->ap = 0;
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_test_associate(
+ void *priv, struct wpa_driver_associate_params *params)
+{
+ struct test_driver_bss *dbss = priv;
+ struct wpa_driver_test_data *drv = dbss->drv;
+ wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
+ "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
+ __func__, priv, params->freq, params->pairwise_suite,
+ params->group_suite, params->key_mgmt_suite,
+ params->auth_alg, params->mode);
+ 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 int wpa_driver_test_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct test_driver_bss *dbss = priv;
+ struct wpa_driver_test_data *drv = dbss->drv;
+ wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
+ __func__, MAC2STR(addr), reason_code);
+ os_memset(dbss->bssid, 0, ETH_ALEN);
+ drv->associated = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ return wpa_driver_test_send_disassoc(drv);
+}
+
+
+static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
+{
+ const u8 *end, *pos;
+
+ pos = (const u8 *) (res + 1);
+ end = pos + res->ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == ie)
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+
+static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv,
+ struct sockaddr *from,
+ socklen_t fromlen,
+ const char *data)
+{
+ struct wpa_scan_res *res;
+ const char *pos, *pos2;
+ size_t len;
+ u8 *ie_pos, *ie_start, *ie_end;
+#define MAX_IE_LEN 1000
+ const u8 *ds_params;
+
+ wpa_printf(MSG_DEBUG, "test_driver: SCANRESP %s", data);
+ if (drv->num_scanres >= MAX_SCAN_RESULTS) {
+ wpa_printf(MSG_DEBUG, "test_driver: No room for the new scan "
+ "result");
+ return;
+ }
+
+ /* SCANRESP BSSID SSID IEs */
+
+ res = os_zalloc(sizeof(*res) + MAX_IE_LEN);
+ if (res == NULL)
+ return;
+ ie_start = ie_pos = (u8 *) (res + 1);
+ ie_end = ie_pos + MAX_IE_LEN;
+
+ if (hwaddr_aton(data, res->bssid)) {
+ wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in scanres");
+ os_free(res);
+ return;
+ }
+
+ pos = data + 17;
+ while (*pos == ' ')
+ pos++;
+ pos2 = os_strchr(pos, ' ');
+ if (pos2 == NULL) {
+ wpa_printf(MSG_DEBUG, "test_driver: invalid SSID termination "
+ "in scanres");
+ os_free(res);
+ return;
+ }
+ len = (pos2 - pos) / 2;
+ if (len > 32)
+ len = 32;
+ /*
+ * Generate SSID IE from the SSID field since this IE is not included
+ * in the main IE field.
+ */
+ *ie_pos++ = WLAN_EID_SSID;
+ *ie_pos++ = len;
+ if (hexstr2bin(pos, ie_pos, len) < 0) {
+ wpa_printf(MSG_DEBUG, "test_driver: invalid SSID in scanres");
+ os_free(res);
+ return;
+ }
+ ie_pos += len;
+
+ pos = pos2 + 1;
+ pos2 = os_strchr(pos, ' ');
+ if (pos2 == NULL)
+ len = os_strlen(pos) / 2;
+ else
+ len = (pos2 - pos) / 2;
+ if ((int) len > ie_end - ie_pos)
+ len = ie_end - ie_pos;
+ if (hexstr2bin(pos, ie_pos, len) < 0) {
+ wpa_printf(MSG_DEBUG, "test_driver: invalid IEs in scanres");
+ os_free(res);
+ return;
+ }
+ ie_pos += len;
+ res->ie_len = ie_pos - ie_start;
+
+ if (pos2) {
+ pos = pos2 + 1;
+ while (*pos == ' ')
+ pos++;
+ if (os_strstr(pos, "PRIVACY"))
+ res->caps |= IEEE80211_CAP_PRIVACY;
+ if (os_strstr(pos, "IBSS"))
+ res->caps |= IEEE80211_CAP_IBSS;
+ }
+
+ ds_params = wpa_scan_get_ie(res, WLAN_EID_DS_PARAMS);
+ if (ds_params && ds_params[1] > 0) {
+ if (ds_params[2] >= 1 && ds_params[2] <= 13)
+ res->freq = 2407 + ds_params[2] * 5;
+ }
+
+ os_free(drv->scanres[drv->num_scanres]);
+ drv->scanres[drv->num_scanres++] = res;
+}
+
+
+static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv,
+ struct sockaddr *from,
+ socklen_t fromlen,
+ const char *data)
+{
+ struct test_driver_bss *bss;
+
+ bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
+
+ /* ASSOCRESP BSSID <res> */
+ if (hwaddr_aton(data, bss->bssid)) {
+ wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in "
+ "assocresp");
+ }
+ if (drv->use_associnfo) {
+ union wpa_event_data event;
+ os_memset(&event, 0, sizeof(event));
+ event.assoc_info.req_ies = drv->assoc_wpa_ie;
+ event.assoc_info.req_ies_len = drv->assoc_wpa_ie_len;
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &event);
+ }
+ drv->associated = 1;
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+}
+
+
+static void wpa_driver_test_disassoc(struct wpa_driver_test_data *drv,
+ struct sockaddr *from,
+ socklen_t fromlen)
+{
+ drv->associated = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+}
+
+
+static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv,
+ struct sockaddr *from,
+ socklen_t fromlen,
+ const u8 *data, size_t data_len)
+{
+ const u8 *src;
+ struct test_driver_bss *bss;
+
+ bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
+
+ if (data_len > 14) {
+ /* Skip Ethernet header */
+ src = data + ETH_ALEN;
+ data += 14;
+ data_len -= 14;
+ } else
+ src = bss->bssid;
+
+ drv_event_eapol_rx(drv->ctx, src, data, data_len);
+}
+
+
+static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv,
+ struct sockaddr *from,
+ socklen_t fromlen,
+ const u8 *data, size_t data_len)
+{
+ int freq = 0, own_freq;
+ union wpa_event_data event;
+ 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.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,
+ 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, 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("bind(PF_UNIX)");
+ close(drv->test_socket);
+ unlink(drv->own_socket_path);
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ return -1;
+ }
+
+ eloop_register_read_sock(drv->test_socket,
+ wpa_driver_test_receive_unix, drv, NULL);
+
+ return 0;
+#else /* DRIVER_TEST_UNIX */
+ return -1;
+#endif /* DRIVER_TEST_UNIX */
+}
+
+
+static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv,
+ char *dst)
+{
+ char *pos;
+
+ pos = os_strchr(dst, ':');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ wpa_printf(MSG_DEBUG, "%s: addr=%s port=%s", __func__, dst, pos);
+
+ drv->test_socket = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->test_socket < 0) {
+ perror("socket(PF_INET)");
+ return -1;
+ }
+
+ os_memset(&drv->hostapd_addr_udp, 0, sizeof(drv->hostapd_addr_udp));
+ drv->hostapd_addr_udp.sin_family = AF_INET;
+#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA)
+ {
+ int a[4];
+ u8 *pos;
+ sscanf(dst, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]);
+ pos = (u8 *) &drv->hostapd_addr_udp.sin_addr;
+ *pos++ = a[0];
+ *pos++ = a[1];
+ *pos++ = a[2];
+ *pos++ = a[3];
+ }
+#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
+ inet_aton(dst, &drv->hostapd_addr_udp.sin_addr);
+#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
+ drv->hostapd_addr_udp.sin_port = htons(atoi(pos));
+
+ drv->hostapd_addr_udp_set = 1;
+
+ eloop_register_read_sock(drv->test_socket,
+ wpa_driver_test_receive_unix, drv, NULL);
+
+ return 0;
+}
+
+
+static int wpa_driver_test_set_param(void *priv, const char *param)
+{
+ struct test_driver_bss *dbss = priv;
+ struct wpa_driver_test_data *drv = dbss->drv;
+ const char *pos;
+
+ wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
+ if (param == NULL)
+ return 0;
+
+ wpa_driver_test_close_test_socket(drv);
+
+#ifdef DRIVER_TEST_UNIX
+ pos = os_strstr(param, "test_socket=");
+ if (pos) {
+ const char *pos2;
+ size_t len;
+
+ pos += 12;
+ pos2 = os_strchr(pos, ' ');
+ if (pos2)
+ len = pos2 - pos;
+ else
+ len = os_strlen(pos);
+ if (len > sizeof(drv->hostapd_addr.sun_path))
+ return -1;
+ os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr));
+ drv->hostapd_addr.sun_family = AF_UNIX;
+ os_memcpy(drv->hostapd_addr.sun_path, pos, len);
+ drv->hostapd_addr_set = 1;
+ }
+#endif /* DRIVER_TEST_UNIX */
+
+ pos = os_strstr(param, "test_dir=");
+ if (pos) {
+ char *end;
+ os_free(drv->test_dir);
+ drv->test_dir = os_strdup(pos + 9);
+ if (drv->test_dir == NULL)
+ return -1;
+ end = os_strchr(drv->test_dir, ' ');
+ if (end)
+ *end = '\0';
+ if (wpa_driver_test_attach(drv, drv->test_dir, 0))
+ return -1;
+ } else {
+ pos = os_strstr(param, "test_udp=");
+ if (pos) {
+ char *dst, *epos;
+ dst = os_strdup(pos + 9);
+ if (dst == NULL)
+ return -1;
+ epos = os_strchr(dst, ' ');
+ if (epos)
+ *epos = '\0';
+ if (wpa_driver_test_attach_udp(drv, dst))
+ return -1;
+ os_free(dst);
+ } else if (wpa_driver_test_attach(drv, NULL, 0))
+ return -1;
+ }
+
+ if (os_strstr(param, "use_associnfo=1")) {
+ wpa_printf(MSG_DEBUG, "test_driver: Use AssocInfo events");
+ drv->use_associnfo = 1;
+ }
+
+#ifdef CONFIG_CLIENT_MLME
+ if (os_strstr(param, "use_mlme=1")) {
+ wpa_printf(MSG_DEBUG, "test_driver: Use internal MLME");
+ drv->use_mlme = 1;
+ }
+#endif /* CONFIG_CLIENT_MLME */
+
+ 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(&eth, 0, sizeof(eth));
+ os_memcpy(eth.h_dest, dest, ETH_ALEN);
+ os_memcpy(eth.h_source, drv->own_addr, ETH_ALEN);
+ eth.h_proto = host_to_be16(proto);
+
+ msg_len = 6 + sizeof(eth) + data_len;
+ msg = os_malloc(msg_len);
+ if (msg == NULL)
+ return -1;
+ os_memcpy(msg, "EAPOL ", 6);
+ os_memcpy(msg + 6, &eth, sizeof(eth));
+ os_memcpy(msg + 6 + sizeof(eth), data, data_len);
+
+ if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 ||
+ drv->test_dir == NULL) {
+ if (drv->hostapd_addr_udp_set) {
+ addr = (struct sockaddr *) &drv->hostapd_addr_udp;
+ alen = sizeof(drv->hostapd_addr_udp);
+ } else {
+#ifdef DRIVER_TEST_UNIX
+ addr = (struct sockaddr *) &drv->hostapd_addr;
+ alen = sizeof(drv->hostapd_addr);
+#else /* DRIVER_TEST_UNIX */
+ os_free(msg);
+ return -1;
+#endif /* DRIVER_TEST_UNIX */
+ }
+ } else {
+#ifdef DRIVER_TEST_UNIX
+ struct stat st;
+ os_memset(&addr_un, 0, sizeof(addr_un));
+ addr_un.sun_family = AF_UNIX;
+ os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path),
+ "%s/STA-" MACSTR, drv->test_dir, MAC2STR(dest));
+ if (stat(addr_un.sun_path, &st) < 0) {
+ os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path),
+ "%s/AP-" MACSTR,
+ drv->test_dir, MAC2STR(dest));
+ }
+ addr = (struct sockaddr *) &addr_un;
+ alen = sizeof(addr_un);
+#else /* DRIVER_TEST_UNIX */
+ os_free(msg);
+ return -1;
+#endif /* DRIVER_TEST_UNIX */
+ }
+
+ if (sendto(drv->test_socket, msg, msg_len, 0, addr, alen) < 0) {
+ perror("sendmsg(test_socket)");
+ os_free(msg);
+ return -1;
+ }
+
+ os_free(msg);
+ return 0;
+}
+
+
+static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ struct test_driver_bss *dbss = priv;
+ struct wpa_driver_test_data *drv = dbss->drv;
+ os_memset(capa, 0, sizeof(*capa));
+ capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+ capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 |
+ WPA_DRIVER_CAPA_ENC_WEP104 |
+ WPA_DRIVER_CAPA_ENC_TKIP |
+ WPA_DRIVER_CAPA_ENC_CCMP;
+ capa->auth = WPA_DRIVER_AUTH_OPEN |
+ WPA_DRIVER_AUTH_SHARED |
+ WPA_DRIVER_AUTH_LEAP;
+ if (drv->use_mlme)
+ capa->flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME;
+ 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 int wpa_driver_test_set_channel(void *priv,
+ enum hostapd_hw_mode phymode,
+ int chan, int freq)
+{
+ struct test_driver_bss *dbss = priv;
+ struct wpa_driver_test_data *drv = dbss->drv;
+ wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d",
+ __func__, phymode, chan, freq);
+ drv->current_freq = freq;
+ return 0;
+}
+
+
+static int wpa_driver_test_mlme_add_sta(void *priv, const u8 *addr,
+ const u8 *supp_rates,
+ size_t supp_rates_len)
+{
+ wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr));
+ return 0;
+}
+
+
+static int wpa_driver_test_mlme_remove_sta(void *priv, const u8 *addr)
+{
+ wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr));
+ return 0;
+}
+
+
+static int wpa_driver_test_set_ssid(void *priv, const u8 *ssid,
+ size_t ssid_len)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ return 0;
+}
+
+
+static int wpa_driver_test_set_bssid(void *priv, const u8 *bssid)
+{
+ wpa_printf(MSG_DEBUG, "%s: bssid=" MACSTR, __func__, MAC2STR(bssid));
+ return 0;
+}
+
+
+static void * wpa_driver_test_global_init(void)
+{
+ struct wpa_driver_test_global *global;
+
+ global = os_zalloc(sizeof(*global));
+ return global;
+}
+
+
+static void wpa_driver_test_global_deinit(void *priv)
+{
+ struct wpa_driver_test_global *global = priv;
+ os_free(global);
+}
+
+
+static struct wpa_interface_info *
+wpa_driver_test_get_interfaces(void *global_priv)
+{
+ /* struct wpa_driver_test_global *global = priv; */
+ struct wpa_interface_info *iface;
+
+ iface = os_zalloc(sizeof(*iface));
+ if (iface == NULL)
+ return iface;
+ iface->ifname = os_strdup("sta0");
+ iface->desc = os_strdup("test interface 0");
+ iface->drv_name = "test";
+ iface->next = os_zalloc(sizeof(*iface));
+ if (iface->next) {
+ iface->next->ifname = os_strdup("sta1");
+ iface->next->desc = os_strdup("test interface 1");
+ iface->next->drv_name = "test";
+ }
+
+ return iface;
+}
+
+
+static struct hostapd_hw_modes *
+wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+{
+ struct hostapd_hw_modes *modes;
+ size_t i;
+
+ *num_modes = 3;
+ *flags = 0;
+ modes = os_zalloc(*num_modes * sizeof(struct hostapd_hw_modes));
+ if (modes == NULL)
+ return NULL;
+ modes[0].mode = HOSTAPD_MODE_IEEE80211G;
+ modes[0].num_channels = 11;
+ modes[0].num_rates = 12;
+ modes[0].channels =
+ os_zalloc(11 * sizeof(struct hostapd_channel_data));
+ modes[0].rates = os_zalloc(modes[0].num_rates * sizeof(int));
+ if (modes[0].channels == NULL || modes[0].rates == NULL)
+ goto fail;
+ for (i = 0; i < 11; i++) {
+ modes[0].channels[i].chan = i + 1;
+ modes[0].channels[i].freq = 2412 + 5 * i;
+ modes[0].channels[i].flag = 0;
+ }
+ modes[0].rates[0] = 10;
+ modes[0].rates[1] = 20;
+ modes[0].rates[2] = 55;
+ modes[0].rates[3] = 110;
+ modes[0].rates[4] = 60;
+ modes[0].rates[5] = 90;
+ modes[0].rates[6] = 120;
+ modes[0].rates[7] = 180;
+ modes[0].rates[8] = 240;
+ modes[0].rates[9] = 360;
+ modes[0].rates[10] = 480;
+ modes[0].rates[11] = 540;
+
+ modes[1].mode = HOSTAPD_MODE_IEEE80211B;
+ modes[1].num_channels = 11;
+ modes[1].num_rates = 4;
+ modes[1].channels =
+ os_zalloc(11 * sizeof(struct hostapd_channel_data));
+ modes[1].rates = os_zalloc(modes[1].num_rates * sizeof(int));
+ if (modes[1].channels == NULL || modes[1].rates == NULL)
+ goto fail;
+ for (i = 0; i < 11; i++) {
+ modes[1].channels[i].chan = i + 1;
+ modes[1].channels[i].freq = 2412 + 5 * i;
+ modes[1].channels[i].flag = 0;
+ }
+ modes[1].rates[0] = 10;
+ modes[1].rates[1] = 20;
+ modes[1].rates[2] = 55;
+ modes[1].rates[3] = 110;
+
+ modes[2].mode = HOSTAPD_MODE_IEEE80211A;
+ modes[2].num_channels = 1;
+ modes[2].num_rates = 8;
+ modes[2].channels = os_zalloc(sizeof(struct hostapd_channel_data));
+ modes[2].rates = os_zalloc(modes[2].num_rates * sizeof(int));
+ if (modes[2].channels == NULL || modes[2].rates == NULL)
+ goto fail;
+ modes[2].channels[0].chan = 60;
+ modes[2].channels[0].freq = 5300;
+ modes[2].channels[0].flag = 0;
+ modes[2].rates[0] = 60;
+ modes[2].rates[1] = 90;
+ modes[2].rates[2] = 120;
+ modes[2].rates[3] = 180;
+ modes[2].rates[4] = 240;
+ modes[2].rates[5] = 360;
+ modes[2].rates[6] = 480;
+ modes[2].rates[7] = 540;
+
+ return modes;
+
+fail:
+ if (modes) {
+ for (i = 0; i < *num_modes; i++) {
+ os_free(modes[i].channels);
+ os_free(modes[i].rates);
+ }
+ os_free(modes);
+ }
+ return NULL;
+}
+
+
+static int wpa_driver_test_set_freq(void *priv,
+ struct hostapd_freq_params *freq)
+{
+ struct test_driver_bss *dbss = priv;
+ struct wpa_driver_test_data *drv = dbss->drv;
+ wpa_printf(MSG_DEBUG, "test: set_freq %u MHz", freq->freq);
+ drv->current_freq = freq->freq;
+ return 0;
+}
+
+
+static int wpa_driver_test_send_action(void *priv, unsigned int freq,
+ unsigned int wait,
+ const u8 *dst, const u8 *src,
+ const u8 *bssid,
+ const u8 *data, size_t data_len)
+{
+ struct test_driver_bss *dbss = priv;
+ struct wpa_driver_test_data *drv = dbss->drv;
+ int ret = -1;
+ u8 *buf;
+ struct ieee80211_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "test: Send Action frame");
+
+ if ((drv->remain_on_channel_freq &&
+ freq != drv->remain_on_channel_freq) ||
+ (drv->remain_on_channel_freq == 0 &&
+ freq != (unsigned int) drv->current_freq)) {
+ wpa_printf(MSG_DEBUG, "test: Reject Action frame TX on "
+ "unexpected channel: freq=%u MHz (current_freq=%u "
+ "MHz, remain-on-channel freq=%u MHz)",
+ freq, drv->current_freq,
+ drv->remain_on_channel_freq);
+ return -1;
+ }
+
+ buf = os_zalloc(24 + data_len);
+ if (buf == NULL)
+ return ret;
+ os_memcpy(buf + 24, data, data_len);
+ hdr = (struct ieee80211_hdr *) buf;
+ hdr->frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
+ os_memcpy(hdr->addr1, dst, ETH_ALEN);
+ os_memcpy(hdr->addr2, src, ETH_ALEN);
+ os_memcpy(hdr->addr3, bssid, ETH_ALEN);
+
+ ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len);
+ os_free(buf);
+ return ret;
+}
+
+
+#ifdef CONFIG_P2P
+static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_test_data *drv = eloop_ctx;
+ int res;
+
+ 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));
+ res = 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));
+}
+#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 wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
+ if (!drv->p2p)
+ return -1;
+ return p2p_find(drv->p2p, timeout, type, 0, NULL);
+}
+
+
+static int wpa_driver_test_p2p_stop_find(void *priv)
+{
+ struct wpa_driver_test_data *drv = priv;
+ 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 wpa_driver_test_data *drv = priv;
+ 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 wpa_driver_test_data *drv = priv;
+ 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);
+}
+
+
+static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr)
+{
+ struct wpa_driver_test_data *drv = priv;
+ 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 wpa_driver_test_data *drv = priv;
+ 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 wpa_driver_test_data *drv = priv;
+ 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)
+{
+ 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 };
+
+ wpa_printf(MSG_DEBUG, "%s(type=%d freq=%d)",
+ __func__, type, freq);
+
+ os_memset(&params, 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(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
+ WPS_REQ_ENROLLEE);
+#else
+ wps_ie = wpabuf_alloc(1);
+#endif
+ if (wps_ie == NULL)
+ return -1;
+
+ ies = wpabuf_alloc(wpabuf_len(wps_ie) + 100);
+ 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);
+
+ 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_SPECIFIC:
+ social_channels[0] = freq;
+ social_channels[1] = 0;
+ params.freqs = social_channels;
+ 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, &params);
+
+ 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;
+
+ 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)
+{
+ 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 */
+}
+
+#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.msg_ctx = drv->ctx;
+ p2p.cb_ctx = drv;
+ 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,
+ .valid_bss_mask = test_driver_valid_bss_mask,
+ .hapd_set_ssid = test_driver_set_ssid,
+ .set_privacy = test_driver_set_privacy,
+ .set_sta_vlan = test_driver_set_sta_vlan,
+ .sta_add = test_driver_sta_add,
+ .send_ether = test_driver_send_ether,
+ .set_ap_wps_ie = test_driver_set_ap_wps_ie,
+ .get_bssid = wpa_driver_test_get_bssid,
+ .get_ssid = wpa_driver_test_get_ssid,
+ .set_key = wpa_driver_test_set_key,
+ .deinit = wpa_driver_test_deinit,
+ .set_param = wpa_driver_test_set_param,
+ .deauthenticate = wpa_driver_test_deauthenticate,
+ .disassociate = wpa_driver_test_disassociate,
+ .associate = wpa_driver_test_associate,
+ .get_capa = wpa_driver_test_get_capa,
+ .get_mac_addr = wpa_driver_test_get_mac_addr,
+ .send_eapol = wpa_driver_test_send_eapol,
+ .mlme_setprotection = wpa_driver_test_mlme_setprotection,
+ .set_channel = wpa_driver_test_set_channel,
+ .set_ssid = wpa_driver_test_set_ssid,
+ .set_bssid = wpa_driver_test_set_bssid,
+ .mlme_add_sta = wpa_driver_test_mlme_add_sta,
+ .mlme_remove_sta = wpa_driver_test_mlme_remove_sta,
+ .get_scan_results2 = wpa_driver_test_get_scan_results2,
+ .global_init = wpa_driver_test_global_init,
+ .global_deinit = wpa_driver_test_global_deinit,
+ .init2 = wpa_driver_test_init2,
+ .get_interfaces = wpa_driver_test_get_interfaces,
+ .scan2 = wpa_driver_test_scan,
+ .set_freq = wpa_driver_test_set_freq,
+ .send_action = wpa_driver_test_send_action,
+ .remain_on_channel = wpa_driver_test_remain_on_channel,
+ .cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel,
+ .probe_req_report = wpa_driver_test_probe_req_report,
+#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/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
new file mode 100644
index 0000000..e731101
--- /dev/null
+++ b/src/drivers/driver_wext.c
@@ -0,0 +1,2320 @@
+/*
+ * Driver interaction with generic Linux Wireless Extensions
+ * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file implements a driver interface for the Linux Wireless Extensions.
+ * When used with WE-18 or newer, this interface can be used as-is with number
+ * of drivers. In addition to this, some of the common functions in this file
+ * can be used by other driver interface implementations that use generic WE
+ * ioctls, but require private ioctls for some of the functionality.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+
+#include "wireless_copy.h"
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+#include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "rfkill.h"
+#include "driver.h"
+#include "driver_wext.h"
+
+
+static int wpa_driver_wext_flush_pmkid(void *priv);
+static int wpa_driver_wext_get_range(void *priv);
+static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv);
+static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv);
+static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg);
+
+
+int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
+ int idx, u32 value)
+{
+ struct iwreq iwr;
+ int ret = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.param.flags = idx & IW_AUTH_INDEX;
+ iwr.u.param.value = value;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) {
+ if (errno != EOPNOTSUPP) {
+ wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d "
+ "value 0x%x) failed: %s)",
+ idx, value, strerror(errno));
+ }
+ ret = errno == EOPNOTSUPP ? -2 : -1;
+ }
+
+ return ret;
+}
+
+
+/**
+ * wpa_driver_wext_get_bssid - Get BSSID, SIOCGIWAP
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @bssid: Buffer for BSSID
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+ if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) {
+ perror("ioctl[SIOCGIWAP]");
+ ret = -1;
+ }
+ os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN);
+
+ return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_bssid - Set BSSID, SIOCSIWAP
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @bssid: BSSID
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.ap_addr.sa_family = ARPHRD_ETHER;
+ if (bssid)
+ os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN);
+ else
+ os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN);
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) {
+ perror("ioctl[SIOCSIWAP]");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+/**
+ * wpa_driver_wext_get_ssid - Get SSID, SIOCGIWESSID
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @ssid: Buffer for the SSID; must be at least 32 bytes long
+ * Returns: SSID length on success, -1 on failure
+ */
+int wpa_driver_wext_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.essid.pointer = (caddr_t) ssid;
+ iwr.u.essid.length = 32;
+
+ if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+ perror("ioctl[SIOCGIWESSID]");
+ ret = -1;
+ } else {
+ ret = iwr.u.essid.length;
+ if (ret > 32)
+ ret = 32;
+ /* Some drivers include nul termination in the SSID, so let's
+ * remove it here before further processing. WE-21 changes this
+ * to explicitly require the length _not_ to include nul
+ * termination. */
+ if (ret > 0 && ssid[ret - 1] == '\0' &&
+ drv->we_version_compiled < 21)
+ ret--;
+ }
+
+ return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_ssid - Set SSID, SIOCSIWESSID
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @ssid: SSID
+ * @ssid_len: Length of SSID (0..32)
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+ char buf[33];
+
+ if (ssid_len > 32)
+ return -1;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ /* flags: 1 = ESSID is active, 0 = not (promiscuous) */
+ iwr.u.essid.flags = (ssid_len != 0);
+ os_memset(buf, 0, sizeof(buf));
+ os_memcpy(buf, ssid, ssid_len);
+ iwr.u.essid.pointer = (caddr_t) buf;
+ if (drv->we_version_compiled < 21) {
+ /* For historic reasons, set SSID length to include one extra
+ * character, C string nul termination, even though SSID is
+ * really an octet string that should not be presented as a C
+ * string. Some Linux drivers decrement the length by one and
+ * can thus end up missing the last octet of the SSID if the
+ * length is not incremented here. WE-21 changes this to
+ * explicitly require the length _not_ to include nul
+ * termination. */
+ if (ssid_len)
+ ssid_len++;
+ }
+ iwr.u.essid.length = ssid_len;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+ perror("ioctl[SIOCSIWESSID]");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_freq - Set frequency/channel, SIOCSIWFREQ
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @freq: Frequency in MHz
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_freq(void *priv, int freq)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.freq.m = freq * 100000;
+ iwr.u.freq.e = 1;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
+ perror("ioctl[SIOCSIWFREQ]");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+static void
+wpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
+{
+ union wpa_event_data data;
+
+ wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'",
+ custom);
+
+ os_memset(&data, 0, sizeof(data));
+ /* Host AP driver */
+ if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+ data.michael_mic_failure.unicast =
+ os_strstr(custom, " unicast ") != NULL;
+ /* TODO: parse parameters(?) */
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+ } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) {
+ char *spos;
+ int bytes;
+ u8 *req_ies = NULL, *resp_ies = NULL;
+
+ spos = custom + 17;
+
+ bytes = strspn(spos, "0123456789abcdefABCDEF");
+ if (!bytes || (bytes & 1))
+ return;
+ bytes /= 2;
+
+ req_ies = os_malloc(bytes);
+ if (req_ies == NULL ||
+ hexstr2bin(spos, req_ies, bytes) < 0)
+ goto done;
+ data.assoc_info.req_ies = req_ies;
+ data.assoc_info.req_ies_len = bytes;
+
+ spos += bytes * 2;
+
+ data.assoc_info.resp_ies = NULL;
+ data.assoc_info.resp_ies_len = 0;
+
+ if (os_strncmp(spos, " RespIEs=", 9) == 0) {
+ spos += 9;
+
+ bytes = strspn(spos, "0123456789abcdefABCDEF");
+ if (!bytes || (bytes & 1))
+ goto done;
+ bytes /= 2;
+
+ resp_ies = os_malloc(bytes);
+ if (resp_ies == NULL ||
+ hexstr2bin(spos, resp_ies, bytes) < 0)
+ goto done;
+ data.assoc_info.resp_ies = resp_ies;
+ data.assoc_info.resp_ies_len = bytes;
+ }
+
+ wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
+
+ done:
+ os_free(resp_ies);
+ os_free(req_ies);
+#ifdef CONFIG_PEERKEY
+ } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) {
+ if (hwaddr_aton(custom + 17, data.stkstart.peer)) {
+ wpa_printf(MSG_DEBUG, "WEXT: unrecognized "
+ "STKSTART.request '%s'", custom + 17);
+ return;
+ }
+ wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
+#endif /* CONFIG_PEERKEY */
+ }
+}
+
+
+static int wpa_driver_wext_event_wireless_michaelmicfailure(
+ void *ctx, const char *ev, size_t len)
+{
+ const struct iw_michaelmicfailure *mic;
+ union wpa_event_data data;
+
+ if (len < sizeof(*mic))
+ return -1;
+
+ mic = (const struct iw_michaelmicfailure *) ev;
+
+ wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: "
+ "flags=0x%x src_addr=" MACSTR, mic->flags,
+ MAC2STR(mic->src_addr.sa_data));
+
+ os_memset(&data, 0, sizeof(data));
+ data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP);
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+
+ return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_pmkidcand(
+ struct wpa_driver_wext_data *drv, const char *ev, size_t len)
+{
+ const struct iw_pmkid_cand *cand;
+ union wpa_event_data data;
+ const u8 *addr;
+
+ if (len < sizeof(*cand))
+ return -1;
+
+ cand = (const struct iw_pmkid_cand *) ev;
+ addr = (const u8 *) cand->bssid.sa_data;
+
+ wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: "
+ "flags=0x%x index=%d bssid=" MACSTR, cand->flags,
+ cand->index, MAC2STR(addr));
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN);
+ data.pmkid_candidate.index = cand->index;
+ data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH;
+ wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
+
+ return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_assocreqie(
+ struct wpa_driver_wext_data *drv, const char *ev, int len)
+{
+ if (len < 0)
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev,
+ len);
+ os_free(drv->assoc_req_ies);
+ drv->assoc_req_ies = os_malloc(len);
+ if (drv->assoc_req_ies == NULL) {
+ drv->assoc_req_ies_len = 0;
+ return -1;
+ }
+ os_memcpy(drv->assoc_req_ies, ev, len);
+ drv->assoc_req_ies_len = len;
+
+ return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_assocrespie(
+ struct wpa_driver_wext_data *drv, const char *ev, int len)
+{
+ if (len < 0)
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev,
+ len);
+ os_free(drv->assoc_resp_ies);
+ drv->assoc_resp_ies = os_malloc(len);
+ if (drv->assoc_resp_ies == NULL) {
+ drv->assoc_resp_ies_len = 0;
+ return -1;
+ }
+ os_memcpy(drv->assoc_resp_ies, ev, len);
+ drv->assoc_resp_ies_len = len;
+
+ return 0;
+}
+
+
+static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv)
+{
+ union wpa_event_data data;
+
+ if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ if (drv->assoc_req_ies) {
+ data.assoc_info.req_ies = drv->assoc_req_ies;
+ data.assoc_info.req_ies_len = drv->assoc_req_ies_len;
+ }
+ if (drv->assoc_resp_ies) {
+ data.assoc_info.resp_ies = drv->assoc_resp_ies;
+ data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len;
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
+
+ os_free(drv->assoc_req_ies);
+ drv->assoc_req_ies = NULL;
+ os_free(drv->assoc_resp_ies);
+ drv->assoc_resp_ies = NULL;
+}
+
+
+static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
+ char *data, int len)
+{
+ struct iw_event iwe_buf, *iwe = &iwe_buf;
+ char *pos, *end, *custom, *buf;
+
+ pos = data;
+ end = data + len;
+
+ while (pos + IW_EV_LCP_LEN <= end) {
+ /* Event data may be unaligned, so make a local, aligned copy
+ * before processing. */
+ os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+ wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
+ iwe->cmd, iwe->len);
+ if (iwe->len <= IW_EV_LCP_LEN)
+ return;
+
+ custom = pos + IW_EV_POINT_LEN;
+ if (drv->we_version_compiled > 18 &&
+ (iwe->cmd == IWEVMICHAELMICFAILURE ||
+ iwe->cmd == IWEVCUSTOM ||
+ iwe->cmd == IWEVASSOCREQIE ||
+ iwe->cmd == IWEVASSOCRESPIE ||
+ iwe->cmd == IWEVPMKIDCAND)) {
+ /* WE-19 removed the pointer from struct iw_point */
+ char *dpos = (char *) &iwe_buf.u.data.length;
+ int dlen = dpos - (char *) &iwe_buf;
+ os_memcpy(dpos, pos + IW_EV_LCP_LEN,
+ sizeof(struct iw_event) - dlen);
+ } else {
+ os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+ custom += IW_EV_POINT_OFF;
+ }
+
+ switch (iwe->cmd) {
+ case SIOCGIWAP:
+ wpa_printf(MSG_DEBUG, "Wireless event: new AP: "
+ MACSTR,
+ MAC2STR((u8 *) iwe->u.ap_addr.sa_data));
+ if (is_zero_ether_addr(
+ (const u8 *) iwe->u.ap_addr.sa_data) ||
+ os_memcmp(iwe->u.ap_addr.sa_data,
+ "\x44\x44\x44\x44\x44\x44", ETH_ALEN) ==
+ 0) {
+ os_free(drv->assoc_req_ies);
+ drv->assoc_req_ies = NULL;
+ os_free(drv->assoc_resp_ies);
+ drv->assoc_resp_ies = NULL;
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC,
+ NULL);
+
+ } else {
+ wpa_driver_wext_event_assoc_ies(drv);
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC,
+ NULL);
+ }
+ break;
+ case IWEVMICHAELMICFAILURE:
+ if (custom + iwe->u.data.length > end) {
+ wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+ "IWEVMICHAELMICFAILURE length");
+ return;
+ }
+ wpa_driver_wext_event_wireless_michaelmicfailure(
+ drv->ctx, custom, iwe->u.data.length);
+ break;
+ case IWEVCUSTOM:
+ if (custom + iwe->u.data.length > end) {
+ wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+ "IWEVCUSTOM length");
+ return;
+ }
+ buf = os_malloc(iwe->u.data.length + 1);
+ if (buf == NULL)
+ return;
+ os_memcpy(buf, custom, iwe->u.data.length);
+ buf[iwe->u.data.length] = '\0';
+ wpa_driver_wext_event_wireless_custom(drv->ctx, buf);
+ os_free(buf);
+ break;
+ case SIOCGIWSCAN:
+ drv->scan_complete_events = 1;
+ eloop_cancel_timeout(wpa_driver_wext_scan_timeout,
+ drv, drv->ctx);
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
+ NULL);
+ break;
+ case IWEVASSOCREQIE:
+ if (custom + iwe->u.data.length > end) {
+ wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+ "IWEVASSOCREQIE length");
+ return;
+ }
+ wpa_driver_wext_event_wireless_assocreqie(
+ drv, custom, iwe->u.data.length);
+ break;
+ case IWEVASSOCRESPIE:
+ if (custom + iwe->u.data.length > end) {
+ wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+ "IWEVASSOCRESPIE length");
+ return;
+ }
+ wpa_driver_wext_event_wireless_assocrespie(
+ drv, custom, iwe->u.data.length);
+ break;
+ case IWEVPMKIDCAND:
+ if (custom + iwe->u.data.length > end) {
+ wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+ "IWEVPMKIDCAND length");
+ return;
+ }
+ wpa_driver_wext_event_wireless_pmkidcand(
+ drv, custom, iwe->u.data.length);
+ break;
+ }
+
+ pos += iwe->len;
+ }
+}
+
+
+static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
+ char *buf, size_t len, int del)
+{
+ union wpa_event_data event;
+
+ os_memset(&event, 0, sizeof(event));
+ if (len > sizeof(event.interface_status.ifname))
+ len = sizeof(event.interface_status.ifname) - 1;
+ os_memcpy(event.interface_status.ifname, buf, len);
+ event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED :
+ EVENT_INTERFACE_ADDED;
+
+ wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s",
+ del ? "DEL" : "NEW",
+ event.interface_status.ifname,
+ del ? "removed" : "added");
+
+ if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) {
+ if (del)
+ drv->if_removed = 1;
+ else
+ drv->if_removed = 0;
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv,
+ u8 *buf, size_t len)
+{
+ int attrlen, rta_len;
+ struct rtattr *attr;
+
+ attrlen = len;
+ attr = (struct rtattr *) buf;
+
+ rta_len = RTA_ALIGN(sizeof(struct rtattr));
+ while (RTA_OK(attr, attrlen)) {
+ if (attr->rta_type == IFLA_IFNAME) {
+ if (os_strcmp(((char *) attr) + rta_len, drv->ifname)
+ == 0)
+ return 1;
+ else
+ break;
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv,
+ int ifindex, u8 *buf, size_t len)
+{
+ if (drv->ifindex == ifindex || drv->ifindex2 == ifindex)
+ return 1;
+
+ if (drv->if_removed && wpa_driver_wext_own_ifname(drv, buf, len)) {
+ drv->ifindex = if_nametoindex(drv->ifname);
+ wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed "
+ "interface");
+ wpa_driver_wext_finish_drv_init(drv);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
+{
+ struct wpa_driver_wext_data *drv = ctx;
+ int attrlen, rta_len;
+ struct rtattr *attr;
+
+ if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) {
+ wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
+ ifi->ifi_index);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x "
+ "(%s%s%s%s)",
+ drv->operstate, ifi->ifi_flags,
+ (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+ (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+ (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+ (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+ 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)) {
+ 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;
+
+ 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 = 5;
+ if (drv->scan_complete_events) {
+ /*
+ * The driver seems to deliver SIOCGIWSCAN events to notify
+ * when scan is complete, so use longer timeout to avoid race
+ * conditions with scanning and following association request.
+ */
+ timeout = 30;
+ }
+ wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
+ "seconds", ret, timeout);
+ eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv,
+ drv->ctx);
+
+ return ret;
+}
+
+
+static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv,
+ size_t *len)
+{
+ struct iwreq iwr;
+ u8 *res_buf;
+ size_t res_buf_len;
+
+ res_buf_len = IW_SCAN_MAX_DATA;
+ for (;;) {
+ res_buf = os_malloc(res_buf_len);
+ if (res_buf == NULL)
+ return NULL;
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.pointer = res_buf;
+ iwr.u.data.length = res_buf_len;
+
+ if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0)
+ break;
+
+ if (errno == E2BIG && res_buf_len < 65535) {
+ os_free(res_buf);
+ res_buf = NULL;
+ res_buf_len *= 2;
+ if (res_buf_len > 65535)
+ res_buf_len = 65535; /* 16-bit length field */
+ wpa_printf(MSG_DEBUG, "Scan results did not fit - "
+ "trying larger buffer (%lu bytes)",
+ (unsigned long) res_buf_len);
+ } else {
+ perror("ioctl[SIOCGIWSCAN]");
+ os_free(res_buf);
+ return NULL;
+ }
+ }
+
+ if (iwr.u.data.length > res_buf_len) {
+ os_free(res_buf);
+ return NULL;
+ }
+ *len = iwr.u.data.length;
+
+ return res_buf;
+}
+
+
+/*
+ * Data structure for collecting WEXT scan results. This is needed to allow
+ * the various methods of reporting IEs to be combined into a single IE buffer.
+ */
+struct wext_scan_data {
+ struct wpa_scan_res res;
+ u8 *ie;
+ size_t ie_len;
+ u8 ssid[32];
+ size_t ssid_len;
+ int maxrate;
+};
+
+
+static void wext_get_scan_mode(struct iw_event *iwe,
+ struct wext_scan_data *res)
+{
+ if (iwe->u.mode == IW_MODE_ADHOC)
+ res->res.caps |= IEEE80211_CAP_IBSS;
+ else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA)
+ res->res.caps |= IEEE80211_CAP_ESS;
+}
+
+
+static void wext_get_scan_ssid(struct iw_event *iwe,
+ struct wext_scan_data *res, char *custom,
+ char *end)
+{
+ int ssid_len = iwe->u.essid.length;
+ if (custom + ssid_len > end)
+ return;
+ if (iwe->u.essid.flags &&
+ ssid_len > 0 &&
+ ssid_len <= IW_ESSID_MAX_SIZE) {
+ os_memcpy(res->ssid, custom, ssid_len);
+ res->ssid_len = ssid_len;
+ }
+}
+
+
+static void wext_get_scan_freq(struct iw_event *iwe,
+ struct wext_scan_data *res)
+{
+ int divi = 1000000, i;
+
+ if (iwe->u.freq.e == 0) {
+ /*
+ * Some drivers do not report frequency, but a channel.
+ * Try to map this to frequency by assuming they are using
+ * IEEE 802.11b/g. But don't overwrite a previously parsed
+ * frequency if the driver sends both frequency and channel,
+ * since the driver may be sending an A-band channel that we
+ * don't handle here.
+ */
+
+ if (res->res.freq)
+ return;
+
+ if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) {
+ res->res.freq = 2407 + 5 * iwe->u.freq.m;
+ return;
+ } else if (iwe->u.freq.m == 14) {
+ res->res.freq = 2484;
+ return;
+ }
+ }
+
+ if (iwe->u.freq.e > 6) {
+ wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID="
+ MACSTR " m=%d e=%d)",
+ MAC2STR(res->res.bssid), iwe->u.freq.m,
+ iwe->u.freq.e);
+ return;
+ }
+
+ for (i = 0; i < iwe->u.freq.e; i++)
+ divi /= 10;
+ res->res.freq = iwe->u.freq.m / divi;
+}
+
+
+static void wext_get_scan_qual(struct 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(res->res,
+ (res->num + 1) * sizeof(struct wpa_scan_res *));
+ if (tmp == NULL) {
+ os_free(r);
+ return;
+ }
+ tmp[res->num++] = r;
+ res->res = tmp;
+}
+
+
+/**
+ * wpa_driver_wext_get_scan_results - Fetch the latest scan results
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * Returns: Scan results on success, -1 on failure
+ */
+struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ size_t ap_num = 0, len;
+ int first;
+ u8 *res_buf;
+ struct iw_event iwe_buf, *iwe = &iwe_buf;
+ char *pos, *end, *custom;
+ struct wpa_scan_results *res;
+ struct wext_scan_data data;
+
+ res_buf = wpa_driver_wext_giwscan(drv, &len);
+ if (res_buf == NULL)
+ return NULL;
+
+ ap_num = 0;
+ first = 1;
+
+ res = os_zalloc(sizeof(*res));
+ if (res == NULL) {
+ os_free(res_buf);
+ return NULL;
+ }
+
+ pos = (char *) res_buf;
+ end = (char *) res_buf + len;
+ os_memset(&data, 0, sizeof(data));
+
+ while (pos + IW_EV_LCP_LEN <= end) {
+ /* Event data may be unaligned, so make a local, aligned copy
+ * before processing. */
+ os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+ if (iwe->len <= IW_EV_LCP_LEN)
+ break;
+
+ custom = pos + IW_EV_POINT_LEN;
+ if (wext_19_iw_point(drv, iwe->cmd)) {
+ /* WE-19 removed the pointer from struct iw_point */
+ char *dpos = (char *) &iwe_buf.u.data.length;
+ int dlen = dpos - (char *) &iwe_buf;
+ os_memcpy(dpos, pos + IW_EV_LCP_LEN,
+ sizeof(struct iw_event) - dlen);
+ } else {
+ os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+ custom += IW_EV_POINT_OFF;
+ }
+
+ switch (iwe->cmd) {
+ case SIOCGIWAP:
+ if (!first)
+ wpa_driver_wext_add_scan_entry(res, &data);
+ first = 0;
+ os_free(data.ie);
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.res.bssid,
+ iwe->u.ap_addr.sa_data, ETH_ALEN);
+ break;
+ case SIOCGIWMODE:
+ wext_get_scan_mode(iwe, &data);
+ break;
+ case SIOCGIWESSID:
+ wext_get_scan_ssid(iwe, &data, custom, end);
+ break;
+ case SIOCGIWFREQ:
+ wext_get_scan_freq(iwe, &data);
+ break;
+ case IWEVQUAL:
+ wext_get_scan_qual(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;
+ 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 };
+ u8 ssid[32];
+ int i;
+
+ /*
+ * Only force-disconnect when the card is in infrastructure mode,
+ * otherwise the driver might interpret the cleared BSSID and random
+ * SSID as an attempt to create a new ad-hoc network.
+ */
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
+ perror("ioctl[SIOCGIWMODE]");
+ iwr.u.mode = IW_MODE_INFRA;
+ }
+
+ if (iwr.u.mode == IW_MODE_INFRA) {
+ if (drv->cfg80211) {
+ /*
+ * cfg80211 supports SIOCSIWMLME commands, so there is
+ * no need for the random SSID hack, but clear the
+ * BSSID and SSID.
+ */
+ if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 ||
+ wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Failed to clear "
+ "to disconnect");
+ }
+ return;
+ }
+ /*
+ * Clear the BSSID selection and set a random SSID to make sure
+ * the driver will not be trying to associate with something
+ * even if it does not understand SIOCSIWMLME commands (or
+ * tries to associate automatically after deauth/disassoc).
+ */
+ for (i = 0; i < 32; i++)
+ ssid[i] = rand() & 0xFF;
+ if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 ||
+ wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
+ "BSSID/SSID to disconnect");
+ }
+ }
+}
+
+
+static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ int ret;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code);
+ wpa_driver_wext_disconnect(drv);
+ return ret;
+}
+
+
+static int wpa_driver_wext_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ int ret;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC, reason_code);
+ wpa_driver_wext_disconnect(drv);
+ return ret;
+}
+
+
+static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie,
+ size_t ie_len)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.pointer = (caddr_t) ie;
+ iwr.u.data.length = ie_len;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) {
+ perror("ioctl[SIOCSIWGENIE]");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+int wpa_driver_wext_cipher2wext(int cipher)
+{
+ switch (cipher) {
+ case CIPHER_NONE:
+ return IW_AUTH_CIPHER_NONE;
+ case CIPHER_WEP40:
+ return IW_AUTH_CIPHER_WEP40;
+ case CIPHER_TKIP:
+ return IW_AUTH_CIPHER_TKIP;
+ case CIPHER_CCMP:
+ return IW_AUTH_CIPHER_CCMP;
+ case CIPHER_WEP104:
+ return IW_AUTH_CIPHER_WEP104;
+ default:
+ return 0;
+ }
+}
+
+
+int wpa_driver_wext_keymgmt2wext(int keymgmt)
+{
+ switch (keymgmt) {
+ case KEY_MGMT_802_1X:
+ case KEY_MGMT_802_1X_NO_WPA:
+ return IW_AUTH_KEY_MGMT_802_1X;
+ case KEY_MGMT_PSK:
+ return IW_AUTH_KEY_MGMT_PSK;
+ default:
+ return 0;
+ }
+}
+
+
+static int
+wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv,
+ struct wpa_driver_associate_params *params)
+{
+ struct iwreq iwr;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "WEXT: Driver did not support "
+ "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE");
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ /* Just changing mode, not actual keys */
+ iwr.u.encoding.flags = 0;
+ iwr.u.encoding.pointer = (caddr_t) NULL;
+ iwr.u.encoding.length = 0;
+
+ /*
+ * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two
+ * different things. Here they are used to indicate Open System vs.
+ * Shared Key authentication algorithm. However, some drivers may use
+ * them to select between open/restricted WEP encrypted (open = allow
+ * both unencrypted and encrypted frames; restricted = only allow
+ * encrypted frames).
+ */
+
+ if (!drv->use_crypt) {
+ iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+ } else {
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+ iwr.u.encoding.flags |= IW_ENCODE_OPEN;
+ if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+ iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED;
+ }
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+ perror("ioctl[SIOCSIWENCODE]");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+int wpa_driver_wext_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ int ret = 0;
+ int allow_unencrypted_eapol;
+ int value;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (drv->cfg80211) {
+ /*
+ * Stop cfg80211 from trying to associate before we are done
+ * with all parameters.
+ */
+ wpa_driver_wext_set_ssid(drv, (u8 *) "", 0);
+ }
+
+ if (wpa_driver_wext_set_drop_unencrypted(drv, params->drop_unencrypted)
+ < 0)
+ ret = -1;
+ if (wpa_driver_wext_set_auth_alg(drv, params->auth_alg) < 0)
+ ret = -1;
+ if (wpa_driver_wext_set_mode(drv, params->mode) < 0)
+ ret = -1;
+
+ /*
+ * If the driver did not support SIOCSIWAUTH, fallback to
+ * SIOCSIWENCODE here.
+ */
+ if (drv->auth_alg_fallback &&
+ wpa_driver_wext_auth_alg_fallback(drv, params) < 0)
+ ret = -1;
+
+ if (!params->bssid &&
+ wpa_driver_wext_set_bssid(drv, NULL) < 0)
+ ret = -1;
+
+ /* TODO: should consider getting wpa version and cipher/key_mgmt suites
+ * from configuration, not from here, where only the selected suite is
+ * available */
+ if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len)
+ < 0)
+ ret = -1;
+ if (params->wpa_ie == NULL || params->wpa_ie_len == 0)
+ value = IW_AUTH_WPA_VERSION_DISABLED;
+ else if (params->wpa_ie[0] == WLAN_EID_RSN)
+ value = IW_AUTH_WPA_VERSION_WPA2;
+ else
+ value = IW_AUTH_WPA_VERSION_WPA;
+ if (wpa_driver_wext_set_auth_param(drv,
+ IW_AUTH_WPA_VERSION, value) < 0)
+ ret = -1;
+ value = wpa_driver_wext_cipher2wext(params->pairwise_suite);
+ if (wpa_driver_wext_set_auth_param(drv,
+ IW_AUTH_CIPHER_PAIRWISE, value) < 0)
+ ret = -1;
+ value = wpa_driver_wext_cipher2wext(params->group_suite);
+ if (wpa_driver_wext_set_auth_param(drv,
+ IW_AUTH_CIPHER_GROUP, value) < 0)
+ ret = -1;
+ value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite);
+ if (wpa_driver_wext_set_auth_param(drv,
+ IW_AUTH_KEY_MGMT, value) < 0)
+ ret = -1;
+ value = params->key_mgmt_suite != KEY_MGMT_NONE ||
+ params->pairwise_suite != CIPHER_NONE ||
+ params->group_suite != CIPHER_NONE ||
+ params->wpa_ie_len;
+ if (wpa_driver_wext_set_auth_param(drv,
+ IW_AUTH_PRIVACY_INVOKED, value) < 0)
+ ret = -1;
+
+ /* Allow unencrypted EAPOL messages even if pairwise keys are set when
+ * not using WPA. IEEE 802.1X specifies that these frames are not
+ * encrypted, but WPA encrypts them when pairwise keys are in use. */
+ if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
+ params->key_mgmt_suite == KEY_MGMT_PSK)
+ allow_unencrypted_eapol = 0;
+ else
+ allow_unencrypted_eapol = 1;
+
+ if (wpa_driver_wext_set_psk(drv, params->psk) < 0)
+ ret = -1;
+ if (wpa_driver_wext_set_auth_param(drv,
+ IW_AUTH_RX_UNENCRYPTED_EAPOL,
+ allow_unencrypted_eapol) < 0)
+ ret = -1;
+#ifdef CONFIG_IEEE80211W
+ switch (params->mgmt_frame_protection) {
+ case NO_MGMT_FRAME_PROTECTION:
+ value = IW_AUTH_MFP_DISABLED;
+ break;
+ case MGMT_FRAME_PROTECTION_OPTIONAL:
+ value = IW_AUTH_MFP_OPTIONAL;
+ break;
+ case MGMT_FRAME_PROTECTION_REQUIRED:
+ value = IW_AUTH_MFP_REQUIRED;
+ break;
+ };
+ if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0)
+ ret = -1;
+#endif /* CONFIG_IEEE80211W */
+ if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0)
+ ret = -1;
+ if (!drv->cfg80211 &&
+ wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
+ ret = -1;
+ if (params->bssid &&
+ wpa_driver_wext_set_bssid(drv, params->bssid) < 0)
+ ret = -1;
+ if (drv->cfg80211 &&
+ wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
+ ret = -1;
+
+ return ret;
+}
+
+
+static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ int algs = 0, res;
+
+ if (auth_alg & WPA_AUTH_ALG_OPEN)
+ algs |= IW_AUTH_ALG_OPEN_SYSTEM;
+ if (auth_alg & WPA_AUTH_ALG_SHARED)
+ algs |= IW_AUTH_ALG_SHARED_KEY;
+ if (auth_alg & WPA_AUTH_ALG_LEAP)
+ algs |= IW_AUTH_ALG_LEAP;
+ if (algs == 0) {
+ /* at least one algorithm should be set */
+ algs = IW_AUTH_ALG_OPEN_SYSTEM;
+ }
+
+ res = wpa_driver_wext_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG,
+ algs);
+ drv->auth_alg_fallback = res == -2;
+ return res;
+}
+
+
+/**
+ * wpa_driver_wext_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_mode(void *priv, int mode)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = -1;
+ unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.mode = new_mode;
+ if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) == 0) {
+ ret = 0;
+ goto done;
+ }
+
+ if (errno != EBUSY) {
+ perror("ioctl[SIOCSIWMODE]");
+ goto done;
+ }
+
+ /* mac80211 doesn't allow mode changes while the device is up, so if
+ * the device isn't in the mode we're about to change to, take device
+ * down, try to set the mode again, and bring it back up.
+ */
+ if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
+ perror("ioctl[SIOCGIWMODE]");
+ goto done;
+ }
+
+ if (iwr.u.mode == new_mode) {
+ ret = 0;
+ goto done;
+ }
+
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0) == 0) {
+ /* Try to set the mode again while the interface is down */
+ iwr.u.mode = new_mode;
+ if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0)
+ perror("ioctl[SIOCSIWMODE]");
+ else
+ ret = 0;
+
+ (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1);
+ }
+
+done:
+ return ret;
+}
+
+
+static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv,
+ u32 cmd, const u8 *bssid, const u8 *pmkid)
+{
+ struct iwreq iwr;
+ struct iw_pmksa pmksa;
+ int ret = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ os_memset(&pmksa, 0, sizeof(pmksa));
+ pmksa.cmd = cmd;
+ pmksa.bssid.sa_family = ARPHRD_ETHER;
+ if (bssid)
+ os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN);
+ if (pmkid)
+ os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN);
+ iwr.u.data.pointer = (caddr_t) &pmksa;
+ iwr.u.data.length = sizeof(pmksa);
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) {
+ if (errno != EOPNOTSUPP)
+ perror("ioctl[SIOCSIWPMKSA]");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid);
+}
+
+
+static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid);
+}
+
+
+static int wpa_driver_wext_flush_pmkid(void *priv)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ return wpa_driver_wext_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL);
+}
+
+
+int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ if (!drv->has_capability)
+ return -1;
+ os_memcpy(capa, &drv->capa, sizeof(*capa));
+ return 0;
+}
+
+
+int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv,
+ const char *ifname)
+{
+ if (ifname == NULL) {
+ drv->ifindex2 = -1;
+ return 0;
+ }
+
+ drv->ifindex2 = if_nametoindex(ifname);
+ if (drv->ifindex2 <= 0)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for "
+ "wireless events", drv->ifindex2, ifname);
+
+ return 0;
+}
+
+
+int wpa_driver_wext_set_operstate(void *priv, int state)
+{
+ struct wpa_driver_wext_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
+ __func__, drv->operstate, state, state ? "UP" : "DORMANT");
+ drv->operstate = state;
+ return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+ state ? IF_OPER_UP : IF_OPER_DORMANT);
+}
+
+
+int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv)
+{
+ return drv->we_version_compiled;
+}
+
+
+static const char * wext_get_radio_name(void *priv)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ return drv->phyname;
+}
+
+
+const struct wpa_driver_ops wpa_driver_wext_ops = {
+ .name = "wext",
+ .desc = "Linux wireless extensions (generic)",
+ .get_bssid = wpa_driver_wext_get_bssid,
+ .get_ssid = wpa_driver_wext_get_ssid,
+ .set_key = wpa_driver_wext_set_key,
+ .set_countermeasures = wpa_driver_wext_set_countermeasures,
+ .scan2 = wpa_driver_wext_scan,
+ .get_scan_results2 = wpa_driver_wext_get_scan_results,
+ .deauthenticate = wpa_driver_wext_deauthenticate,
+ .disassociate = wpa_driver_wext_disassociate,
+ .associate = wpa_driver_wext_associate,
+ .init = wpa_driver_wext_init,
+ .deinit = wpa_driver_wext_deinit,
+ .add_pmkid = wpa_driver_wext_add_pmkid,
+ .remove_pmkid = wpa_driver_wext_remove_pmkid,
+ .flush_pmkid = wpa_driver_wext_flush_pmkid,
+ .get_capa = wpa_driver_wext_get_capa,
+ .set_operstate = wpa_driver_wext_set_operstate,
+ .get_radio_name = wext_get_radio_name,
+};
diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h
new file mode 100644
index 0000000..89c13eb
--- /dev/null
+++ b/src/drivers/driver_wext.h
@@ -0,0 +1,87 @@
+/*
+ * WPA Supplicant - driver_wext exported functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef DRIVER_WEXT_H
+#define DRIVER_WEXT_H
+
+#include <net/if.h>
+
+struct wpa_driver_wext_data {
+ void *ctx;
+ struct netlink_data *netlink;
+ int ioctl_sock;
+ int mlme_sock;
+ char ifname[IFNAMSIZ + 1];
+ 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;
+};
+
+int wpa_driver_wext_get_bssid(void *priv, u8 *bssid);
+int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid);
+int wpa_driver_wext_get_ssid(void *priv, u8 *ssid);
+int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len);
+int wpa_driver_wext_set_freq(void *priv, int freq);
+int wpa_driver_wext_set_mode(void *priv, int mode);
+int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+ const u8 *addr, int key_idx,
+ int set_tx, const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len);
+int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params);
+struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv);
+
+void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+
+int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv,
+ const char *ifname);
+
+void * wpa_driver_wext_init(void *ctx, const char *ifname);
+void wpa_driver_wext_deinit(void *priv);
+
+int wpa_driver_wext_set_operstate(void *priv, int state);
+int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv);
+
+int wpa_driver_wext_associate(void *priv,
+ struct wpa_driver_associate_params *params);
+int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa);
+int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
+ int idx, u32 value);
+int wpa_driver_wext_cipher2wext(int cipher);
+int wpa_driver_wext_keymgmt2wext(int keymgmt);
+
+#endif /* DRIVER_WEXT_H */
diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c
new file mode 100644
index 0000000..618db26
--- /dev/null
+++ b/src/drivers/driver_wired.c
@@ -0,0 +1,629 @@
+/*
+ * Wired Ethernet driver interface
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#ifdef __linux__
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#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;
+}
+
+
+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;
+ }
+
+ return drv;
+}
+
+
+static void wpa_driver_wired_deinit(void *priv)
+{
+ struct wpa_driver_wired_data *drv = priv;
+ int flags;
+
+ if (drv->membership &&
+ wired_multicast_membership(drv->pf_sock,
+ if_nametoindex(drv->ifname),
+ pae_group_addr, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
+ "group (PACKET)", __func__);
+ }
+
+ if (drv->multi &&
+ wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
+ "group (SIOCDELMULTI)", __func__);
+ }
+
+ if (drv->iff_allmulti &&
+ (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 ||
+ wpa_driver_wired_set_ifflags(drv->ifname,
+ flags & ~IFF_ALLMULTI) < 0)) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
+ __func__);
+ }
+
+ if (drv->iff_up &&
+ wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 &&
+ (flags & IFF_UP) &&
+ wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
+ __func__);
+ }
+
+ if (drv->pf_sock != -1)
+ close(drv->pf_sock);
+
+ os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_wired_ops = {
+ .name = "wired",
+ .desc = "Wired Ethernet driver",
+ .hapd_init = wired_driver_hapd_init,
+ .hapd_deinit = wired_driver_hapd_deinit,
+ .hapd_send_eapol = wired_send_eapol,
+ .get_ssid = wpa_driver_wired_get_ssid,
+ .get_bssid = wpa_driver_wired_get_bssid,
+ .get_capa = wpa_driver_wired_get_capa,
+ .init = wpa_driver_wired_init,
+ .deinit = wpa_driver_wired_deinit,
+};
diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
new file mode 100644
index 0000000..b710778
--- /dev/null
+++ b/src/drivers/drivers.c
@@ -0,0 +1,114 @@
+/*
+ * Driver interface list
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+
+#ifdef CONFIG_DRIVER_WEXT
+extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NL80211
+extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_HOSTAP
+extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_MADWIFI
+extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */
+#endif /* CONFIG_DRIVER_MADWIFI */
+#ifdef CONFIG_DRIVER_BROADCOM
+extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */
+#endif /* CONFIG_DRIVER_BROADCOM */
+#ifdef CONFIG_DRIVER_BSD
+extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_NDIS
+extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_WIRED
+extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
+#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_TEST
+extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */
+#endif /* CONFIG_DRIVER_TEST */
+#ifdef CONFIG_DRIVER_RALINK
+extern struct wpa_driver_ops wpa_driver_ralink_ops; /* driver_ralink.c */
+#endif /* CONFIG_DRIVER_RALINK */
+#ifdef CONFIG_DRIVER_OSX
+extern struct wpa_driver_ops wpa_driver_osx_ops; /* driver_osx.m */
+#endif /* CONFIG_DRIVER_OSX */
+#ifdef CONFIG_DRIVER_IPHONE
+extern struct wpa_driver_ops wpa_driver_iphone_ops; /* driver_iphone.m */
+#endif /* CONFIG_DRIVER_IPHONE */
+#ifdef CONFIG_DRIVER_ROBOSWITCH
+/* driver_roboswitch.c */
+extern struct wpa_driver_ops wpa_driver_roboswitch_ops;
+#endif /* CONFIG_DRIVER_ROBOSWITCH */
+#ifdef CONFIG_DRIVER_ATHEROS
+extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */
+#endif /* CONFIG_DRIVER_ATHEROS */
+#ifdef CONFIG_DRIVER_NONE
+extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */
+#endif /* CONFIG_DRIVER_NONE */
+
+
+struct wpa_driver_ops *wpa_drivers[] =
+{
+#ifdef CONFIG_DRIVER_WEXT
+ &wpa_driver_wext_ops,
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NL80211
+ &wpa_driver_nl80211_ops,
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_HOSTAP
+ &wpa_driver_hostap_ops,
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_MADWIFI
+ &wpa_driver_madwifi_ops,
+#endif /* CONFIG_DRIVER_MADWIFI */
+#ifdef CONFIG_DRIVER_BROADCOM
+ &wpa_driver_broadcom_ops,
+#endif /* CONFIG_DRIVER_BROADCOM */
+#ifdef CONFIG_DRIVER_BSD
+ &wpa_driver_bsd_ops,
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_NDIS
+ &wpa_driver_ndis_ops,
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_WIRED
+ &wpa_driver_wired_ops,
+#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_TEST
+ &wpa_driver_test_ops,
+#endif /* CONFIG_DRIVER_TEST */
+#ifdef CONFIG_DRIVER_RALINK
+ &wpa_driver_ralink_ops,
+#endif /* CONFIG_DRIVER_RALINK */
+#ifdef CONFIG_DRIVER_OSX
+ &wpa_driver_osx_ops,
+#endif /* CONFIG_DRIVER_OSX */
+#ifdef CONFIG_DRIVER_IPHONE
+ &wpa_driver_iphone_ops,
+#endif /* CONFIG_DRIVER_IPHONE */
+#ifdef CONFIG_DRIVER_ROBOSWITCH
+ &wpa_driver_roboswitch_ops,
+#endif /* CONFIG_DRIVER_ROBOSWITCH */
+#ifdef CONFIG_DRIVER_ATHEROS
+ &wpa_driver_atheros_ops,
+#endif /* CONFIG_DRIVER_ATHEROS */
+#ifdef CONFIG_DRIVER_NONE
+ &wpa_driver_none_ops,
+#endif /* CONFIG_DRIVER_NONE */
+ NULL
+};
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
new file mode 100644
index 0000000..ebf39c8
--- /dev/null
+++ b/src/drivers/drivers.mak
@@ -0,0 +1,180 @@
+##### 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_HOSTAP
+DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP
+DRV_OBJS += ../src/drivers/driver_hostap.o
+CONFIG_WIRELESS_EXTENSION=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_WIRED
+DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
+DRV_OBJS += ../src/drivers/driver_wired.o
+endif
+
+ifdef CONFIG_DRIVER_MADWIFI
+DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI
+DRV_OBJS += ../src/drivers/driver_madwifi.o
+CONFIG_WIRELESS_EXTENSION=y
+CONFIG_L2_PACKET=linux
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_NL80211
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
+DRV_OBJS += ../src/drivers/driver_nl80211.o
+DRV_OBJS += ../src/utils/radiotap.o
+NEED_SME=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+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
+
+ifdef CONFIG_DRIVER_BSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_BSD
+DRV_OBJS += ../src/drivers/driver_bsd.o
+CONFIG_L2_FREEBSD=y
+CONFIG_DNET_PCAP=y
+endif
+
+ifdef CONFIG_DRIVER_TEST
+DRV_CFLAGS += -DCONFIG_DRIVER_TEST
+DRV_OBJS += ../src/drivers/driver_test.o
+NEED_AP_MLME=y
+endif
+
+ifdef CONFIG_DRIVER_NONE
+DRV_CFLAGS += -DCONFIG_DRIVER_NONE
+DRV_OBJS += ../src/drivers/driver_none.o
+endif
+
+##### PURE AP DRIVERS
+
+ifdef CONFIG_DRIVER_ATHEROS
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
+DRV_AP_OBJS += ../src/drivers/driver_atheros.o
+CONFIG_L2_PACKET=linux
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+##### PURE CLIENT DRIVERS
+
+ifdef CONFIG_DRIVER_WEXT
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+CONFIG_WIRELESS_EXTENSION=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+endif
+
+ifdef CONFIG_DRIVER_RALINK
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK
+DRV_WPA_OBJS += ../src/drivers/driver_ralink.o
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_BROADCOM
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM
+DRV_WPA_OBJS += ../src/drivers/driver_broadcom.o
+endif
+
+ifdef CONFIG_DRIVER_NDIS
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
+DRV_WPA_OBJS += ../src/drivers/driver_ndis.o
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+DRV_WPA_OBJS += ../src/drivers/driver_ndis_.o
+endif
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=pcap
+endif
+CONFIG_WINPCAP=y
+ifdef CONFIG_USE_NDISUIO
+DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO
+endif
+endif
+
+ifdef CONFIG_DRIVER_OSX
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_OSX
+DRV_WPA_OBJS += ../src/drivers/driver_osx.o
+DRV_WPA_LDFLAGS += -framework CoreFoundation
+DRV_WPA_LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211
+endif
+
+ifdef CONFIG_DRIVER_IPHONE
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPHONE
+DRV_WPA_OBJS += ../src/drivers/driver_iphone.o
+DRV_WPA_OBJS += ../src/drivers/MobileApple80211.o
+DRV_WPA_LDFLAGS += -framework CoreFoundation
+endif
+
+ifdef CONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_OBJS += ../src/drivers/driver_roboswitch.o
+endif
+
+ifdef CONFIG_WIRELESS_EXTENSION
+DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+DRV_WPA_OBJS += ../src/drivers/driver_wext.o
+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
+
+
+##### COMMON VARS
+DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
+DRV_WPA_CFLAGS += $(DRV_CFLAGS)
+DRV_AP_CFLAGS += $(DRV_CFLAGS)
+
+DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS)
+DRV_WPA_LIBS += $(DRV_LIBS)
+DRV_AP_LIBS += $(DRV_LIBS)
+
+DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS)
+DRV_WPA_OBJS += $(DRV_OBJS)
+DRV_AP_OBJS += $(DRV_OBJS)
+
+DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS)
+DRV_WPA_LDFLAGS += $(DRV_LDFLAGS)
+DRV_AP_LDFLAGS += $(DRV_LDFLAGS)
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
new file mode 100644
index 0000000..c690e1c
--- /dev/null
+++ b/src/drivers/drivers.mk
@@ -0,0 +1,183 @@
+##### 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_HOSTAP
+DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP
+DRV_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_WIRED
+DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
+DRV_OBJS += src/drivers/driver_wired.c
+endif
+
+ifdef CONFIG_DRIVER_MADWIFI
+DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI
+DRV_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_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_LIBNL_TINY
+DRV_LIBS += -lnl-tiny
+else
+DRV_LIBS += -lnl
+endif
+
+ifdef CONFIG_LIBNL20
+DRV_LIBS += -lnl-genl
+DRV_CFLAGS += -DCONFIG_LIBNL20
+endif
+endif
+
+ifdef CONFIG_DRIVER_BSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_BSD
+DRV_OBJS += src/drivers/driver_bsd.c
+CONFIG_L2_FREEBSD=y
+CONFIG_DNET_PCAP=y
+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_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_RALINK
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK
+DRV_WPA_OBJS += src/drivers/driver_ralink.c
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_BROADCOM
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM
+DRV_WPA_OBJS += src/drivers/driver_broadcom.c
+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_OSX
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_OSX
+DRV_WPA_OBJS += src/drivers/driver_osx.c
+DRV_WPA_LDFLAGS += -framework CoreFoundation
+DRV_WPA_LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211
+endif
+
+ifdef CONFIG_DRIVER_IPHONE
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPHONE
+DRV_WPA_OBJS += src/drivers/driver_iphone.c
+DRV_WPA_OBJS += src/drivers/MobileApple80211.c
+DRV_WPA_LDFLAGS += -framework CoreFoundation
+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
+
+##### COMMON VARS
+DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
+DRV_WPA_CFLAGS += $(DRV_CFLAGS)
+DRV_AP_CFLAGS += $(DRV_CFLAGS)
+
+DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS)
+DRV_WPA_LIBS += $(DRV_LIBS)
+DRV_AP_LIBS += $(DRV_LIBS)
+
+DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS)
+DRV_WPA_OBJS += $(DRV_OBJS)
+DRV_AP_OBJS += $(DRV_OBJS)
+
+DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS)
+DRV_WPA_LDFLAGS += $(DRV_LDFLAGS)
+DRV_AP_LDFLAGS += $(DRV_LDFLAGS)
diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
new file mode 100644
index 0000000..0d6cf54
--- /dev/null
+++ b/src/drivers/linux_ioctl.c
@@ -0,0 +1,198 @@
+/*
+ * Linux ioctl helper functions for driver wrappers
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include "utils/common.h"
+#include "linux_ioctl.h"
+
+
+int linux_set_iface_flags(int sock, const char *ifname, int dev_up)
+{
+ struct ifreq ifr;
+
+ if (sock < 0)
+ return -1;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
+ ifname, strerror(errno));
+ return -1;
+ }
+
+ if (dev_up) {
+ if (ifr.ifr_flags & IFF_UP)
+ return 0;
+ ifr.ifr_flags |= IFF_UP;
+ } else {
+ if (!(ifr.ifr_flags & IFF_UP))
+ return 0;
+ ifr.ifr_flags &= ~IFF_UP;
+ }
+
+ if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "Could not set interface %s flags: %s",
+ ifname, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr)
+{
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFHWADDR, &ifr)) {
+ wpa_printf(MSG_ERROR, "Could not get interface %s hwaddr: %s",
+ ifname, strerror(errno));
+ return -1;
+ }
+
+ if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+ wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x",
+ ifname, ifr.ifr_hwaddr.sa_family);
+ return -1;
+ }
+ os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+ return 0;
+}
+
+
+int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr)
+{
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
+ ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
+
+ if (ioctl(sock, SIOCSIFHWADDR, &ifr)) {
+ wpa_printf(MSG_DEBUG, "Could not set interface %s hwaddr: %s",
+ ifname, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#ifndef SIOCBRADDBR
+#define SIOCBRADDBR 0x89a0
+#endif
+#ifndef SIOCBRDELBR
+#define SIOCBRDELBR 0x89a1
+#endif
+#ifndef SIOCBRADDIF
+#define SIOCBRADDIF 0x89a2
+#endif
+#ifndef SIOCBRDELIF
+#define SIOCBRDELIF 0x89a3
+#endif
+
+
+int linux_br_add(int sock, const char *brname)
+{
+ if (ioctl(sock, SIOCBRADDBR, brname) < 0) {
+ wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s",
+ brname, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int linux_br_del(int sock, const char *brname)
+{
+ if (ioctl(sock, SIOCBRDELBR, brname) < 0) {
+ wpa_printf(MSG_DEBUG, "Could not remove bridge %s: %s",
+ brname, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int linux_br_add_if(int sock, const char *brname, const char *ifname)
+{
+ struct ifreq ifr;
+ int ifindex;
+
+ ifindex = if_nametoindex(ifname);
+ if (ifindex == 0)
+ return -1;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ);
+ ifr.ifr_ifindex = ifindex;
+ if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) {
+ wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
+ "%s: %s", ifname, brname, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int linux_br_del_if(int sock, const char *brname, const char *ifname)
+{
+ struct ifreq ifr;
+ int ifindex;
+
+ ifindex = if_nametoindex(ifname);
+ if (ifindex == 0)
+ return -1;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ);
+ ifr.ifr_ifindex = ifindex;
+ if (ioctl(sock, SIOCBRDELIF, &ifr) < 0) {
+ wpa_printf(MSG_DEBUG, "Could not remove interface %s from "
+ "bridge %s: %s", ifname, brname, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int linux_br_get(char *brname, const char *ifname)
+{
+ char path[128], brlink[128], *pos;
+ os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge",
+ ifname);
+ os_memset(brlink, 0, sizeof(brlink));
+ if (readlink(path, brlink, sizeof(brlink) - 1) < 0)
+ return -1;
+ pos = os_strrchr(brlink, '/');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ os_strlcpy(brname, pos, IFNAMSIZ);
+ return 0;
+}
diff --git a/src/drivers/linux_ioctl.h b/src/drivers/linux_ioctl.h
new file mode 100644
index 0000000..a555738
--- /dev/null
+++ b/src/drivers/linux_ioctl.h
@@ -0,0 +1,27 @@
+/*
+ * Linux ioctl helper functions for driver wrappers
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef LINUX_IOCTL_H
+#define LINUX_IOCTL_H
+
+int linux_set_iface_flags(int sock, const char *ifname, int dev_up);
+int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr);
+int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr);
+int linux_br_add(int sock, const char *brname);
+int linux_br_del(int sock, const char *brname);
+int linux_br_add_if(int sock, const char *brname, const char *ifname);
+int linux_br_del_if(int sock, const char *brname, const char *ifname);
+int linux_br_get(char *brname, const char *ifname);
+
+#endif /* LINUX_IOCTL_H */
diff --git a/src/drivers/ndis_events.c b/src/drivers/ndis_events.c
new file mode 100644
index 0000000..f6eaa7c
--- /dev/null
+++ b/src/drivers/ndis_events.c
@@ -0,0 +1,808 @@
+/*
+ * ndis_events - Receive NdisMIndicateStatus() events using WMI
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#define _WIN32_WINNT 0x0400
+
+#include "includes.h"
+
+#ifndef COBJMACROS
+#define COBJMACROS
+#endif /* COBJMACROS */
+#include <wbemidl.h>
+
+#include "common.h"
+
+
+static int wmi_refcnt = 0;
+static int wmi_first = 1;
+
+struct ndis_events_data {
+ IWbemObjectSink sink;
+ IWbemObjectSinkVtbl sink_vtbl;
+
+ IWbemServices *pSvc;
+ IWbemLocator *pLoc;
+
+ HANDLE read_pipe, write_pipe, event_avail;
+ UINT ref;
+ int terminating;
+ char *ifname; /* {GUID..} */
+ WCHAR *adapter_desc;
+};
+
+#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL
+#define BstrFree(x) if (x) SysFreeString(x)
+
+/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to
+ * BSTRs */
+HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery(
+ IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
+ long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
+{
+ BSTR bsQueryLanguage, bsQuery;
+ HRESULT hr;
+
+ bsQueryLanguage = BstrAlloc(strQueryLanguage);
+ bsQuery = BstrAlloc(strQuery);
+
+ hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags,
+ pCtx, ppEnum);
+
+ BstrFree(bsQueryLanguage);
+ BstrFree(bsQuery);
+
+ return hr;
+}
+
+
+HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync(
+ IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
+ long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
+{
+ BSTR bsQueryLanguage, bsQuery;
+ HRESULT hr;
+
+ bsQueryLanguage = BstrAlloc(strQueryLanguage);
+ bsQuery = BstrAlloc(strQuery);
+
+ hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage,
+ bsQuery, lFlags, pCtx,
+ pResponseHandler);
+
+ BstrFree(bsQueryLanguage);
+ BstrFree(bsQuery);
+
+ return hr;
+}
+
+
+HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer(
+ IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser,
+ LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags,
+ LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace)
+{
+ BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority;
+ HRESULT hr;
+
+ bsNetworkResource = BstrAlloc(strNetworkResource);
+ bsUser = BstrAlloc(strUser);
+ bsPassword = BstrAlloc(strPassword);
+ bsLocale = BstrAlloc(strLocale);
+ bsAuthority = BstrAlloc(strAuthority);
+
+ hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser,
+ bsPassword, bsLocale, lSecurityFlags,
+ bsAuthority, pCtx, ppNamespace);
+
+ BstrFree(bsNetworkResource);
+ BstrFree(bsUser);
+ BstrFree(bsPassword);
+ BstrFree(bsLocale);
+ BstrFree(bsAuthority);
+
+ return hr;
+}
+
+
+enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
+ EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
+
+static int ndis_events_get_adapter(struct ndis_events_data *events,
+ const char *ifname, const char *desc);
+
+
+static int ndis_events_constructor(struct ndis_events_data *events)
+{
+ events->ref = 1;
+
+ if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) {
+ wpa_printf(MSG_ERROR, "CreatePipe() failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+ events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (events->event_avail == NULL) {
+ wpa_printf(MSG_ERROR, "CreateEvent() failed: %d",
+ (int) GetLastError());
+ CloseHandle(events->read_pipe);
+ CloseHandle(events->write_pipe);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void ndis_events_destructor(struct ndis_events_data *events)
+{
+ CloseHandle(events->read_pipe);
+ CloseHandle(events->write_pipe);
+ CloseHandle(events->event_avail);
+ IWbemServices_Release(events->pSvc);
+ IWbemLocator_Release(events->pLoc);
+ if (--wmi_refcnt == 0)
+ CoUninitialize();
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
+{
+ *obj = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_IWbemObjectSink)) {
+ *obj = this;
+ IWbemObjectSink_AddRef(this);
+ return NOERROR;
+ }
+
+ return E_NOINTERFACE;
+}
+
+
+static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
+{
+ struct ndis_events_data *events = (struct ndis_events_data *) this;
+ return ++events->ref;
+}
+
+
+static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
+{
+ struct ndis_events_data *events = (struct ndis_events_data *) this;
+
+ if (--events->ref != 0)
+ return events->ref;
+
+ ndis_events_destructor(events);
+ wpa_printf(MSG_DEBUG, "ndis_events: terminated");
+ os_free(events->adapter_desc);
+ os_free(events->ifname);
+ os_free(events);
+ return 0;
+}
+
+
+static int ndis_events_send_event(struct ndis_events_data *events,
+ enum event_types type,
+ char *data, size_t data_len)
+{
+ char buf[512], *pos, *end;
+ int _type;
+ DWORD written;
+
+ end = buf + sizeof(buf);
+ _type = (int) type;
+ os_memcpy(buf, &_type, sizeof(_type));
+ pos = buf + sizeof(_type);
+
+ if (data) {
+ if (2 + data_len > (size_t) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "Not enough room for send_event "
+ "data (%d)", data_len);
+ return -1;
+ }
+ *pos++ = data_len >> 8;
+ *pos++ = data_len & 0xff;
+ os_memcpy(pos, data, data_len);
+ pos += data_len;
+ }
+
+ if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
+ SetEvent(events->event_avail);
+ return 0;
+ }
+ wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
+ return -1;
+}
+
+
+static void ndis_events_media_connect(struct ndis_events_data *events)
+{
+ wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
+ ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
+}
+
+
+static void ndis_events_media_disconnect(struct ndis_events_data *events)
+{
+ wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
+ ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
+}
+
+
+static void ndis_events_media_specific(struct ndis_events_data *events,
+ IWbemClassObject *pObj)
+{
+ VARIANT vt;
+ HRESULT hr;
+ LONG lower, upper, k;
+ UCHAR ch;
+ char *data, *pos;
+ size_t data_len;
+
+ wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
+
+ /* This is the StatusBuffer from NdisMIndicateStatus() call */
+ hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication",
+ 0, &vt, NULL, NULL);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_DEBUG, "Could not get "
+ "NdisStatusMediaSpecificIndication from "
+ "the object?!");
+ return;
+ }
+
+ SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower);
+ SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper);
+ data_len = upper - lower + 1;
+ data = os_malloc(data_len);
+ if (data == NULL) {
+ wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event "
+ "data");
+ VariantClear(&vt);
+ return;
+ }
+
+ pos = data;
+ for (k = lower; k <= upper; k++) {
+ SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
+ *pos++ = ch;
+ }
+ wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len);
+
+ VariantClear(&vt);
+
+ ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
+
+ os_free(data);
+}
+
+
+static void ndis_events_adapter_arrival(struct ndis_events_data *events)
+{
+ wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
+ ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
+}
+
+
+static void ndis_events_adapter_removal(struct ndis_events_data *events)
+{
+ wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
+ ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
+ IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
+{
+ struct ndis_events_data *events = (struct ndis_events_data *) this;
+ long i;
+
+ if (events->terminating) {
+ wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
+ "indication - terminating");
+ return WBEM_NO_ERROR;
+ }
+ /* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
+ lObjectCount); */
+
+ for (i = 0; i < lObjectCount; i++) {
+ IWbemClassObject *pObj = ppObjArray[i];
+ HRESULT hr;
+ VARIANT vtClass, vt;
+
+ hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
+ NULL);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
+ "event.");
+ break;
+ }
+ /* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
+
+ hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
+ NULL);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
+ "from event.");
+ VariantClear(&vtClass);
+ break;
+ }
+
+ if (wcscmp(vtClass.bstrVal,
+ L"MSNdis_NotifyAdapterArrival") == 0) {
+ wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
+ "update adapter description since it may "
+ "have changed with new adapter instance");
+ ndis_events_get_adapter(events, events->ifname, NULL);
+ }
+
+ if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
+ wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
+ "indication for foreign adapter: "
+ "InstanceName: '%S' __CLASS: '%S'",
+ vt.bstrVal, vtClass.bstrVal);
+ VariantClear(&vtClass);
+ VariantClear(&vt);
+ continue;
+ }
+ VariantClear(&vt);
+
+ if (wcscmp(vtClass.bstrVal,
+ L"MSNdis_StatusMediaSpecificIndication") == 0) {
+ ndis_events_media_specific(events, pObj);
+ } else if (wcscmp(vtClass.bstrVal,
+ L"MSNdis_StatusMediaConnect") == 0) {
+ ndis_events_media_connect(events);
+ } else if (wcscmp(vtClass.bstrVal,
+ L"MSNdis_StatusMediaDisconnect") == 0) {
+ ndis_events_media_disconnect(events);
+ } else if (wcscmp(vtClass.bstrVal,
+ L"MSNdis_NotifyAdapterArrival") == 0) {
+ ndis_events_adapter_arrival(events);
+ } else if (wcscmp(vtClass.bstrVal,
+ L"MSNdis_NotifyAdapterRemoval") == 0) {
+ ndis_events_adapter_removal(events);
+ } else {
+ wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
+ "'%S'", vtClass.bstrVal);
+ }
+
+ VariantClear(&vtClass);
+ }
+
+ return WBEM_NO_ERROR;
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
+ BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
+{
+ return WBEM_NO_ERROR;
+}
+
+
+static int notification_query(IWbemObjectSink *pDestSink,
+ IWbemServices *pSvc, const char *class_name)
+{
+ HRESULT hr;
+ WCHAR query[256];
+
+ _snwprintf(query, 256,
+ L"SELECT * FROM %S", class_name);
+ wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+ hr = call_IWbemServices_ExecNotificationQueryAsync(
+ pSvc, L"WQL", query, 0, 0, pDestSink);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
+ "failed with hresult of 0x%x",
+ class_name, (int) hr);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int register_async_notification(IWbemObjectSink *pDestSink,
+ IWbemServices *pSvc)
+{
+ int i;
+ const char *class_list[] = {
+ "MSNdis_StatusMediaConnect",
+ "MSNdis_StatusMediaDisconnect",
+ "MSNdis_StatusMediaSpecificIndication",
+ "MSNdis_NotifyAdapterArrival",
+ "MSNdis_NotifyAdapterRemoval",
+ NULL
+ };
+
+ for (i = 0; class_list[i]; i++) {
+ if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void ndis_events_deinit(struct ndis_events_data *events)
+{
+ events->terminating = 1;
+ IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
+ IWbemObjectSink_Release(&events->sink);
+ /*
+ * Rest of deinitialization is done in ndis_events_destructor() once
+ * all reference count drops to zero.
+ */
+}
+
+
+static int ndis_events_use_desc(struct ndis_events_data *events,
+ const char *desc)
+{
+ char *tmp, *pos;
+ size_t len;
+
+ if (desc == NULL) {
+ if (events->adapter_desc == NULL)
+ return -1;
+ /* Continue using old description */
+ return 0;
+ }
+
+ tmp = os_strdup(desc);
+ if (tmp == NULL)
+ return -1;
+
+ pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
+ if (pos)
+ *pos = '\0';
+
+ len = os_strlen(tmp);
+ events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
+ if (events->adapter_desc == NULL) {
+ os_free(tmp);
+ return -1;
+ }
+ _snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
+ os_free(tmp);
+ return 0;
+}
+
+
+static int ndis_events_get_adapter(struct ndis_events_data *events,
+ const char *ifname, const char *desc)
+{
+ HRESULT hr;
+ IWbemServices *pSvc;
+#define MAX_QUERY_LEN 256
+ WCHAR query[MAX_QUERY_LEN];
+ IEnumWbemClassObject *pEnumerator;
+ IWbemClassObject *pObj;
+ ULONG uReturned;
+ VARIANT vt;
+ int len, pos;
+
+ /*
+ * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
+ * to have better probability of matching with InstanceName from
+ * MSNdis events. If this fails, use the provided description.
+ */
+
+ os_free(events->adapter_desc);
+ events->adapter_desc = NULL;
+
+ hr = call_IWbemLocator_ConnectServer(
+ events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
+ "server (ROOT\\CIMV2) - error 0x%x", (int) hr);
+ return ndis_events_use_desc(events, desc);
+ }
+ wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
+
+ _snwprintf(query, MAX_QUERY_LEN,
+ L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
+ L"WHERE SettingID='%S'", ifname);
+ wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+
+ hr = call_IWbemServices_ExecQuery(
+ pSvc, L"WQL", query,
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ NULL, &pEnumerator);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+ "GUID from Win32_NetworkAdapterConfiguration: "
+ "0x%x", (int) hr);
+ IWbemServices_Release(pSvc);
+ return ndis_events_use_desc(events, desc);
+ }
+
+ uReturned = 0;
+ hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+ &pObj, &uReturned);
+ if (!SUCCEEDED(hr) || uReturned == 0) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+ "GUID from Win32_NetworkAdapterConfiguration: "
+ "0x%x", (int) hr);
+ IEnumWbemClassObject_Release(pEnumerator);
+ IWbemServices_Release(pSvc);
+ return ndis_events_use_desc(events, desc);
+ }
+ IEnumWbemClassObject_Release(pEnumerator);
+
+ VariantInit(&vt);
+ hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
+ "Win32_NetworkAdapterConfiguration: 0x%x",
+ (int) hr);
+ IWbemServices_Release(pSvc);
+ return ndis_events_use_desc(events, desc);
+ }
+
+ _snwprintf(query, MAX_QUERY_LEN,
+ L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
+ L"Index=%d",
+ vt.uintVal);
+ wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+ VariantClear(&vt);
+ IWbemClassObject_Release(pObj);
+
+ hr = call_IWbemServices_ExecQuery(
+ pSvc, L"WQL", query,
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ NULL, &pEnumerator);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+ "from Win32_NetworkAdapter: 0x%x", (int) hr);
+ IWbemServices_Release(pSvc);
+ return ndis_events_use_desc(events, desc);
+ }
+
+ uReturned = 0;
+ hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+ &pObj, &uReturned);
+ if (!SUCCEEDED(hr) || uReturned == 0) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+ "from Win32_NetworkAdapter: 0x%x", (int) hr);
+ IEnumWbemClassObject_Release(pEnumerator);
+ IWbemServices_Release(pSvc);
+ return ndis_events_use_desc(events, desc);
+ }
+ IEnumWbemClassObject_Release(pEnumerator);
+
+ hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
+ "Win32_NetworkAdapter: 0x%x", (int) hr);
+ IWbemClassObject_Release(pObj);
+ IWbemServices_Release(pSvc);
+ return ndis_events_use_desc(events, desc);
+ }
+
+ wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
+ vt.bstrVal);
+ events->adapter_desc = _wcsdup(vt.bstrVal);
+ VariantClear(&vt);
+
+ /*
+ * Try to get even better candidate for matching with InstanceName
+ * from Win32_PnPEntity. This is needed at least for some USB cards
+ * that can change the InstanceName whenever being unplugged and
+ * plugged again.
+ */
+
+ hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
+ "from Win32_NetworkAdapter: 0x%x", (int) hr);
+ IWbemClassObject_Release(pObj);
+ IWbemServices_Release(pSvc);
+ if (events->adapter_desc == NULL)
+ return ndis_events_use_desc(events, desc);
+ return 0; /* use Win32_NetworkAdapter::Name */
+ }
+
+ wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
+ "'%S'", vt.bstrVal);
+
+ len = _snwprintf(query, MAX_QUERY_LEN,
+ L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
+ if (len < 0 || len >= MAX_QUERY_LEN - 1) {
+ VariantClear(&vt);
+ IWbemClassObject_Release(pObj);
+ IWbemServices_Release(pSvc);
+ if (events->adapter_desc == NULL)
+ return ndis_events_use_desc(events, desc);
+ return 0; /* use Win32_NetworkAdapter::Name */
+ }
+
+ /* Escape \ as \\ */
+ for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
+ if (vt.bstrVal[pos] == '\\') {
+ if (len >= MAX_QUERY_LEN - 3)
+ break;
+ query[len++] = '\\';
+ }
+ query[len++] = vt.bstrVal[pos];
+ }
+ query[len++] = L'\'';
+ query[len] = L'\0';
+ VariantClear(&vt);
+ IWbemClassObject_Release(pObj);
+ wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+
+ hr = call_IWbemServices_ExecQuery(
+ pSvc, L"WQL", query,
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ NULL, &pEnumerator);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+ "Name from Win32_PnPEntity: 0x%x", (int) hr);
+ IWbemServices_Release(pSvc);
+ if (events->adapter_desc == NULL)
+ return ndis_events_use_desc(events, desc);
+ return 0; /* use Win32_NetworkAdapter::Name */
+ }
+
+ uReturned = 0;
+ hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+ &pObj, &uReturned);
+ if (!SUCCEEDED(hr) || uReturned == 0) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+ "from Win32_PnPEntity: 0x%x", (int) hr);
+ IEnumWbemClassObject_Release(pEnumerator);
+ IWbemServices_Release(pSvc);
+ if (events->adapter_desc == NULL)
+ return ndis_events_use_desc(events, desc);
+ return 0; /* use Win32_NetworkAdapter::Name */
+ }
+ IEnumWbemClassObject_Release(pEnumerator);
+
+ hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
+ "Win32_PnPEntity: 0x%x", (int) hr);
+ IWbemClassObject_Release(pObj);
+ IWbemServices_Release(pSvc);
+ if (events->adapter_desc == NULL)
+ return ndis_events_use_desc(events, desc);
+ return 0; /* use Win32_NetworkAdapter::Name */
+ }
+
+ wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
+ vt.bstrVal);
+ os_free(events->adapter_desc);
+ events->adapter_desc = _wcsdup(vt.bstrVal);
+ VariantClear(&vt);
+
+ IWbemClassObject_Release(pObj);
+
+ IWbemServices_Release(pSvc);
+
+ if (events->adapter_desc == NULL)
+ return ndis_events_use_desc(events, desc);
+
+ return 0;
+}
+
+
+struct ndis_events_data *
+ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
+ const char *ifname, const char *desc)
+{
+ HRESULT hr;
+ IWbemObjectSink *pSink;
+ struct ndis_events_data *events;
+
+ events = os_zalloc(sizeof(*events));
+ if (events == NULL) {
+ wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
+ return NULL;
+ }
+ events->ifname = os_strdup(ifname);
+ if (events->ifname == NULL) {
+ os_free(events);
+ return NULL;
+ }
+
+ if (wmi_refcnt++ == 0) {
+ hr = CoInitializeEx(0, COINIT_MULTITHREADED);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
+ "returned 0x%x", (int) hr);
+ os_free(events);
+ return NULL;
+ }
+ }
+
+ if (wmi_first) {
+ /* CoInitializeSecurity() must be called once and only once
+ * per process, so let's use wmi_first flag to protect against
+ * multiple calls. */
+ wmi_first = 0;
+
+ hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
+ RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ NULL, EOAC_SECURE_REFS, NULL);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
+ "- returned 0x%x", (int) hr);
+ os_free(events);
+ return NULL;
+ }
+ }
+
+ hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
+ &IID_IWbemLocator,
+ (LPVOID *) (void *) &events->pLoc);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
+ "0x%x", (int) hr);
+ CoUninitialize();
+ os_free(events);
+ return NULL;
+ }
+
+ if (ndis_events_get_adapter(events, ifname, desc) < 0) {
+ CoUninitialize();
+ os_free(events);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
+ events->adapter_desc);
+
+ hr = call_IWbemLocator_ConnectServer(
+ events->pLoc, L"ROOT\\WMI", NULL, NULL,
+ 0, 0, 0, 0, &events->pSvc);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_ERROR, "Could not connect to server - error "
+ "0x%x", (int) hr);
+ CoUninitialize();
+ os_free(events->adapter_desc);
+ os_free(events);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
+
+ ndis_events_constructor(events);
+ pSink = &events->sink;
+ pSink->lpVtbl = &events->sink_vtbl;
+ events->sink_vtbl.QueryInterface = ndis_events_query_interface;
+ events->sink_vtbl.AddRef = ndis_events_add_ref;
+ events->sink_vtbl.Release = ndis_events_release;
+ events->sink_vtbl.Indicate = ndis_events_indicate;
+ events->sink_vtbl.SetStatus = ndis_events_set_status;
+
+ if (register_async_notification(pSink, events->pSvc) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to register async "
+ "notifications");
+ ndis_events_destructor(events);
+ os_free(events->adapter_desc);
+ os_free(events);
+ return NULL;
+ }
+
+ *read_pipe = events->read_pipe;
+ *event_avail = events->event_avail;
+
+ return events;
+}
diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c
new file mode 100644
index 0000000..ad15b1d
--- /dev/null
+++ b/src/drivers/netlink.c
@@ -0,0 +1,204 @@
+/*
+ * Netlink helper functions for driver wrappers
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "priv_netlink.h"
+#include "netlink.h"
+
+
+struct netlink_data {
+ struct netlink_config *cfg;
+ int sock;
+};
+
+
+static void netlink_receive_link(struct netlink_data *netlink,
+ void (*cb)(void *ctx, struct ifinfomsg *ifi,
+ u8 *buf, size_t len),
+ struct nlmsghdr *h)
+{
+ if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg))
+ return;
+ cb(netlink->cfg->ctx, NLMSG_DATA(h),
+ NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
+ NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)));
+}
+
+
+static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct netlink_data *netlink = eloop_ctx;
+ char buf[8192];
+ int left;
+ struct sockaddr_nl from;
+ socklen_t fromlen;
+ struct nlmsghdr *h;
+ int max_events = 10;
+
+try_again:
+ fromlen = sizeof(from);
+ left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+ (struct sockaddr *) &from, &fromlen);
+ if (left < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s",
+ strerror(errno));
+ return;
+ }
+
+ h = (struct nlmsghdr *) buf;
+ while (NLMSG_OK(h, left)) {
+ switch (h->nlmsg_type) {
+ case RTM_NEWLINK:
+ netlink_receive_link(netlink, netlink->cfg->newlink_cb,
+ h);
+ break;
+ case RTM_DELLINK:
+ netlink_receive_link(netlink, netlink->cfg->dellink_cb,
+ h);
+ break;
+ }
+
+ h = NLMSG_NEXT(h, left);
+ }
+
+ if (left > 0) {
+ wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of "
+ "netlink message", left);
+ }
+
+ if (--max_events > 0) {
+ /*
+ * Try to receive all events in one eloop call in order to
+ * limit race condition on cases where AssocInfo event, Assoc
+ * event, and EAPOL frames are received more or less at the
+ * same time. We want to process the event messages first
+ * before starting EAPOL processing.
+ */
+ goto try_again;
+ }
+}
+
+
+struct netlink_data * netlink_init(struct netlink_config *cfg)
+{
+ struct netlink_data *netlink;
+ struct sockaddr_nl local;
+
+ netlink = os_zalloc(sizeof(*netlink));
+ if (netlink == NULL)
+ return NULL;
+
+ netlink->cfg = cfg;
+
+ netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (netlink->sock < 0) {
+ wpa_printf(MSG_ERROR, "netlink: Failed to open netlink "
+ "socket: %s", strerror(errno));
+ netlink_deinit(netlink);
+ return NULL;
+ }
+
+ os_memset(&local, 0, sizeof(local));
+ local.nl_family = AF_NETLINK;
+ local.nl_groups = RTMGRP_LINK;
+ if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0)
+ {
+ wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink "
+ "socket: %s", strerror(errno));
+ netlink_deinit(netlink);
+ return NULL;
+ }
+
+ eloop_register_read_sock(netlink->sock, netlink_receive, netlink,
+ NULL);
+
+ return netlink;
+}
+
+
+void netlink_deinit(struct netlink_data *netlink)
+{
+ if (netlink == NULL)
+ return;
+ if (netlink->sock >= 0) {
+ eloop_unregister_read_sock(netlink->sock);
+ close(netlink->sock);
+ }
+ os_free(netlink->cfg);
+ os_free(netlink);
+}
+
+int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
+ int linkmode, int operstate)
+{
+ struct {
+ struct nlmsghdr hdr;
+ struct ifinfomsg ifinfo;
+ char opts[16];
+ } req;
+ struct rtattr *rta;
+ static int nl_seq;
+ ssize_t ret;
+
+ os_memset(&req, 0, sizeof(req));
+
+ req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.hdr.nlmsg_type = RTM_SETLINK;
+ req.hdr.nlmsg_flags = NLM_F_REQUEST;
+ req.hdr.nlmsg_seq = ++nl_seq;
+ req.hdr.nlmsg_pid = 0;
+
+ req.ifinfo.ifi_family = AF_UNSPEC;
+ req.ifinfo.ifi_type = 0;
+ req.ifinfo.ifi_index = ifindex;
+ req.ifinfo.ifi_flags = 0;
+ req.ifinfo.ifi_change = 0;
+
+ if (linkmode != -1) {
+ rta = aliasing_hide_typecast(
+ ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
+ struct rtattr);
+ rta->rta_type = IFLA_LINKMODE;
+ rta->rta_len = RTA_LENGTH(sizeof(char));
+ *((char *) RTA_DATA(rta)) = linkmode;
+ req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
+ RTA_LENGTH(sizeof(char));
+ }
+ if (operstate != -1) {
+ rta = aliasing_hide_typecast(
+ ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
+ struct rtattr);
+ rta->rta_type = IFLA_OPERSTATE;
+ rta->rta_len = RTA_LENGTH(sizeof(char));
+ *((char *) RTA_DATA(rta)) = operstate;
+ req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
+ RTA_LENGTH(sizeof(char));
+ }
+
+ wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d",
+ linkmode, operstate);
+
+ ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA "
+ "failed: %s (assume operstate is not supported)",
+ strerror(errno));
+ }
+
+ return ret < 0 ? -1 : 0;
+}
diff --git a/src/drivers/netlink.h b/src/drivers/netlink.h
new file mode 100644
index 0000000..ccf12a5
--- /dev/null
+++ b/src/drivers/netlink.h
@@ -0,0 +1,34 @@
+/*
+ * Netlink helper functions for driver wrappers
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef NETLINK_H
+#define NETLINK_H
+
+struct netlink_data;
+struct 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/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
new file mode 100644
index 0000000..7483a89
--- /dev/null
+++ b/src/drivers/nl80211_copy.h
@@ -0,0 +1,1939 @@
+#ifndef __LINUX_NL80211_H
+#define __LINUX_NL80211_H
+/*
+ * 802.11 netlink interface public header
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2008 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com>
+ * Copyright 2008 Michael Buesch <mb@bu3sch.de>
+ * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
+ * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
+ * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <linux/types.h>
+
+/**
+ * DOC: Station handling
+ *
+ * Stations are added per interface, but a special case exists with VLAN
+ * interfaces. When a station is bound to an AP interface, it may be moved
+ * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN).
+ * The station is still assumed to belong to the AP interface it was added
+ * to.
+ *
+ * TODO: need more info?
+ */
+
+/**
+ * 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.
+ */
+
+/**
+ * enum nl80211_commands - supported nl80211 commands
+ *
+ * @NL80211_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request
+ * to get a list of all present wiphys.
+ * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
+ * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
+ * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ,
+ * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT,
+ * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+ * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
+ * 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: retrieve beacon information (returned in a
+ * %NL80222_CMD_NEW_BEACON message)
+ * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface
+ * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD,
+ * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes.
+ * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface,
+ * parameters are like for %NL80211_CMD_SET_BEACON.
+ * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it
+ *
+ * @NL80211_CMD_GET_STATION: Get station attributes for station identified by
+ * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_STATION: Set station attributes for station identified by
+ * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the
+ * the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
+ * or, if no MAC address given, all stations, on the interface identified
+ * by %NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
+ * destination %NL80211_ATTR_MAC on the interface identified by
+ * %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to
+ * destination %NL80211_ATTR_MAC on the interface identified by
+ * %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
+ * the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
+ * or, if no MAC address given, all mesh paths, on the interface identified
+ * by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by
+ * %NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
+ * regulatory domain.
+ * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
+ * after being queried by the kernel. CRDA replies by sending a regulatory
+ * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
+ * current alpha2 if it found a match. It also provides
+ * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each
+ * regulatory rule is a nested set of attributes given by
+ * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and
+ * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by
+ * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
+ * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
+ * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain
+ * to the specified ISO/IEC 3166-1 alpha2 country code. The core will
+ * store this as a valid request and then query userspace for it.
+ *
+ * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the
+ * interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the
+ * interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The
+ * interface is identified with %NL80211_ATTR_IFINDEX and the management
+ * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be
+ * added to the end of the specified management frame is specified with
+ * %NL80211_ATTR_IE. If the command succeeds, the requested data will be
+ * added to all specified management frames generated by
+ * kernel/firmware/driver.
+ * Note: This command has been removed and it is only reserved at this
+ * point to avoid re-using existing command number. The functionality this
+ * command was planned for has been provided with cleaner design with the
+ * option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN,
+ * NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE,
+ * NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE.
+ *
+ * @NL80211_CMD_GET_SCAN: get scan results
+ * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
+ * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
+ * NL80211_CMD_GET_SCAN and on the "scan" multicast group)
+ * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
+ * partial scan results may be available
+ *
+ * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
+ * or noise level
+ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
+ * NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
+ *
+ * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
+ * has been changed and provides details of the request information
+ * that caused the change such as who initiated the regulatory request
+ * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx
+ * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if
+ * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or
+ * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain
+ * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is
+ * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
+ * to (%NL80211_ATTR_REG_ALPHA2).
+ * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon
+ * has been found while world roaming thus enabling active scan or
+ * any mode of operation that initiates TX (beacons) on a channel
+ * where we would not have been able to do either before. As an example
+ * if you are world roaming (regulatory domain set to world or if your
+ * driver is using a custom world roaming regulatory domain) and while
+ * doing a passive scan on the 5 GHz band you find an AP there (if not
+ * on a DFS channel) you will now be able to actively scan for that AP
+ * or use AP mode on your card on that same channel. Note that this will
+ * never be used for channels 1-11 on the 2 GHz band as they are always
+ * enabled world wide. This beacon hint is only sent if your device had
+ * either disabled active scanning or beaconing on a channel. We send to
+ * userspace the wiphy on which we removed a restriction from
+ * (%NL80211_ATTR_WIPHY) and the channel on which this occurred
+ * before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER)
+ * the beacon hint was processed.
+ *
+ * @NL80211_CMD_AUTHENTICATE: authentication request and notification.
+ * This command is used both as a command (request to authenticate) and
+ * as an event on the "mlme" multicast group indicating completion of the
+ * authentication process.
+ * When used as a command, %NL80211_ATTR_IFINDEX is used to identify the
+ * interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and
+ * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify
+ * the SSID (mainly for association, but is included in authentication
+ * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used
+ * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE
+ * is used to specify the authentication type. %NL80211_ATTR_IE is used to
+ * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs)
+ * to be added to the frame.
+ * When used as an event, this reports reception of an Authentication
+ * frame in station and IBSS modes when the local MLME processed the
+ * frame, i.e., it was for the local STA and was received in correct
+ * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the
+ * MLME SAP interface (kernel providing MLME, userspace SME). The
+ * included %NL80211_ATTR_FRAME attribute contains the management frame
+ * (including both the header and frame body, but not FCS). This event is
+ * also used to indicate if the authentication attempt timed out. In that
+ * case the %NL80211_ATTR_FRAME attribute is replaced with a
+ * %NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which
+ * pending authentication timed out).
+ * @NL80211_CMD_ASSOCIATE: association request and notification; like
+ * NL80211_CMD_AUTHENTICATE but for Association and Reassociation
+ * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request,
+ * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives).
+ * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like
+ * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to
+ * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication
+ * primitives).
+ * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like
+ * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to
+ * MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives).
+ *
+ * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael
+ * MIC (part of TKIP) failure; sent on the "mlme" multicast group; the
+ * event includes %NL80211_ATTR_MAC to describe the source MAC address of
+ * the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key
+ * type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and
+ * %NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this
+ * event matches with MLME-MICHAELMICFAILURE.indication() primitive
+ *
+ * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a
+ * FREQ attribute (for the initial frequency if no peer can be found)
+ * and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those
+ * should be fixed rather than automatically determined. Can only be
+ * executed on a network interface that is UP, and fixed BSSID/FREQ
+ * may be rejected. Another optional parameter is the beacon interval,
+ * given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not
+ * given defaults to 100 TU (102.4ms).
+ * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is
+ * determined by the network interface.
+ *
+ * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute
+ * to identify the device, and the TESTDATA blob attribute to pass through
+ * to the driver.
+ *
+ * @NL80211_CMD_CONNECT: connection request and notification; this command
+ * requests to connect to a specified network but without separating
+ * auth and assoc steps. For this, you need to specify the SSID in a
+ * %NL80211_ATTR_SSID attribute, and can optionally specify the association
+ * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC,
+ * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
+ * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ * 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 and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be
+ * optionally used to specify additional channel parameters.
+ * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds
+ * to remain on the channel. This command is also used as an event to
+ * notify when the requested duration starts (it may take a while for the
+ * driver to schedule this time due to other concurrent needs for the
+ * radio).
+ * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE)
+ * that will be included with any events pertaining to this request;
+ * the cookie is also used to cancel the request.
+ * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a
+ * pending remain-on-channel duration if the desired operation has been
+ * completed prior to expiration of the originally requested duration.
+ * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the
+ * radio. The %NL80211_ATTR_COOKIE attribute must be given as well to
+ * uniquely identify the request.
+ * This command is also used as an event to notify when a requested
+ * remain-on-channel duration has expired.
+ *
+ * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX
+ * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface
+ * and @NL80211_ATTR_TX_RATES the set of allowed rates.
+ *
+ * @NL80211_CMD_REGISTER_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 (and
+ * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) 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_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_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 %NL80211_ATTR_WIPHY_CHANNEL_TYPE) 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_MAX: highest used command number
+ * @__NL80211_CMD_AFTER_LAST: internal use
+ */
+enum nl80211_commands {
+/* don't change the order or add anything inbetween, this is ABI! */
+ NL80211_CMD_UNSPEC,
+
+ NL80211_CMD_GET_WIPHY, /* can dump */
+ NL80211_CMD_SET_WIPHY,
+ NL80211_CMD_NEW_WIPHY,
+ NL80211_CMD_DEL_WIPHY,
+
+ NL80211_CMD_GET_INTERFACE, /* can dump */
+ NL80211_CMD_SET_INTERFACE,
+ NL80211_CMD_NEW_INTERFACE,
+ NL80211_CMD_DEL_INTERFACE,
+
+ NL80211_CMD_GET_KEY,
+ NL80211_CMD_SET_KEY,
+ NL80211_CMD_NEW_KEY,
+ NL80211_CMD_DEL_KEY,
+
+ NL80211_CMD_GET_BEACON,
+ NL80211_CMD_SET_BEACON,
+ NL80211_CMD_NEW_BEACON,
+ NL80211_CMD_DEL_BEACON,
+
+ NL80211_CMD_GET_STATION,
+ NL80211_CMD_SET_STATION,
+ NL80211_CMD_NEW_STATION,
+ NL80211_CMD_DEL_STATION,
+
+ NL80211_CMD_GET_MPATH,
+ NL80211_CMD_SET_MPATH,
+ NL80211_CMD_NEW_MPATH,
+ NL80211_CMD_DEL_MPATH,
+
+ NL80211_CMD_SET_BSS,
+
+ NL80211_CMD_SET_REG,
+ NL80211_CMD_REQ_SET_REG,
+
+ NL80211_CMD_GET_MESH_PARAMS,
+ NL80211_CMD_SET_MESH_PARAMS,
+
+ NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */,
+
+ NL80211_CMD_GET_REG,
+
+ NL80211_CMD_GET_SCAN,
+ NL80211_CMD_TRIGGER_SCAN,
+ NL80211_CMD_NEW_SCAN_RESULTS,
+ NL80211_CMD_SCAN_ABORTED,
+
+ NL80211_CMD_REG_CHANGE,
+
+ NL80211_CMD_AUTHENTICATE,
+ NL80211_CMD_ASSOCIATE,
+ NL80211_CMD_DEAUTHENTICATE,
+ NL80211_CMD_DISASSOCIATE,
+
+ NL80211_CMD_MICHAEL_MIC_FAILURE,
+
+ NL80211_CMD_REG_BEACON_HINT,
+
+ NL80211_CMD_JOIN_IBSS,
+ NL80211_CMD_LEAVE_IBSS,
+
+ NL80211_CMD_TESTMODE,
+
+ NL80211_CMD_CONNECT,
+ NL80211_CMD_ROAM,
+ NL80211_CMD_DISCONNECT,
+
+ NL80211_CMD_SET_WIPHY_NETNS,
+
+ NL80211_CMD_GET_SURVEY,
+ NL80211_CMD_NEW_SURVEY_RESULTS,
+
+ NL80211_CMD_SET_PMKSA,
+ NL80211_CMD_DEL_PMKSA,
+ NL80211_CMD_FLUSH_PMKSA,
+
+ NL80211_CMD_REMAIN_ON_CHANNEL,
+ NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
+
+ NL80211_CMD_SET_TX_BITRATE_MASK,
+
+ NL80211_CMD_REGISTER_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,
+
+ /* add new commands above here */
+
+ /* used to define NL80211_CMD_MAX below */
+ __NL80211_CMD_AFTER_LAST,
+ NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
+};
+
+/*
+ * Allow user space programs to use #ifdef on new commands by defining them
+ * here
+ */
+#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS
+#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE
+#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE
+#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE
+#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE
+#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE
+#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE
+#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT
+
+/**
+ * enum nl80211_attrs - nl80211 netlink attributes
+ *
+ * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf.
+ * /sys/class/ieee80211/<phyname>/index
+ * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming)
+ * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters
+ * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz
+ * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
+ * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included):
+ * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including
+ * this attribute)
+ * NL80211_CHAN_HT20 = HT20 only
+ * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel
+ * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel
+ * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is
+ * less than or equal to the RTS threshold; allowed range: 1..255;
+ * dot11ShortRetryLimit; u8
+ * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is
+ * greater than the RTS threshold; allowed range: 1..255;
+ * dot11ShortLongLimit; u8
+ * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum
+ * length in octets for frames; allowed range: 256..8000, disable
+ * fragmentation with (u32)-1; dot11FragmentationThreshold; u32
+ * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length
+ * larger than or equal to this use RTS/CTS handshake); allowed range:
+ * 0..65536, disable with (u32)-1; dot11RTSThreshold; u32
+ * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11
+ * section 7.3.2.9; dot11CoverageClass; u8
+ *
+ * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on
+ * @NL80211_ATTR_IFNAME: network interface name
+ * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype
+ *
+ * @NL80211_ATTR_MAC: MAC address (various uses)
+ *
+ * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of
+ * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC
+ * keys
+ * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3)
+ * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11
+ * section 7.3.2.25.1, e.g. 0x000FAC04)
+ * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
+ * CCMP keys, each six bytes in little endian
+ *
+ * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU
+ * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing
+ * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE
+ * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE
+ *
+ * @NL80211_ATTR_STA_AID: Association ID for the station (u16)
+ * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of
+ * &enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2)
+ * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by
+ * IEEE 802.11 7.3.1.6 (u16).
+ * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported
+ * rates as defined by IEEE 802.11 7.3.2.2 but without the length
+ * restriction (at most %NL80211_MAX_SUPP_RATES).
+ * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station
+ * to, or the AP interface the station was originally added to to.
+ * @NL80211_ATTR_STA_INFO: information about a station, part of station info
+ * given for %NL80211_CMD_GET_STATION, nested attribute containing
+ * info as possible, see &enum nl80211_sta_info.
+ *
+ * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands,
+ * consisting of a nested array.
+ *
+ * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
+ * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link.
+ * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
+ * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
+ * info given for %NL80211_CMD_GET_MPATH, nested attribute described at
+ * &enum nl80211_mpath_info.
+ *
+ * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
+ * &enum nl80211_mntr_flags.
+ *
+ * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the
+ * current regulatory domain should be set to or is already set to.
+ * For example, 'CR', for Costa Rica. This attribute is used by the kernel
+ * to query the CRDA to retrieve one regulatory domain. This attribute can
+ * also be used by userspace to query the kernel for the currently set
+ * regulatory domain. We chose an alpha2 as that is also used by the
+ * IEEE-802.11d country information element to identify a country.
+ * Users can also simply ask the wireless core to set regulatory domain
+ * to a specific alpha2.
+ * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
+ * rules.
+ *
+ * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled
+ * (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled
+ * (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic
+ * rates in format defined by IEEE 802.11 7.3.2.2 but without the length
+ * restriction (at most %NL80211_MAX_SUPP_RATES).
+ *
+ * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from
+ * association request when used with NL80211_CMD_NEW_STATION)
+ *
+ * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all
+ * supported interface types, each a flag attribute with the number
+ * of the interface mode.
+ *
+ * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for
+ * %NL80211_CMD_SET_MGMT_EXTRA_IE.
+ *
+ * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with
+ * %NL80211_CMD_SET_MGMT_EXTRA_IE).
+ *
+ * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with
+ * a single scan request, a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements
+ * that can be added to a scan request
+ *
+ * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz)
+ * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive
+ * scanning and include a zero-length SSID (wildcard) for wildcard scan
+ * @NL80211_ATTR_BSS: scan result BSS
+ *
+ * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain
+ * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_*
+ * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently
+ * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
+ *
+ * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies
+ * an array of command numbers (i.e. a mapping index to command number)
+ * that the driver for the given wiphy supports.
+ *
+ * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header
+ * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and
+ * NL80211_CMD_ASSOCIATE events
+ * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets)
+ * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type,
+ * represented as a u32
+ * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and
+ * %NL80211_CMD_DISASSOCIATE, u16
+ *
+ * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as
+ * a u32
+ *
+ * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change
+ * due to considerations from a beacon hint. This attribute reflects
+ * the state of the channel _before_ the beacon hint processing. This
+ * attributes consists of a nested attribute containing
+ * NL80211_FREQUENCY_ATTR_*
+ * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change
+ * due to considerations from a beacon hint. This attribute reflects
+ * the state of the channel _after_ the beacon hint processing. This
+ * attributes consists of a nested attribute containing
+ * NL80211_FREQUENCY_ATTR_*
+ *
+ * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported
+ * cipher suites
+ *
+ * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look
+ * for other networks on different channels
+ *
+ * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this
+ * is used, e.g., with %NL80211_CMD_AUTHENTICATE event
+ *
+ * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
+ * used for the association (&enum nl80211_mfp, represented as a u32);
+ * this attribute can be used
+ * with %NL80211_CMD_ASSOCIATE request
+ *
+ * @NL80211_ATTR_STA_FLAGS2: Attribute containing a
+ * &struct nl80211_sta_flag_update.
+ *
+ * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls
+ * IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in
+ * station mode. If the flag is included in %NL80211_CMD_ASSOCIATE
+ * request, the driver will assume that the port is unauthorized until
+ * authorized by user space. Otherwise, port is marked authorized by
+ * default in station mode.
+ * @NL80211_ATTR_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.
+ *
+ * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT and ASSOCIATE to
+ * indicate which unicast key ciphers will be used with the connection
+ * (an array of u32).
+ * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT and ASSOCIATE to indicate
+ * which group key cipher will be used with the connection (a u32).
+ * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT and ASSOCIATE to indicate
+ * which WPA version(s) the AP we want to associate with is using
+ * (a u32 with flags from &enum nl80211_wpa_versions).
+ * @NL80211_ATTR_AKM_SUITES: Used with CONNECT and ASSOCIATE to indicate
+ * which key management algorithm(s) to use (an array of u32).
+ *
+ * @NL80211_ATTR_REQ_IE: (Re)association request information elements as
+ * sent out by the card, for ROAM and successful CONNECT events.
+ * @NL80211_ATTR_RESP_IE: (Re)association response information elements as
+ * sent by peer, for ROAM and successful CONNECT events.
+ *
+ * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE
+ * commands to specify using a reassociate frame
+ *
+ * @NL80211_ATTR_KEY: key information in a nested attribute with
+ * %NL80211_KEY_* sub-attributes
+ * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect()
+ * and join_ibss(), key information is in a nested attribute each
+ * with %NL80211_KEY_* sub-attributes
+ *
+ * @NL80211_ATTR_PID: Process ID of a network namespace.
+ *
+ * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for
+ * dumps. This number increases whenever the object list being
+ * dumped changes, and as such userspace can verify that it has
+ * obtained a complete and consistent snapshot by verifying that
+ * all dump messages contain the same generation number. If it
+ * changed then the list changed and the dump should be repeated
+ * completely from scratch.
+ *
+ * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface
+ *
+ * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of
+ * the survey response for %NL80211_CMD_GET_SURVEY, nested attribute
+ * containing info as possible, see &enum survey_info.
+ *
+ * @NL80211_ATTR_PMKID: PMK material for PMKSA caching.
+ * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can
+ * cache, a wiphy attribute.
+ *
+ * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32.
+ * @NL80211_ATTR_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_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 descripton see @NL80211_ATTR_WIPHY_ANTENNA_TX.
+ *
+ * @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_HTOPMODE: 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_MAX: highest attribute number currently defined
+ * @__NL80211_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_attrs {
+/* don't change the order or add anything inbetween, this is ABI! */
+ NL80211_ATTR_UNSPEC,
+
+ NL80211_ATTR_WIPHY,
+ NL80211_ATTR_WIPHY_NAME,
+
+ NL80211_ATTR_IFINDEX,
+ NL80211_ATTR_IFNAME,
+ NL80211_ATTR_IFTYPE,
+
+ NL80211_ATTR_MAC,
+
+ NL80211_ATTR_KEY_DATA,
+ NL80211_ATTR_KEY_IDX,
+ NL80211_ATTR_KEY_CIPHER,
+ NL80211_ATTR_KEY_SEQ,
+ NL80211_ATTR_KEY_DEFAULT,
+
+ NL80211_ATTR_BEACON_INTERVAL,
+ NL80211_ATTR_DTIM_PERIOD,
+ NL80211_ATTR_BEACON_HEAD,
+ NL80211_ATTR_BEACON_TAIL,
+
+ NL80211_ATTR_STA_AID,
+ NL80211_ATTR_STA_FLAGS,
+ NL80211_ATTR_STA_LISTEN_INTERVAL,
+ NL80211_ATTR_STA_SUPPORTED_RATES,
+ NL80211_ATTR_STA_VLAN,
+ NL80211_ATTR_STA_INFO,
+
+ NL80211_ATTR_WIPHY_BANDS,
+
+ NL80211_ATTR_MNTR_FLAGS,
+
+ NL80211_ATTR_MESH_ID,
+ NL80211_ATTR_STA_PLINK_ACTION,
+ NL80211_ATTR_MPATH_NEXT_HOP,
+ NL80211_ATTR_MPATH_INFO,
+
+ NL80211_ATTR_BSS_CTS_PROT,
+ NL80211_ATTR_BSS_SHORT_PREAMBLE,
+ NL80211_ATTR_BSS_SHORT_SLOT_TIME,
+
+ NL80211_ATTR_HT_CAPABILITY,
+
+ NL80211_ATTR_SUPPORTED_IFTYPES,
+
+ NL80211_ATTR_REG_ALPHA2,
+ NL80211_ATTR_REG_RULES,
+
+ NL80211_ATTR_MESH_PARAMS,
+
+ NL80211_ATTR_BSS_BASIC_RATES,
+
+ NL80211_ATTR_WIPHY_TXQ_PARAMS,
+ NL80211_ATTR_WIPHY_FREQ,
+ NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+
+ NL80211_ATTR_KEY_DEFAULT_MGMT,
+
+ NL80211_ATTR_MGMT_SUBTYPE,
+ NL80211_ATTR_IE,
+
+ NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
+
+ NL80211_ATTR_SCAN_FREQUENCIES,
+ NL80211_ATTR_SCAN_SSIDS,
+ NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */
+ NL80211_ATTR_BSS,
+
+ NL80211_ATTR_REG_INITIATOR,
+ NL80211_ATTR_REG_TYPE,
+
+ NL80211_ATTR_SUPPORTED_COMMANDS,
+
+ NL80211_ATTR_FRAME,
+ NL80211_ATTR_SSID,
+ NL80211_ATTR_AUTH_TYPE,
+ NL80211_ATTR_REASON_CODE,
+
+ NL80211_ATTR_KEY_TYPE,
+
+ NL80211_ATTR_MAX_SCAN_IE_LEN,
+ NL80211_ATTR_CIPHER_SUITES,
+
+ NL80211_ATTR_FREQ_BEFORE,
+ NL80211_ATTR_FREQ_AFTER,
+
+ NL80211_ATTR_FREQ_FIXED,
+
+
+ NL80211_ATTR_WIPHY_RETRY_SHORT,
+ NL80211_ATTR_WIPHY_RETRY_LONG,
+ NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+ NL80211_ATTR_WIPHY_RTS_THRESHOLD,
+
+ NL80211_ATTR_TIMED_OUT,
+
+ NL80211_ATTR_USE_MFP,
+
+ NL80211_ATTR_STA_FLAGS2,
+
+ NL80211_ATTR_CONTROL_PORT,
+
+ NL80211_ATTR_TESTDATA,
+
+ NL80211_ATTR_PRIVACY,
+
+ NL80211_ATTR_DISCONNECTED_BY_AP,
+ NL80211_ATTR_STATUS_CODE,
+
+ NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+ NL80211_ATTR_CIPHER_SUITE_GROUP,
+ NL80211_ATTR_WPA_VERSIONS,
+ NL80211_ATTR_AKM_SUITES,
+
+ NL80211_ATTR_REQ_IE,
+ NL80211_ATTR_RESP_IE,
+
+ NL80211_ATTR_PREV_BSSID,
+
+ NL80211_ATTR_KEY,
+ NL80211_ATTR_KEYS,
+
+ NL80211_ATTR_PID,
+
+ NL80211_ATTR_4ADDR,
+
+ NL80211_ATTR_SURVEY_INFO,
+
+ NL80211_ATTR_PMKID,
+ NL80211_ATTR_MAX_NUM_PMKIDS,
+
+ NL80211_ATTR_DURATION,
+
+ NL80211_ATTR_COOKIE,
+
+ NL80211_ATTR_WIPHY_COVERAGE_CLASS,
+
+ NL80211_ATTR_TX_RATES,
+
+ NL80211_ATTR_FRAME_MATCH,
+
+ NL80211_ATTR_ACK,
+
+ NL80211_ATTR_PS_STATE,
+
+ NL80211_ATTR_CQM,
+
+ NL80211_ATTR_LOCAL_STATE_CHANGE,
+
+ 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,
+
+ /* add attributes here, update the policy in nl80211.c */
+
+ __NL80211_ATTR_AFTER_LAST,
+ NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+};
+
+/* source-level API compatibility */
+#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
+
+/*
+ * Allow user space programs to use #ifdef on new attributes by defining them
+ * here
+ */
+#define NL80211_CMD_CONNECT NL80211_CMD_CONNECT
+#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY
+#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES
+#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS
+#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ
+#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE
+#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE
+#define NL80211_ATTR_IE NL80211_ATTR_IE
+#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR
+#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE
+#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME
+#define NL80211_ATTR_SSID NL80211_ATTR_SSID
+#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE
+#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE
+#define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE
+#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP
+#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS
+#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES
+#define NL80211_ATTR_KEY NL80211_ATTR_KEY
+#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS
+
+#define NL80211_MAX_SUPP_RATES 32
+#define NL80211_MAX_SUPP_REG_RULES 32
+#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0
+#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16
+#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24
+#define NL80211_HT_CAPABILITY_LEN 26
+
+#define NL80211_MAX_NR_CIPHER_SUITES 5
+#define NL80211_MAX_NR_AKM_SUITES 2
+
+/**
+ * enum nl80211_iftype - (virtual) interface types
+ *
+ * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides
+ * @NL80211_IFTYPE_ADHOC: independent BSS member
+ * @NL80211_IFTYPE_STATION: managed BSS member
+ * @NL80211_IFTYPE_AP: access point
+ * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points
+ * @NL80211_IFTYPE_WDS: wireless distribution interface
+ * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
+ * @NL80211_IFTYPE_MESH_POINT: mesh point
+ * @NL80211_IFTYPE_P2P_CLIENT: P2P client
+ * @NL80211_IFTYPE_P2P_GO: P2P group owner
+ * @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,
+
+ /* 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_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,
+
+ /* keep last */
+ __NL80211_STA_FLAG_AFTER_LAST,
+ NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
+};
+
+/**
+ * struct nl80211_sta_flag_update - station flags mask/set
+ * @mask: mask of station flags to set
+ * @set: which values to set them to
+ *
+ * Both mask and set contain bits as per &enum nl80211_sta_flags.
+ */
+struct nl80211_sta_flag_update {
+ __u32 mask;
+ __u32 set;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_rate_info - bitrate information
+ *
+ * These attribute types are used with %NL80211_STA_INFO_TXRATE
+ * when getting information about the bitrate of a station.
+ *
+ * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s)
+ * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8)
+ * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate
+ * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval
+ * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
+ * @__NL80211_RATE_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_rate_info {
+ __NL80211_RATE_INFO_INVALID,
+ NL80211_RATE_INFO_BITRATE,
+ NL80211_RATE_INFO_MCS,
+ NL80211_RATE_INFO_40_MHZ_WIDTH,
+ NL80211_RATE_INFO_SHORT_GI,
+
+ /* keep last */
+ __NL80211_RATE_INFO_AFTER_LAST,
+ NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_info - station information
+ *
+ * These attribute types are used with %NL80211_ATTR_STA_INFO
+ * when getting information about a station.
+ *
+ * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
+ * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
+ * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
+ * @__NL80211_STA_INFO_AFTER_LAST: internal
+ * @NL80211_STA_INFO_MAX: highest possible station info attribute
+ * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
+ * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
+ * containing info as possible, see &enum nl80211_sta_info_txrate.
+ * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station)
+ * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this
+ * station)
+ * @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)
+ */
+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,
+
+ /* 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_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,
+
+ /* 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_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,
+
+ /* 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 pased 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_reg_rule_flags - regulatory rule flags
+ *
+ * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed
+ * @NL80211_RRF_NO_CCK: CCK modulation not allowed
+ * @NL80211_RRF_NO_INDOOR: indoor operation not allowed
+ * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed
+ * @NL80211_RRF_DFS: DFS support is required to be used
+ * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links
+ * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links
+ * @NL80211_RRF_PASSIVE_SCAN: passive scan is required
+ * @NL80211_RRF_NO_IBSS: no IBSS is allowed
+ */
+enum nl80211_reg_rule_flags {
+ NL80211_RRF_NO_OFDM = 1<<0,
+ NL80211_RRF_NO_CCK = 1<<1,
+ NL80211_RRF_NO_INDOOR = 1<<2,
+ NL80211_RRF_NO_OUTDOOR = 1<<3,
+ NL80211_RRF_DFS = 1<<4,
+ NL80211_RRF_PTP_ONLY = 1<<5,
+ NL80211_RRF_PTMP_ONLY = 1<<6,
+ NL80211_RRF_PASSIVE_SCAN = 1<<7,
+ NL80211_RRF_NO_IBSS = 1<<8,
+};
+
+/**
+ * enum nl80211_survey_info - survey information
+ *
+ * These attribute types are used with %NL80211_ATTR_SURVEY_INFO
+ * when getting information about a survey.
+ *
+ * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel
+ * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm)
+ * @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_AFTER_LAST: internal use
+ * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag
+ */
+enum nl80211_mntr_flags {
+ __NL80211_MNTR_FLAG_INVALID,
+ NL80211_MNTR_FLAG_FCSFAIL,
+ NL80211_MNTR_FLAG_PLCPFAIL,
+ NL80211_MNTR_FLAG_CONTROL,
+ NL80211_MNTR_FLAG_OTHER_BSS,
+ NL80211_MNTR_FLAG_COOK_FRAMES,
+
+ /* keep last */
+ __NL80211_MNTR_FLAG_AFTER_LAST,
+ NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_meshconf_params - mesh configuration parameters
+ *
+ * Mesh configuration parameters
+ *
+ * @__NL80211_MESHCONF_INVALID: internal use
+ *
+ * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in
+ * millisecond units, used by the Peer Link Open message
+ *
+ * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the inital confirm timeout, in
+ * millisecond units, used by the peer link management to close a peer link
+ *
+ * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in
+ * millisecond units
+ *
+ * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed
+ * on this mesh interface
+ *
+ * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link
+ * open retries that can be sent to establish a new peer link instance in a
+ * mesh
+ *
+ * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
+ * point.
+ *
+ * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
+ * source mesh point for path selection elements.
+ *
+ * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically
+ * open peer links when we detect compatible mesh peers.
+ *
+ * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames
+ * containing a PREQ that an MP can send to a particular destination (path
+ * target)
+ *
+ * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths
+ * (in milliseconds)
+ *
+ * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait
+ * until giving up on a path discovery (in milliseconds)
+ *
+ * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh
+ * points receiving a PREQ shall consider the forwarding information from the
+ * root to be valid. (TU = time unit)
+ *
+ * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in
+ * TUs) during which an MP can send only one action frame containing a PREQ
+ * reference element
+ *
+ * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs)
+ * that it takes for an HWMP information element to propagate across the mesh
+ *
+ * @NL80211_MESHCONF_ROOTMODE: whether root mode is enabled or not
+ *
+ * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
+ *
+ * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_meshconf_params {
+ __NL80211_MESHCONF_INVALID,
+ NL80211_MESHCONF_RETRY_TIMEOUT,
+ NL80211_MESHCONF_CONFIRM_TIMEOUT,
+ NL80211_MESHCONF_HOLDING_TIMEOUT,
+ NL80211_MESHCONF_MAX_PEER_LINKS,
+ NL80211_MESHCONF_MAX_RETRIES,
+ NL80211_MESHCONF_TTL,
+ NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+ NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+ NL80211_MESHCONF_PATH_REFRESH_TIME,
+ NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+ NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+ NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+ NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ NL80211_MESHCONF_HWMP_ROOTMODE,
+ NL80211_MESHCONF_ELEMENT_TTL,
+
+ /* keep last */
+ __NL80211_MESHCONF_ATTR_AFTER_LAST,
+ NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_txq_attr - TX queue parameter attributes
+ * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
+ * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*)
+ * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning
+ * disabled
+ * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form
+ * 2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form
+ * 2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255]
+ * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal
+ * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number
+ */
+enum nl80211_txq_attr {
+ __NL80211_TXQ_ATTR_INVALID,
+ NL80211_TXQ_ATTR_QUEUE,
+ NL80211_TXQ_ATTR_TXOP,
+ NL80211_TXQ_ATTR_CWMIN,
+ NL80211_TXQ_ATTR_CWMAX,
+ NL80211_TXQ_ATTR_AIFS,
+
+ /* keep last */
+ __NL80211_TXQ_ATTR_AFTER_LAST,
+ NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1
+};
+
+enum nl80211_txq_q {
+ NL80211_TXQ_Q_VO,
+ NL80211_TXQ_Q_VI,
+ NL80211_TXQ_Q_BE,
+ NL80211_TXQ_Q_BK
+};
+
+enum nl80211_channel_type {
+ NL80211_CHAN_NO_HT,
+ NL80211_CHAN_HT20,
+ NL80211_CHAN_HT40MINUS,
+ NL80211_CHAN_HT40PLUS
+};
+
+/**
+ * enum nl80211_bss - netlink attributes for a BSS
+ *
+ * @__NL80211_BSS_INVALID: invalid
+ * @NL80211_BSS_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_AFTER_LAST: internal
+ * @NL80211_BSS_MAX: highest BSS attribute
+ */
+enum nl80211_bss {
+ __NL80211_BSS_INVALID,
+ NL80211_BSS_BSSID,
+ NL80211_BSS_FREQUENCY,
+ NL80211_BSS_TSF,
+ NL80211_BSS_BEACON_INTERVAL,
+ NL80211_BSS_CAPABILITY,
+ NL80211_BSS_INFORMATION_ELEMENTS,
+ NL80211_BSS_SIGNAL_MBM,
+ NL80211_BSS_SIGNAL_UNSPEC,
+ NL80211_BSS_STATUS,
+ NL80211_BSS_SEEN_MS_AGO,
+ NL80211_BSS_BEACON_IES,
+
+ /* keep last */
+ __NL80211_BSS_AFTER_LAST,
+ NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_bss_status - BSS "status"
+ * @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_NUM: internal
+ * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm
+ * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by
+ * trying multiple times); this is invalid in netlink -- leave out
+ * the attribute for this on CONNECT commands.
+ */
+enum nl80211_auth_type {
+ NL80211_AUTHTYPE_OPEN_SYSTEM,
+ NL80211_AUTHTYPE_SHARED_KEY,
+ NL80211_AUTHTYPE_FT,
+ NL80211_AUTHTYPE_NETWORK_EAP,
+
+ /* keep last */
+ __NL80211_AUTHTYPE_NUM,
+ NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1,
+ NL80211_AUTHTYPE_AUTOMATIC
+};
+
+/**
+ * enum nl80211_key_type - Key Type
+ * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key
+ * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key
+ * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS)
+ * @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_AFTER_LAST: internal
+ * @NL80211_TXRATE_MAX: highest TX rate attribute
+ */
+enum nl80211_tx_rate_attributes {
+ __NL80211_TXRATE_INVALID,
+ NL80211_TXRATE_LEGACY,
+
+ /* keep last */
+ __NL80211_TXRATE_AFTER_LAST,
+ NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_band - Frequency band
+ * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
+ * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
+ */
+enum nl80211_band {
+ NL80211_BAND_2GHZ,
+ NL80211_BAND_5GHZ,
+};
+
+enum nl80211_ps_state {
+ NL80211_PS_DISABLED,
+ NL80211_PS_ENABLED,
+};
+
+/**
+ * enum nl80211_attr_cqm - connection quality monitor attributes
+ * @__NL80211_ATTR_CQM_INVALID: invalid
+ * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
+ * the threshold for the RSSI level at which an event will be sent. Zero
+ * to disable.
+ * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
+ * the minimum amount the RSSI level must change after an event before a
+ * new event may be issued (to reduce effects of RSSI oscillation).
+ * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
+ * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many
+ * consecutive packets were not acknowledged by the peer
+ * @__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,
+
+ /* keep last */
+ __NL80211_ATTR_CQM_AFTER_LAST,
+ NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the
+ * configured threshold
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the
+ * configured threshold
+ */
+enum nl80211_cqm_rssi_threshold_event {
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+};
+
+
+/**
+ * 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,
+};
+
+#endif /* __LINUX_NL80211_H */
diff --git a/src/drivers/priv_netlink.h b/src/drivers/priv_netlink.h
new file mode 100644
index 0000000..23eff83
--- /dev/null
+++ b/src/drivers/priv_netlink.h
@@ -0,0 +1,113 @@
+/*
+ * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions.
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PRIV_NETLINK_H
+#define PRIV_NETLINK_H
+
+/*
+ * This should be replaced with user space header once one is available with C
+ * library, etc..
+ */
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
+#endif
+#ifndef IFF_DORMANT
+#define IFF_DORMANT 0x20000 /* driver signals dormant */
+#endif
+
+#ifndef IFLA_IFNAME
+#define IFLA_IFNAME 3
+#endif
+#ifndef IFLA_WIRELESS
+#define IFLA_WIRELESS 11
+#endif
+#ifndef IFLA_OPERSTATE
+#define IFLA_OPERSTATE 16
+#endif
+#ifndef IFLA_LINKMODE
+#define IFLA_LINKMODE 17
+#define IF_OPER_DORMANT 5
+#define IF_OPER_UP 6
+#endif
+
+#define NLM_F_REQUEST 1
+
+#define NETLINK_ROUTE 0
+#define RTMGRP_LINK 1
+#define RTM_BASE 0x10
+#define RTM_NEWLINK (RTM_BASE + 0)
+#define RTM_DELLINK (RTM_BASE + 1)
+#define RTM_SETLINK (RTM_BASE + 3)
+
+#define NLMSG_ALIGNTO 4
+#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1))
+#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0)))
+#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+ (struct nlmsghdr *) \
+ (((char *)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+#define NLMSG_OK(nlh,len) ((len) >= (int) sizeof(struct nlmsghdr) && \
+ (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+ (int) (nlh)->nlmsg_len <= (len))
+#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define RTA_ALIGNTO 4
+#define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1))
+#define RTA_OK(rta,len) \
+((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \
+(rta)->rta_len <= (len))
+#define RTA_NEXT(rta,attrlen) \
+((attrlen) -= RTA_ALIGN((rta)->rta_len), \
+(struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))
+#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
+#define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0)))
+
+
+struct sockaddr_nl
+{
+ sa_family_t nl_family;
+ unsigned short nl_pad;
+ u32 nl_pid;
+ u32 nl_groups;
+};
+
+struct nlmsghdr
+{
+ u32 nlmsg_len;
+ u16 nlmsg_type;
+ u16 nlmsg_flags;
+ u32 nlmsg_seq;
+ u32 nlmsg_pid;
+};
+
+struct ifinfomsg
+{
+ unsigned char ifi_family;
+ unsigned char __ifi_pad;
+ unsigned short ifi_type;
+ int ifi_index;
+ unsigned ifi_flags;
+ unsigned ifi_change;
+};
+
+struct rtattr
+{
+ unsigned short rta_len;
+ unsigned short rta_type;
+};
+
+#endif /* PRIV_NETLINK_H */
diff --git a/src/drivers/rfkill.c b/src/drivers/rfkill.c
new file mode 100644
index 0000000..8818311
--- /dev/null
+++ b/src/drivers/rfkill.c
@@ -0,0 +1,194 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <fcntl.h>
+
+#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/src/drivers/rfkill.h b/src/drivers/rfkill.h
new file mode 100644
index 0000000..7a984a6
--- /dev/null
+++ b/src/drivers/rfkill.h
@@ -0,0 +1,31 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#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/src/drivers/wireless_copy.h b/src/drivers/wireless_copy.h
new file mode 100644
index 0000000..201719b
--- /dev/null
+++ b/src/drivers/wireless_copy.h
@@ -0,0 +1,1185 @@
+/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 22.
+ * I have just removed kernel related headers and added some typedefs etc. to
+ * make this easier to include into user space programs.
+ * Jouni Malinen, 2005-03-12.
+ */
+
+
+/*
+ * This file define a set of standard wireless extensions
+ *
+ * Version : 22 16.3.07
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
+ */
+
+#ifndef _LINUX_WIRELESS_H
+#define _LINUX_WIRELESS_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * Initial APIs (1996 -> onward) :
+ * -----------------------------
+ * Basically, the wireless extensions are for now a set of standard ioctl
+ * call + /proc/net/wireless
+ *
+ * The entry /proc/net/wireless give statistics and information on the
+ * driver.
+ * This is better than having each driver having its entry because
+ * its centralised and we may remove the driver module safely.
+ *
+ * Ioctl are used to configure the driver and issue commands. This is
+ * better than command line options of insmod because we may want to
+ * change dynamically (while the driver is running) some parameters.
+ *
+ * The ioctl mechanimsm are copied from standard devices ioctl.
+ * We have the list of command plus a structure descibing the
+ * data exchanged...
+ * Note that to add these ioctl, I was obliged to modify :
+ * # net/core/dev.c (two place + add include)
+ * # net/ipv4/af_inet.c (one place + add include)
+ *
+ * /proc/net/wireless is a copy of /proc/net/dev.
+ * We have a structure for data passed from the driver to /proc/net/wireless
+ * Too add this, I've modified :
+ * # net/core/dev.c (two other places)
+ * # include/linux/netdevice.h (one place)
+ * # include/linux/proc_fs.h (one place)
+ *
+ * New driver API (2002 -> onward) :
+ * -------------------------------
+ * This file is only concerned with the user space API and common definitions.
+ * The new driver API is defined and documented in :
+ * # include/net/iw_handler.h
+ *
+ * Note as well that /proc/net/wireless implementation has now moved in :
+ * # net/core/wireless.c
+ *
+ * Wireless Events (2002 -> onward) :
+ * --------------------------------
+ * Events are defined at the end of this file, and implemented in :
+ * # net/core/wireless.c
+ *
+ * Other comments :
+ * --------------
+ * Do not add here things that are redundant with other mechanisms
+ * (drivers init, ifconfig, /proc/net/dev, ...) and with are not
+ * wireless specific.
+ *
+ * These wireless extensions are not magic : each driver has to provide
+ * support for them...
+ *
+ * IMPORTANT NOTE : As everything in the kernel, this is very much a
+ * work in progress. Contact me if you have ideas of improvements...
+ */
+
+/***************************** INCLUDES *****************************/
+
+ /* jkm - replaced linux headers with C library headers, added typedefs */
+#if 0
+#include <linux/types.h> /* for __u* and __s* typedefs */
+#include <linux/socket.h> /* for "struct sockaddr" et al */
+#include <linux/if.h> /* for IFNAMSIZ and co... */
+#else
+#include <sys/types.h>
+#include <net/if.h>
+#ifndef ANDROID
+typedef __uint32_t __u32;
+typedef __int32_t __s32;
+typedef __uint16_t __u16;
+typedef __int16_t __s16;
+typedef __uint8_t __u8;
+#endif /* ANDROID */
+#ifndef __user
+#define __user
+#endif /* __user */
+#endif
+
+/***************************** VERSION *****************************/
+/*
+ * This constant is used to know the availability of the wireless
+ * extensions and to know which version of wireless extensions it is
+ * (there is some stuff that will be added in the future...)
+ * I just plan to increment with each new version.
+ */
+#define WIRELESS_EXT 22
+
+/*
+ * Changes :
+ *
+ * V2 to V3
+ * --------
+ * Alan Cox start some incompatibles changes. I've integrated a bit more.
+ * - Encryption renamed to Encode to avoid US regulation problems
+ * - Frequency changed from float to struct to avoid problems on old 386
+ *
+ * V3 to V4
+ * --------
+ * - Add sensitivity
+ *
+ * V4 to V5
+ * --------
+ * - Missing encoding definitions in range
+ * - Access points stuff
+ *
+ * V5 to V6
+ * --------
+ * - 802.11 support (ESSID ioctls)
+ *
+ * V6 to V7
+ * --------
+ * - define IW_ESSID_MAX_SIZE and IW_MAX_AP
+ *
+ * V7 to V8
+ * --------
+ * - Changed my e-mail address
+ * - More 802.11 support (nickname, rate, rts, frag)
+ * - List index in frequencies
+ *
+ * V8 to V9
+ * --------
+ * - Support for 'mode of operation' (ad-hoc, managed...)
+ * - Support for unicast and multicast power saving
+ * - Change encoding to support larger tokens (>64 bits)
+ * - Updated iw_params (disable, flags) and use it for NWID
+ * - Extracted iw_point from iwreq for clarity
+ *
+ * V9 to V10
+ * ---------
+ * - Add PM capability to range structure
+ * - Add PM modifier : MAX/MIN/RELATIVE
+ * - Add encoding option : IW_ENCODE_NOKEY
+ * - Add TxPower ioctls (work like TxRate)
+ *
+ * V10 to V11
+ * ----------
+ * - Add WE version in range (help backward/forward compatibility)
+ * - Add retry ioctls (work like PM)
+ *
+ * V11 to V12
+ * ----------
+ * - Add SIOCSIWSTATS to get /proc/net/wireless programatically
+ * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space
+ * - Add new statistics (frag, retry, beacon)
+ * - Add average quality (for user space calibration)
+ *
+ * V12 to V13
+ * ----------
+ * - Document creation of new driver API.
+ * - Extract union iwreq_data from struct iwreq (for new driver API).
+ * - Rename SIOCSIWNAME as SIOCSIWCOMMIT
+ *
+ * V13 to V14
+ * ----------
+ * - Wireless Events support : define struct iw_event
+ * - Define additional specific event numbers
+ * - Add "addr" and "param" fields in union iwreq_data
+ * - AP scanning stuff (SIOCSIWSCAN and friends)
+ *
+ * V14 to V15
+ * ----------
+ * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg
+ * - Make struct iw_freq signed (both m & e), add explicit padding
+ * - Add IWEVCUSTOM for driver specific event/scanning token
+ * - Add IW_MAX_GET_SPY for driver returning a lot of addresses
+ * - Add IW_TXPOW_RANGE for range of Tx Powers
+ * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points
+ * - Add IW_MODE_MONITOR for passive monitor
+ *
+ * V15 to V16
+ * ----------
+ * - Increase the number of bitrates in iw_range to 32 (for 802.11g)
+ * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a)
+ * - Reshuffle struct iw_range for increases, add filler
+ * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses
+ * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support
+ * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy"
+ * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index
+ *
+ * V16 to V17
+ * ----------
+ * - Add flags to frequency -> auto/fixed
+ * - Document (struct iw_quality *)->updated, add new flags (INVALID)
+ * - Wireless Event capability in struct iw_range
+ * - Add support for relative TxPower (yick !)
+ *
+ * V17 to V18 (From Jouni Malinen <j@w1.fi>)
+ * ----------
+ * - Add support for WPA/WPA2
+ * - Add extended encoding configuration (SIOCSIWENCODEEXT and
+ * SIOCGIWENCODEEXT)
+ * - Add SIOCSIWGENIE/SIOCGIWGENIE
+ * - Add SIOCSIWMLME
+ * - Add SIOCSIWPMKSA
+ * - Add struct iw_range bit field for supported encoding capabilities
+ * - Add optional scan request parameters for SIOCSIWSCAN
+ * - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA
+ * related parameters (extensible up to 4096 parameter values)
+ * - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE,
+ * IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND
+ *
+ * V18 to V19
+ * ----------
+ * - Remove (struct iw_point *)->pointer from events and streams
+ * - Remove header includes to help user space
+ * - Increase IW_ENCODING_TOKEN_MAX from 32 to 64
+ * - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros
+ * - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM
+ * - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros
+ *
+ * V19 to V20
+ * ----------
+ * - RtNetlink requests support (SET/GET)
+ *
+ * V20 to V21
+ * ----------
+ * - Remove (struct net_device *)->get_wireless_stats()
+ * - Change length in ESSID and NICK to strlen() instead of strlen()+1
+ * - Add IW_RETRY_SHORT/IW_RETRY_LONG retry modifiers
+ * - Power/Retry relative values no longer * 100000
+ * - Add explicit flag to tell stats are in 802.11k RCPI : IW_QUAL_RCPI
+ *
+ * V21 to V22
+ * ----------
+ * - Prevent leaking of kernel space in stream on 64 bits.
+ */
+
+/**************************** CONSTANTS ****************************/
+
+/* -------------------------- IOCTL LIST -------------------------- */
+
+/* Wireless Identification */
+#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */
+#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */
+/* SIOCGIWNAME is used to verify the presence of Wireless Extensions.
+ * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"...
+ * Don't put the name of your driver there, it's useless. */
+
+/* Basic operations */
+#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */
+#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */
+#define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */
+#define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */
+#define SIOCSIWMODE 0x8B06 /* set operation mode */
+#define SIOCGIWMODE 0x8B07 /* get operation mode */
+#define SIOCSIWSENS 0x8B08 /* set sensitivity (dBm) */
+#define SIOCGIWSENS 0x8B09 /* get sensitivity (dBm) */
+
+/* Informative stuff */
+#define SIOCSIWRANGE 0x8B0A /* Unused */
+#define SIOCGIWRANGE 0x8B0B /* Get range of parameters */
+#define SIOCSIWPRIV 0x8B0C /* Unused */
+#define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */
+#define SIOCSIWSTATS 0x8B0E /* Unused */
+#define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */
+/* SIOCGIWSTATS is strictly used between user space and the kernel, and
+ * is never passed to the driver (i.e. the driver will never see it). */
+
+/* Spy support (statistics per MAC address - used for Mobile IP support) */
+#define SIOCSIWSPY 0x8B10 /* set spy addresses */
+#define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */
+#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */
+#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */
+
+/* Access Point manipulation */
+#define SIOCSIWAP 0x8B14 /* set access point MAC addresses */
+#define SIOCGIWAP 0x8B15 /* get access point MAC addresses */
+#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */
+#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */
+#define SIOCGIWSCAN 0x8B19 /* get scanning results */
+
+/* 802.11 specific support */
+#define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */
+#define SIOCGIWESSID 0x8B1B /* get ESSID */
+#define SIOCSIWNICKN 0x8B1C /* set node name/nickname */
+#define SIOCGIWNICKN 0x8B1D /* get node name/nickname */
+/* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit
+ * within the 'iwreq' structure, so we need to use the 'data' member to
+ * point to a string in user space, like it is done for RANGE... */
+
+/* Other parameters useful in 802.11 and some other devices */
+#define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */
+#define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */
+#define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */
+#define SIOCGIWRTS 0x8B23 /* get RTS/CTS threshold (bytes) */
+#define SIOCSIWFRAG 0x8B24 /* set fragmentation thr (bytes) */
+#define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */
+#define SIOCSIWTXPOW 0x8B26 /* set transmit power (dBm) */
+#define SIOCGIWTXPOW 0x8B27 /* get transmit power (dBm) */
+#define SIOCSIWRETRY 0x8B28 /* set retry limits and lifetime */
+#define SIOCGIWRETRY 0x8B29 /* get retry limits and lifetime */
+
+/* Encoding stuff (scrambling, hardware security, WEP...) */
+#define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */
+#define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */
+/* Power saving stuff (power management, unicast and multicast) */
+#define SIOCSIWPOWER 0x8B2C /* set Power Management settings */
+#define SIOCGIWPOWER 0x8B2D /* get Power Management settings */
+
+/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM).
+ * This ioctl uses struct iw_point and data buffer that includes IE id and len
+ * fields. More than one IE may be included in the request. Setting the generic
+ * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers
+ * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers
+ * are required to report the used IE as a wireless event, e.g., when
+ * associating with an AP. */
+#define SIOCSIWGENIE 0x8B30 /* set generic IE */
+#define SIOCGIWGENIE 0x8B31 /* get generic IE */
+
+/* WPA : IEEE 802.11 MLME requests */
+#define SIOCSIWMLME 0x8B16 /* request MLME operation; uses
+ * struct iw_mlme */
+/* WPA : Authentication mode parameters */
+#define SIOCSIWAUTH 0x8B32 /* set authentication mode params */
+#define SIOCGIWAUTH 0x8B33 /* get authentication mode params */
+
+/* WPA : Extended version of encoding configuration */
+#define SIOCSIWENCODEEXT 0x8B34 /* set encoding token & mode */
+#define SIOCGIWENCODEEXT 0x8B35 /* get encoding token & mode */
+
+/* WPA2 : PMKSA cache management */
+#define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */
+
+/* -------------------- DEV PRIVATE IOCTL LIST -------------------- */
+
+/* These 32 ioctl are wireless device private, for 16 commands.
+ * Each driver is free to use them for whatever purpose it chooses,
+ * however the driver *must* export the description of those ioctls
+ * with SIOCGIWPRIV and *must* use arguments as defined below.
+ * If you don't follow those rules, DaveM is going to hate you (reason :
+ * it make mixed 32/64bit operation impossible).
+ */
+#define SIOCIWFIRSTPRIV 0x8BE0
+#define SIOCIWLASTPRIV 0x8BFF
+/* Previously, we were using SIOCDEVPRIVATE, but we now have our
+ * separate range because of collisions with other tools such as
+ * 'mii-tool'.
+ * We now have 32 commands, so a bit more space ;-).
+ * Also, all 'even' commands are only usable by root and don't return the
+ * content of ifr/iwr to user (but you are not obliged to use the set/get
+ * convention, just use every other two command). More details in iwpriv.c.
+ * And I repeat : you are not forced to use them with iwpriv, but you
+ * must be compliant with it.
+ */
+
+/* ------------------------- IOCTL STUFF ------------------------- */
+
+/* The first and the last (range) */
+#define SIOCIWFIRST 0x8B00
+#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */
+#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST)
+#define IW_HANDLER(id, func) \
+ [IW_IOCTL_IDX(id)] = func
+
+/* Odd : get (world access), even : set (root access) */
+#define IW_IS_SET(cmd) (!((cmd) & 0x1))
+#define IW_IS_GET(cmd) ((cmd) & 0x1)
+
+/* ----------------------- WIRELESS EVENTS ----------------------- */
+/* Those are *NOT* ioctls, do not issue request on them !!! */
+/* Most events use the same identifier as ioctl requests */
+
+#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */
+#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */
+#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */
+#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */
+#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */
+#define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..)
+ * (scan results); This includes id and
+ * length fields. One IWEVGENIE may
+ * contain more than one IE. Scan
+ * results may contain one or more
+ * IWEVGENIE events. */
+#define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure
+ * (struct iw_michaelmicfailure)
+ */
+#define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request.
+ * The data includes id and length
+ * fields and may contain more than one
+ * IE. This event is required in
+ * Managed mode if the driver
+ * generates its own WPA/RSN IE. This
+ * should be sent just before
+ * IWEVREGISTERED event for the
+ * association. */
+#define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association
+ * Response. The data includes id and
+ * length fields and may contain more
+ * than one IE. This may be sent
+ * between IWEVASSOCREQIE and
+ * IWEVREGISTERED events for the
+ * association. */
+#define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN
+ * pre-authentication
+ * (struct iw_pmkid_cand) */
+
+#define IWEVFIRST 0x8C00
+#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST)
+
+/* ------------------------- PRIVATE INFO ------------------------- */
+/*
+ * The following is used with SIOCGIWPRIV. It allow a driver to define
+ * the interface (name, type of data) for its private ioctl.
+ * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV
+ */
+
+#define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */
+#define IW_PRIV_TYPE_NONE 0x0000
+#define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */
+#define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */
+#define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */
+#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */
+#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */
+
+#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */
+
+#define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */
+
+/*
+ * Note : if the number of args is fixed and the size < 16 octets,
+ * instead of passing a pointer we will put args in the iwreq struct...
+ */
+
+/* ----------------------- OTHER CONSTANTS ----------------------- */
+
+/* Maximum frequencies in the range struct */
+#define IW_MAX_FREQUENCIES 32
+/* Note : if you have something like 80 frequencies,
+ * don't increase this constant and don't fill the frequency list.
+ * The user will be able to set by channel anyway... */
+
+/* Maximum bit rates in the range struct */
+#define IW_MAX_BITRATES 32
+
+/* Maximum tx powers in the range struct */
+#define IW_MAX_TXPOWER 8
+/* Note : if you more than 8 TXPowers, just set the max and min or
+ * a few of them in the struct iw_range. */
+
+/* Maximum of address that you may set with SPY */
+#define IW_MAX_SPY 8
+
+/* Maximum of address that you may get in the
+ list of access points in range */
+#define IW_MAX_AP 64
+
+/* Maximum size of the ESSID and NICKN strings */
+#define IW_ESSID_MAX_SIZE 32
+
+/* Modes of operation */
+#define IW_MODE_AUTO 0 /* Let the driver decides */
+#define IW_MODE_ADHOC 1 /* Single cell network */
+#define IW_MODE_INFRA 2 /* Multi cell network, roaming, ... */
+#define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */
+#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */
+#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */
+#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */
+#define IW_MODE_MESH 7 /* Mesh (IEEE 802.11s) network */
+
+/* Statistics flags (bitmask in updated) */
+#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */
+#define IW_QUAL_LEVEL_UPDATED 0x02
+#define IW_QUAL_NOISE_UPDATED 0x04
+#define IW_QUAL_ALL_UPDATED 0x07
+#define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */
+#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */
+#define IW_QUAL_LEVEL_INVALID 0x20
+#define IW_QUAL_NOISE_INVALID 0x40
+#define IW_QUAL_RCPI 0x80 /* Level + Noise are 802.11k RCPI */
+#define IW_QUAL_ALL_INVALID 0x70
+
+/* Frequency flags */
+#define IW_FREQ_AUTO 0x00 /* Let the driver decides */
+#define IW_FREQ_FIXED 0x01 /* Force a specific value */
+
+/* Maximum number of size of encoding token available
+ * they are listed in the range structure */
+#define IW_MAX_ENCODING_SIZES 8
+
+/* Maximum size of the encoding token in bytes */
+#define IW_ENCODING_TOKEN_MAX 64 /* 512 bits (for now) */
+
+/* Flags for encoding (along with the token) */
+#define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */
+#define IW_ENCODE_FLAGS 0xFF00 /* Flags defined below */
+#define IW_ENCODE_MODE 0xF000 /* Modes defined below */
+#define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */
+#define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */
+#define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */
+#define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */
+#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */
+#define IW_ENCODE_TEMP 0x0400 /* Temporary key */
+
+/* Power management flags available (along with the value, if any) */
+#define IW_POWER_ON 0x0000 /* No details... */
+#define IW_POWER_TYPE 0xF000 /* Type of parameter */
+#define IW_POWER_PERIOD 0x1000 /* Value is a period/duration of */
+#define IW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */
+#define IW_POWER_MODE 0x0F00 /* Power Management mode */
+#define IW_POWER_UNICAST_R 0x0100 /* Receive only unicast messages */
+#define IW_POWER_MULTICAST_R 0x0200 /* Receive only multicast messages */
+#define IW_POWER_ALL_R 0x0300 /* Receive all messages though PM */
+#define IW_POWER_FORCE_S 0x0400 /* Force PM procedure for sending unicast */
+#define IW_POWER_REPEATER 0x0800 /* Repeat broadcast messages in PM period */
+#define IW_POWER_MODIFIER 0x000F /* Modify a parameter */
+#define IW_POWER_MIN 0x0001 /* Value is a minimum */
+#define IW_POWER_MAX 0x0002 /* Value is a maximum */
+#define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */
+
+/* Transmit Power flags available */
+#define IW_TXPOW_TYPE 0x00FF /* Type of value */
+#define IW_TXPOW_DBM 0x0000 /* Value is in dBm */
+#define IW_TXPOW_MWATT 0x0001 /* Value is in mW */
+#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */
+#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */
+
+/* Retry limits and lifetime flags available */
+#define IW_RETRY_ON 0x0000 /* No details... */
+#define IW_RETRY_TYPE 0xF000 /* Type of parameter */
+#define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/
+#define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */
+#define IW_RETRY_MODIFIER 0x00FF /* Modify a parameter */
+#define IW_RETRY_MIN 0x0001 /* Value is a minimum */
+#define IW_RETRY_MAX 0x0002 /* Value is a maximum */
+#define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */
+#define IW_RETRY_SHORT 0x0010 /* Value is for short packets */
+#define IW_RETRY_LONG 0x0020 /* Value is for long packets */
+
+/* Scanning request flags */
+#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */
+#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */
+#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */
+#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */
+#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */
+#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */
+#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */
+#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */
+#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */
+/* struct iw_scan_req scan_type */
+#define IW_SCAN_TYPE_ACTIVE 0
+#define IW_SCAN_TYPE_PASSIVE 1
+/* Maximum size of returned data */
+#define IW_SCAN_MAX_DATA 4096 /* In bytes */
+
+/* Scan capability flags - in (struct iw_range *)->scan_capa */
+#define IW_SCAN_CAPA_NONE 0x00
+#define IW_SCAN_CAPA_ESSID 0x01
+#define IW_SCAN_CAPA_BSSID 0x02
+#define IW_SCAN_CAPA_CHANNEL 0x04
+#define IW_SCAN_CAPA_MODE 0x08
+#define IW_SCAN_CAPA_RATE 0x10
+#define IW_SCAN_CAPA_TYPE 0x20
+#define IW_SCAN_CAPA_TIME 0x40
+
+/* Max number of char in custom event - use multiple of them if needed */
+#define IW_CUSTOM_MAX 256 /* In bytes */
+
+/* Generic information element */
+#define IW_GENERIC_IE_MAX 1024
+
+/* MLME requests (SIOCSIWMLME / struct iw_mlme) */
+#define IW_MLME_DEAUTH 0
+#define IW_MLME_DISASSOC 1
+#define IW_MLME_AUTH 2
+#define IW_MLME_ASSOC 3
+
+/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */
+#define IW_AUTH_INDEX 0x0FFF
+#define IW_AUTH_FLAGS 0xF000
+/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095)
+ * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the
+ * parameter that is being set/get to; value will be read/written to
+ * struct iw_param value field) */
+#define IW_AUTH_WPA_VERSION 0
+#define IW_AUTH_CIPHER_PAIRWISE 1
+#define IW_AUTH_CIPHER_GROUP 2
+#define IW_AUTH_KEY_MGMT 3
+#define IW_AUTH_TKIP_COUNTERMEASURES 4
+#define IW_AUTH_DROP_UNENCRYPTED 5
+#define IW_AUTH_80211_AUTH_ALG 6
+#define IW_AUTH_WPA_ENABLED 7
+#define IW_AUTH_RX_UNENCRYPTED_EAPOL 8
+#define IW_AUTH_ROAMING_CONTROL 9
+#define IW_AUTH_PRIVACY_INVOKED 10
+#define IW_AUTH_CIPHER_GROUP_MGMT 11
+#define IW_AUTH_MFP 12
+
+/* IW_AUTH_WPA_VERSION values (bit field) */
+#define IW_AUTH_WPA_VERSION_DISABLED 0x00000001
+#define IW_AUTH_WPA_VERSION_WPA 0x00000002
+#define IW_AUTH_WPA_VERSION_WPA2 0x00000004
+
+/* IW_AUTH_PAIRWISE_CIPHER, IW_AUTH_GROUP_CIPHER, and IW_AUTH_CIPHER_GROUP_MGMT
+ * values (bit field) */
+#define IW_AUTH_CIPHER_NONE 0x00000001
+#define IW_AUTH_CIPHER_WEP40 0x00000002
+#define IW_AUTH_CIPHER_TKIP 0x00000004
+#define IW_AUTH_CIPHER_CCMP 0x00000008
+#define IW_AUTH_CIPHER_WEP104 0x00000010
+#define IW_AUTH_CIPHER_AES_CMAC 0x00000020
+
+/* IW_AUTH_KEY_MGMT values (bit field) */
+#define IW_AUTH_KEY_MGMT_802_1X 1
+#define IW_AUTH_KEY_MGMT_PSK 2
+
+/* IW_AUTH_80211_AUTH_ALG values (bit field) */
+#define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001
+#define IW_AUTH_ALG_SHARED_KEY 0x00000002
+#define IW_AUTH_ALG_LEAP 0x00000004
+
+/* IW_AUTH_ROAMING_CONTROL values */
+#define IW_AUTH_ROAMING_ENABLE 0 /* driver/firmware based roaming */
+#define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming
+ * control */
+
+/* IW_AUTH_MFP (management frame protection) values */
+#define IW_AUTH_MFP_DISABLED 0 /* MFP disabled */
+#define IW_AUTH_MFP_OPTIONAL 1 /* MFP optional */
+#define IW_AUTH_MFP_REQUIRED 2 /* MFP required */
+
+/* SIOCSIWENCODEEXT definitions */
+#define IW_ENCODE_SEQ_MAX_SIZE 8
+/* struct iw_encode_ext ->alg */
+#define IW_ENCODE_ALG_NONE 0
+#define IW_ENCODE_ALG_WEP 1
+#define IW_ENCODE_ALG_TKIP 2
+#define IW_ENCODE_ALG_CCMP 3
+#define IW_ENCODE_ALG_PMK 4
+#define IW_ENCODE_ALG_AES_CMAC 5
+/* struct iw_encode_ext ->ext_flags */
+#define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001
+#define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002
+#define IW_ENCODE_EXT_GROUP_KEY 0x00000004
+#define IW_ENCODE_EXT_SET_TX_KEY 0x00000008
+
+/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */
+#define IW_MICFAILURE_KEY_ID 0x00000003 /* Key ID 0..3 */
+#define IW_MICFAILURE_GROUP 0x00000004
+#define IW_MICFAILURE_PAIRWISE 0x00000008
+#define IW_MICFAILURE_STAKEY 0x00000010
+#define IW_MICFAILURE_COUNT 0x00000060 /* 1 or 2 (0 = count not supported)
+ */
+
+/* Bit field values for enc_capa in struct iw_range */
+#define IW_ENC_CAPA_WPA 0x00000001
+#define IW_ENC_CAPA_WPA2 0x00000002
+#define IW_ENC_CAPA_CIPHER_TKIP 0x00000004
+#define IW_ENC_CAPA_CIPHER_CCMP 0x00000008
+#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010
+
+/* Event capability macros - in (struct iw_range *)->event_capa
+ * Because we have more than 32 possible events, we use an array of
+ * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */
+#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \
+ (cmd - SIOCIWFIRSTPRIV + 0x60) : \
+ (cmd - SIOCIWFIRST))
+#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5)
+#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F))
+/* Event capability constants - event autogenerated by the kernel
+ * This list is valid for most 802.11 devices, customise as needed... */
+#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \
+ IW_EVENT_CAPA_MASK(0x8B06) | \
+ IW_EVENT_CAPA_MASK(0x8B1A))
+#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A))
+/* "Easy" macro to set events in iw_range (less efficient) */
+#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd))
+#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; }
+
+
+/****************************** TYPES ******************************/
+
+/* --------------------------- SUBTYPES --------------------------- */
+/*
+ * Generic format for most parameters that fit in an int
+ */
+struct iw_param
+{
+ __s32 value; /* The value of the parameter itself */
+ __u8 fixed; /* Hardware should not use auto select */
+ __u8 disabled; /* Disable the feature */
+ __u16 flags; /* Various specifc flags (if any) */
+};
+
+/*
+ * For all data larger than 16 octets, we need to use a
+ * pointer to memory allocated in user space.
+ */
+struct iw_point
+{
+ void __user *pointer; /* Pointer to the data (in user space) */
+ __u16 length; /* number of fields or size in bytes */
+ __u16 flags; /* Optional params */
+};
+
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+
+#include <linux/compat.h>
+
+struct compat_iw_point {
+ compat_caddr_t pointer;
+ __u16 length;
+ __u16 flags;
+};
+#endif
+#endif
+
+/*
+ * A frequency
+ * For numbers lower than 10^9, we encode the number in 'm' and
+ * set 'e' to 0
+ * For number greater than 10^9, we divide it by the lowest power
+ * of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')...
+ * The power of 10 is in 'e', the result of the division is in 'm'.
+ */
+struct iw_freq
+{
+ __s32 m; /* Mantissa */
+ __s16 e; /* Exponent */
+ __u8 i; /* List index (when in range struct) */
+ __u8 flags; /* Flags (fixed/auto) */
+};
+
+/*
+ * Quality of the link
+ */
+struct iw_quality
+{
+ __u8 qual; /* link quality (%retries, SNR,
+ %missed beacons or better...) */
+ __u8 level; /* signal level (dBm) */
+ __u8 noise; /* noise level (dBm) */
+ __u8 updated; /* Flags to know if updated */
+};
+
+/*
+ * Packet discarded in the wireless adapter due to
+ * "wireless" specific problems...
+ * Note : the list of counter and statistics in net_device_stats
+ * is already pretty exhaustive, and you should use that first.
+ * This is only additional stats...
+ */
+struct iw_discarded
+{
+ __u32 nwid; /* Rx : Wrong nwid/essid */
+ __u32 code; /* Rx : Unable to code/decode (WEP) */
+ __u32 fragment; /* Rx : Can't perform MAC reassembly */
+ __u32 retries; /* Tx : Max MAC retries num reached */
+ __u32 misc; /* Others cases */
+};
+
+/*
+ * Packet/Time period missed in the wireless adapter due to
+ * "wireless" specific problems...
+ */
+struct iw_missed
+{
+ __u32 beacon; /* Missed beacons/superframe */
+};
+
+/*
+ * Quality range (for spy threshold)
+ */
+struct iw_thrspy
+{
+ struct sockaddr addr; /* Source address (hw/mac) */
+ struct iw_quality qual; /* Quality of the link */
+ struct iw_quality low; /* Low threshold */
+ struct iw_quality high; /* High threshold */
+};
+
+/*
+ * Optional data for scan request
+ *
+ * Note: these optional parameters are controlling parameters for the
+ * scanning behavior, these do not apply to getting scan results
+ * (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and
+ * provide a merged results with all BSSes even if the previous scan
+ * request limited scanning to a subset, e.g., by specifying an SSID.
+ * Especially, scan results are required to include an entry for the
+ * current BSS if the driver is in Managed mode and associated with an AP.
+ */
+struct iw_scan_req
+{
+ __u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */
+ __u8 essid_len;
+ __u8 num_channels; /* num entries in channel_list;
+ * 0 = scan all allowed channels */
+ __u8 flags; /* reserved as padding; use zero, this may
+ * be used in the future for adding flags
+ * to request different scan behavior */
+ struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or
+ * individual address of a specific BSS */
+
+ /*
+ * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using
+ * the current ESSID. This allows scan requests for specific ESSID
+ * without having to change the current ESSID and potentially breaking
+ * the current association.
+ */
+ __u8 essid[IW_ESSID_MAX_SIZE];
+
+ /*
+ * Optional parameters for changing the default scanning behavior.
+ * These are based on the MLME-SCAN.request from IEEE Std 802.11.
+ * TU is 1.024 ms. If these are set to 0, driver is expected to use
+ * reasonable default values. min_channel_time defines the time that
+ * will be used to wait for the first reply on each channel. If no
+ * replies are received, next channel will be scanned after this. If
+ * replies are received, total time waited on the channel is defined by
+ * max_channel_time.
+ */
+ __u32 min_channel_time; /* in TU */
+ __u32 max_channel_time; /* in TU */
+
+ struct iw_freq channel_list[IW_MAX_FREQUENCIES];
+};
+
+/* ------------------------- WPA SUPPORT ------------------------- */
+
+/*
+ * Extended data structure for get/set encoding (this is used with
+ * SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_*
+ * flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and
+ * only the data contents changes (key data -> this structure, including
+ * key data).
+ *
+ * If the new key is the first group key, it will be set as the default
+ * TX key. Otherwise, default TX key index is only changed if
+ * IW_ENCODE_EXT_SET_TX_KEY flag is set.
+ *
+ * Key will be changed with SIOCSIWENCODEEXT in all cases except for
+ * special "change TX key index" operation which is indicated by setting
+ * key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY.
+ *
+ * tx_seq/rx_seq are only used when respective
+ * IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal
+ * TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start
+ * TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally
+ * used only by an Authenticator (AP or an IBSS station) to get the
+ * current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and
+ * RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for
+ * debugging/testing.
+ */
+struct iw_encode_ext
+{
+ __u32 ext_flags; /* IW_ENCODE_EXT_* */
+ __u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */
+ __u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */
+ struct sockaddr addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast
+ * (group) keys or unicast address for
+ * individual keys */
+ __u16 alg; /* IW_ENCODE_ALG_* */
+ __u16 key_len;
+ __u8 key[0];
+};
+
+/* SIOCSIWMLME data */
+struct iw_mlme
+{
+ __u16 cmd; /* IW_MLME_* */
+ __u16 reason_code;
+ struct sockaddr addr;
+};
+
+/* SIOCSIWPMKSA data */
+#define IW_PMKSA_ADD 1
+#define IW_PMKSA_REMOVE 2
+#define IW_PMKSA_FLUSH 3
+
+#define IW_PMKID_LEN 16
+
+struct iw_pmksa
+{
+ __u32 cmd; /* IW_PMKSA_* */
+ struct sockaddr bssid;
+ __u8 pmkid[IW_PMKID_LEN];
+};
+
+/* IWEVMICHAELMICFAILURE data */
+struct iw_michaelmicfailure
+{
+ __u32 flags;
+ struct sockaddr src_addr;
+ __u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */
+};
+
+/* IWEVPMKIDCAND data */
+#define IW_PMKID_CAND_PREAUTH 0x00000001 /* RNS pre-authentication enabled */
+struct iw_pmkid_cand
+{
+ __u32 flags; /* IW_PMKID_CAND_* */
+ __u32 index; /* the smaller the index, the higher the
+ * priority */
+ struct sockaddr bssid;
+};
+
+/* ------------------------ WIRELESS STATS ------------------------ */
+/*
+ * Wireless statistics (used for /proc/net/wireless)
+ */
+struct iw_statistics
+{
+ __u16 status; /* Status
+ * - device dependent for now */
+
+ struct iw_quality qual; /* Quality of the link
+ * (instant/mean/max) */
+ struct iw_discarded discard; /* Packet discarded counts */
+ struct iw_missed miss; /* Packet missed counts */
+};
+
+/* ------------------------ IOCTL REQUEST ------------------------ */
+/*
+ * This structure defines the payload of an ioctl, and is used
+ * below.
+ *
+ * Note that this structure should fit on the memory footprint
+ * of iwreq (which is the same as ifreq), which mean a max size of
+ * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
+ * You should check this when increasing the structures defined
+ * above in this file...
+ */
+union iwreq_data
+{
+ /* Config - generic */
+ char name[IFNAMSIZ];
+ /* Name : used to verify the presence of wireless extensions.
+ * Name of the protocol/provider... */
+
+ struct iw_point essid; /* Extended network name */
+ struct iw_param nwid; /* network id (or domain - the cell) */
+ struct iw_freq freq; /* frequency or channel :
+ * 0-1000 = channel
+ * > 1000 = frequency in Hz */
+
+ struct iw_param sens; /* signal level threshold */
+ struct iw_param bitrate; /* default bit rate */
+ struct iw_param txpower; /* default transmit power */
+ struct iw_param rts; /* RTS threshold threshold */
+ struct iw_param frag; /* Fragmentation threshold */
+ __u32 mode; /* Operation mode */
+ struct iw_param retry; /* Retry limits & lifetime */
+
+ struct iw_point encoding; /* Encoding stuff : tokens */
+ struct iw_param power; /* PM duration/timeout */
+ struct iw_quality qual; /* Quality part of statistics */
+
+ struct sockaddr ap_addr; /* Access point address */
+ struct sockaddr addr; /* Destination address (hw/mac) */
+
+ struct iw_param param; /* Other small parameters */
+ struct iw_point data; /* Other large parameters */
+};
+
+/*
+ * The structure to exchange data for ioctl.
+ * This structure is the same as 'struct ifreq', but (re)defined for
+ * convenience...
+ * Do I need to remind you about structure size (32 octets) ?
+ */
+struct iwreq
+{
+ union
+ {
+ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */
+ } ifr_ifrn;
+
+ /* Data part (defined just above) */
+ union iwreq_data u;
+};
+
+/* -------------------------- IOCTL DATA -------------------------- */
+/*
+ * For those ioctl which want to exchange mode data that what could
+ * fit in the above structure...
+ */
+
+/*
+ * Range of parameters
+ */
+
+struct iw_range
+{
+ /* Informative stuff (to choose between different interface) */
+ __u32 throughput; /* To give an idea... */
+ /* In theory this value should be the maximum benchmarked
+ * TCP/IP throughput, because with most of these devices the
+ * bit rate is meaningless (overhead an co) to estimate how
+ * fast the connection will go and pick the fastest one.
+ * I suggest people to play with Netperf or any benchmark...
+ */
+
+ /* NWID (or domain id) */
+ __u32 min_nwid; /* Minimal NWID we are able to set */
+ __u32 max_nwid; /* Maximal NWID we are able to set */
+
+ /* Old Frequency (backward compat - moved lower ) */
+ __u16 old_num_channels;
+ __u8 old_num_frequency;
+
+ /* Scan capabilities */
+ __u8 scan_capa; /* IW_SCAN_CAPA_* bit field */
+
+ /* Wireless event capability bitmasks */
+ __u32 event_capa[6];
+
+ /* signal level threshold range */
+ __s32 sensitivity;
+
+ /* Quality of link & SNR stuff */
+ /* Quality range (link, level, noise)
+ * If the quality is absolute, it will be in the range [0 ; max_qual],
+ * if the quality is dBm, it will be in the range [max_qual ; 0].
+ * Don't forget that we use 8 bit arithmetics... */
+ struct iw_quality max_qual; /* Quality of the link */
+ /* This should contain the average/typical values of the quality
+ * indicator. This should be the threshold between a "good" and
+ * a "bad" link (example : monitor going from green to orange).
+ * Currently, user space apps like quality monitors don't have any
+ * way to calibrate the measurement. With this, they can split
+ * the range between 0 and max_qual in different quality level
+ * (using a geometric subdivision centered on the average).
+ * I expect that people doing the user space apps will feedback
+ * us on which value we need to put in each driver... */
+ struct iw_quality avg_qual; /* Quality of the link */
+
+ /* Rates */
+ __u8 num_bitrates; /* Number of entries in the list */
+ __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */
+
+ /* RTS threshold */
+ __s32 min_rts; /* Minimal RTS threshold */
+ __s32 max_rts; /* Maximal RTS threshold */
+
+ /* Frag threshold */
+ __s32 min_frag; /* Minimal frag threshold */
+ __s32 max_frag; /* Maximal frag threshold */
+
+ /* Power Management duration & timeout */
+ __s32 min_pmp; /* Minimal PM period */
+ __s32 max_pmp; /* Maximal PM period */
+ __s32 min_pmt; /* Minimal PM timeout */
+ __s32 max_pmt; /* Maximal PM timeout */
+ __u16 pmp_flags; /* How to decode max/min PM period */
+ __u16 pmt_flags; /* How to decode max/min PM timeout */
+ __u16 pm_capa; /* What PM options are supported */
+
+ /* Encoder stuff */
+ __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */
+ __u8 num_encoding_sizes; /* Number of entry in the list */
+ __u8 max_encoding_tokens; /* Max number of tokens */
+ /* For drivers that need a "login/passwd" form */
+ __u8 encoding_login_index; /* token index for login token */
+
+ /* Transmit power */
+ __u16 txpower_capa; /* What options are supported */
+ __u8 num_txpower; /* Number of entries in the list */
+ __s32 txpower[IW_MAX_TXPOWER]; /* list, in bps */
+
+ /* Wireless Extension version info */
+ __u8 we_version_compiled; /* Must be WIRELESS_EXT */
+ __u8 we_version_source; /* Last update of source */
+
+ /* Retry limits and lifetime */
+ __u16 retry_capa; /* What retry options are supported */
+ __u16 retry_flags; /* How to decode max/min retry limit */
+ __u16 r_time_flags; /* How to decode max/min retry life */
+ __s32 min_retry; /* Minimal number of retries */
+ __s32 max_retry; /* Maximal number of retries */
+ __s32 min_r_time; /* Minimal retry lifetime */
+ __s32 max_r_time; /* Maximal retry lifetime */
+
+ /* Frequency */
+ __u16 num_channels; /* Number of channels [0; num - 1] */
+ __u8 num_frequency; /* Number of entry in the list */
+ struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */
+ /* Note : this frequency list doesn't need to fit channel numbers,
+ * because each entry contain its channel index */
+
+ __u32 enc_capa; /* IW_ENC_CAPA_* bit field */
+};
+
+/*
+ * Private ioctl interface information
+ */
+
+struct iw_priv_args
+{
+ __u32 cmd; /* Number of the ioctl to issue */
+ __u16 set_args; /* Type and number of args */
+ __u16 get_args; /* Type and number of args */
+ char name[IFNAMSIZ]; /* Name of the extension */
+};
+
+/* ----------------------- WIRELESS EVENTS ----------------------- */
+/*
+ * Wireless events are carried through the rtnetlink socket to user
+ * space. They are encapsulated in the IFLA_WIRELESS field of
+ * a RTM_NEWLINK message.
+ */
+
+/*
+ * A Wireless Event. Contains basically the same data as the ioctl...
+ */
+struct iw_event
+{
+ __u16 len; /* Real length of this stuff */
+ __u16 cmd; /* Wireless IOCTL */
+ union iwreq_data u; /* IOCTL fixed payload */
+};
+
+/* Size of the Event prefix (including padding and alignement junk) */
+#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data))
+/* Size of the various events */
+#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ)
+#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32))
+#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq))
+#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param))
+#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr))
+#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality))
+
+/* iw_point events are special. First, the payload (extra data) come at
+ * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second,
+ * we omit the pointer, so start at an offset. */
+#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \
+ (char *) NULL)
+#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \
+ IW_EV_POINT_OFF)
+
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+struct __compat_iw_event {
+ __u16 len; /* Real length of this stuff */
+ __u16 cmd; /* Wireless IOCTL */
+ compat_caddr_t pointer;
+};
+#define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer)
+#define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length)
+
+/* Size of the various events for compat */
+#define IW_EV_COMPAT_CHAR_LEN (IW_EV_COMPAT_LCP_LEN + IFNAMSIZ)
+#define IW_EV_COMPAT_UINT_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(__u32))
+#define IW_EV_COMPAT_FREQ_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_freq))
+#define IW_EV_COMPAT_PARAM_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_param))
+#define IW_EV_COMPAT_ADDR_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct sockaddr))
+#define IW_EV_COMPAT_QUAL_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_quality))
+#define IW_EV_COMPAT_POINT_LEN \
+ (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \
+ IW_EV_COMPAT_POINT_OFF)
+#endif
+#endif
+
+/* Size of the Event prefix when packed in stream */
+#define IW_EV_LCP_PK_LEN (4)
+/* Size of the various events when packed in stream */
+#define IW_EV_CHAR_PK_LEN (IW_EV_LCP_PK_LEN + IFNAMSIZ)
+#define IW_EV_UINT_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(__u32))
+#define IW_EV_FREQ_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_freq))
+#define IW_EV_PARAM_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_param))
+#define IW_EV_ADDR_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct sockaddr))
+#define IW_EV_QUAL_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_quality))
+#define IW_EV_POINT_PK_LEN (IW_EV_LCP_PK_LEN + 4)
+
+#endif /* _LINUX_WIRELESS_H */
diff --git a/src/eap_common/Makefile b/src/eap_common/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/eap_common/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/eap_common/chap.c b/src/eap_common/chap.c
new file mode 100644
index 0000000..60bfc1c
--- /dev/null
+++ b/src/eap_common/chap.c
@@ -0,0 +1,34 @@
+/*
+ * CHAP-MD5 (RFC 1994)
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "chap.h"
+
+int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
+ size_t challenge_len, u8 *response)
+{
+ const u8 *addr[3];
+ size_t len[3];
+
+ addr[0] = &id;
+ len[0] = 1;
+ addr[1] = secret;
+ len[1] = secret_len;
+ addr[2] = challenge;
+ len[2] = challenge_len;
+ return md5_vector(3, addr, len, response);
+}
diff --git a/src/eap_common/chap.h b/src/eap_common/chap.h
new file mode 100644
index 0000000..b9c400c
--- /dev/null
+++ b/src/eap_common/chap.h
@@ -0,0 +1,23 @@
+/*
+ * CHAP-MD5 (RFC 1994)
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef CHAP_H
+#define CHAP_H
+
+#define CHAP_MD5_LEN 16
+
+int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
+ size_t challenge_len, u8 *response);
+
+#endif /* CHAP_H */
diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c
new file mode 100644
index 0000000..4afa1dd
--- /dev/null
+++ b/src/eap_common/eap_common.c
@@ -0,0 +1,184 @@
+/*
+ * EAP common peer/server definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+
+/**
+ * eap_hdr_validate - Validate EAP header
+ * @vendor: Expected EAP Vendor-Id (0 = IETF)
+ * @eap_type: Expected EAP type number
+ * @msg: EAP frame (starting with EAP header)
+ * @plen: Pointer to variable to contain the returned payload length
+ * Returns: Pointer to EAP payload (after type field), or %NULL on failure
+ *
+ * This is a helper function for EAP method implementations. This is usually
+ * called in the beginning of struct eap_method::process() function to verify
+ * that the received EAP request packet has a valid header. This function is
+ * able to process both legacy and expanded EAP headers and in most cases, the
+ * caller can just use the returned payload pointer (into *plen) for processing
+ * the payload regardless of whether the packet used the expanded EAP header or
+ * not.
+ */
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+ const struct wpabuf *msg, size_t *plen)
+{
+ const struct eap_hdr *hdr;
+ const u8 *pos;
+ size_t len;
+
+ hdr = wpabuf_head(msg);
+
+ if (wpabuf_len(msg) < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
+ return NULL;
+ }
+
+ len = be_to_host16(hdr->length);
+ if (len < sizeof(*hdr) + 1 || len > wpabuf_len(msg)) {
+ wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
+ return NULL;
+ }
+
+ pos = (const u8 *) (hdr + 1);
+
+ if (*pos == EAP_TYPE_EXPANDED) {
+ int exp_vendor;
+ u32 exp_type;
+ if (len < sizeof(*hdr) + 8) {
+ wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
+ "length");
+ return NULL;
+ }
+ pos++;
+ exp_vendor = WPA_GET_BE24(pos);
+ pos += 3;
+ exp_type = WPA_GET_BE32(pos);
+ pos += 4;
+ if (exp_vendor != vendor || exp_type != (u32) eap_type) {
+ wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
+ "type");
+ return NULL;
+ }
+
+ *plen = len - sizeof(*hdr) - 8;
+ return pos;
+ } else {
+ if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
+ wpa_printf(MSG_INFO, "EAP: Invalid frame type");
+ return NULL;
+ }
+ *plen = len - sizeof(*hdr) - 1;
+ return pos + 1;
+ }
+}
+
+
+/**
+ * eap_msg_alloc - Allocate a buffer for an EAP message
+ * @vendor: Vendor-Id (0 = IETF)
+ * @type: EAP type
+ * @payload_len: Payload length in bytes (data after Type)
+ * @code: Message Code (EAP_CODE_*)
+ * @identifier: Identifier
+ * Returns: Pointer to the allocated message buffer or %NULL on error
+ *
+ * This function can be used to allocate a buffer for an EAP message and fill
+ * in the EAP header. This function is automatically using expanded EAP header
+ * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
+ * not need to separately select which header type to use when using this
+ * function to allocate the message buffers. The returned buffer has room for
+ * payload_len bytes and has the EAP header and Type field already filled in.
+ */
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+ u8 code, u8 identifier)
+{
+ struct wpabuf *buf;
+ struct eap_hdr *hdr;
+ size_t len;
+
+ len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
+ payload_len;
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return NULL;
+
+ hdr = wpabuf_put(buf, sizeof(*hdr));
+ hdr->code = code;
+ hdr->identifier = identifier;
+ hdr->length = host_to_be16(len);
+
+ if (vendor == EAP_VENDOR_IETF) {
+ wpabuf_put_u8(buf, type);
+ } else {
+ wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
+ wpabuf_put_be24(buf, vendor);
+ wpabuf_put_be32(buf, type);
+ }
+
+ return buf;
+}
+
+
+/**
+ * eap_update_len - Update EAP header length
+ * @msg: EAP message from eap_msg_alloc
+ *
+ * This function updates the length field in the EAP header to match with the
+ * current length for the buffer. This allows eap_msg_alloc() to be used to
+ * allocate a larger buffer than the exact message length (e.g., if exact
+ * message length is not yet known).
+ */
+void eap_update_len(struct wpabuf *msg)
+{
+ struct eap_hdr *hdr;
+ hdr = wpabuf_mhead(msg);
+ if (wpabuf_len(msg) < sizeof(*hdr))
+ return;
+ hdr->length = host_to_be16(wpabuf_len(msg));
+}
+
+
+/**
+ * eap_get_id - Get EAP Identifier from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The Identifier field from the EAP header
+ */
+u8 eap_get_id(const struct wpabuf *msg)
+{
+ const struct eap_hdr *eap;
+
+ if (wpabuf_len(msg) < sizeof(*eap))
+ return 0;
+
+ eap = wpabuf_head(msg);
+ return eap->identifier;
+}
+
+
+/**
+ * eap_get_id - Get EAP Type from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The EAP Type after the EAP header
+ */
+EapType eap_get_type(const struct wpabuf *msg)
+{
+ if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
+ return EAP_TYPE_NONE;
+
+ return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
+}
diff --git a/src/eap_common/eap_common.h b/src/eap_common/eap_common.h
new file mode 100644
index 0000000..b95e76b
--- /dev/null
+++ b/src/eap_common/eap_common.h
@@ -0,0 +1,28 @@
+/*
+ * EAP common peer/server definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_COMMON_H
+#define EAP_COMMON_H
+
+#include "wpabuf.h"
+
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+ const struct wpabuf *msg, size_t *plen);
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+ u8 code, u8 identifier);
+void eap_update_len(struct wpabuf *msg);
+u8 eap_get_id(const struct wpabuf *msg);
+EapType eap_get_type(const struct wpabuf *msg);
+
+#endif /* EAP_COMMON_H */
diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h
new file mode 100644
index 0000000..3035301
--- /dev/null
+++ b/src/eap_common/eap_defs.h
@@ -0,0 +1,86 @@
+/*
+ * EAP server/peer: Shared EAP definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_DEFS_H
+#define EAP_DEFS_H
+
+/* RFC 3748 - Extensible Authentication Protocol (EAP) */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_hdr {
+ u8 code;
+ u8 identifier;
+ be16 length; /* including code and identifier; network byte order */
+ /* followed by length-4 octets of data */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3,
+ EAP_CODE_FAILURE = 4 };
+
+/* EAP Request and Response data begins with one octet Type. Success and
+ * Failure do not have additional data. */
+
+/*
+ * EAP Method Types as allocated by IANA:
+ * http://www.iana.org/assignments/eap-numbers
+ */
+typedef enum {
+ EAP_TYPE_NONE = 0,
+ EAP_TYPE_IDENTITY = 1 /* RFC 3748 */,
+ EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */,
+ EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */,
+ EAP_TYPE_MD5 = 4, /* RFC 3748 */
+ EAP_TYPE_OTP = 5 /* RFC 3748 */,
+ EAP_TYPE_GTC = 6, /* RFC 3748 */
+ EAP_TYPE_TLS = 13 /* RFC 2716 */,
+ EAP_TYPE_LEAP = 17 /* Cisco proprietary */,
+ EAP_TYPE_SIM = 18 /* RFC 4186 */,
+ EAP_TYPE_TTLS = 21 /* RFC 5281 */,
+ EAP_TYPE_AKA = 23 /* RFC 4187 */,
+ EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */,
+ EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */,
+ EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */,
+ EAP_TYPE_TNC = 38 /* TNC IF-T v1.0-r3; note: tentative assignment;
+ * type 38 has previously been allocated for
+ * EAP-HTTP Digest, (funk.com) */,
+ EAP_TYPE_FAST = 43 /* RFC 4851 */,
+ EAP_TYPE_PAX = 46 /* RFC 4746 */,
+ EAP_TYPE_PSK = 47 /* RFC 4764 */,
+ EAP_TYPE_SAKE = 48 /* RFC 4763 */,
+ EAP_TYPE_IKEV2 = 49 /* RFC 5106 */,
+ EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */,
+ EAP_TYPE_GPSK = 51 /* RFC 5433 */,
+ EAP_TYPE_PWD = 52 /* RFC 5931 */,
+ EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
+} EapType;
+
+
+/* SMI Network Management Private Enterprise Code for vendor specific types */
+enum {
+ EAP_VENDOR_IETF = 0,
+ EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */,
+ EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */
+};
+
+#define EAP_MSK_LEN 64
+#define EAP_EMSK_LEN 64
+
+#endif /* EAP_DEFS_H */
diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c
new file mode 100644
index 0000000..4de34a8
--- /dev/null
+++ b/src/eap_common/eap_fast_common.c
@@ -0,0 +1,304 @@
+/*
+ * EAP-FAST common helper functions (RFC 4851)
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_defs.h"
+#include "eap_tlv_common.h"
+#include "eap_fast_common.h"
+
+
+void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len)
+{
+ struct pac_tlv_hdr hdr;
+ hdr.type = host_to_be16(type);
+ hdr.len = host_to_be16(len);
+ wpabuf_put_data(buf, &hdr, sizeof(hdr));
+}
+
+
+void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
+ u16 len)
+{
+ eap_fast_put_tlv_hdr(buf, type, len);
+ wpabuf_put_data(buf, data, len);
+}
+
+
+void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
+ const struct wpabuf *data)
+{
+ eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data));
+ wpabuf_put_buf(buf, data);
+}
+
+
+struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf)
+{
+ struct wpabuf *e;
+
+ if (buf == NULL)
+ return NULL;
+
+ /* Encapsulate EAP packet in EAP-Payload TLV */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV");
+ e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf));
+ if (e == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
+ "for TLV encapsulation");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ eap_fast_put_tlv_buf(e,
+ EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV,
+ buf);
+ wpabuf_free(buf);
+ return e;
+}
+
+
+void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
+ const u8 *client_random, u8 *master_secret)
+{
+#define TLS_RANDOM_LEN 32
+#define TLS_MASTER_SECRET_LEN 48
+ u8 seed[2 * TLS_RANDOM_LEN];
+
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random",
+ client_random, TLS_RANDOM_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random",
+ server_random, TLS_RANDOM_LEN);
+
+ /*
+ * RFC 4851, Section 5.1:
+ * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash",
+ * server_random + client_random, 48)
+ */
+ os_memcpy(seed, server_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN);
+ sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN,
+ "PAC to master secret label hash",
+ seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret",
+ master_secret, TLS_MASTER_SECRET_LEN);
+}
+
+
+u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
+ const char *label, size_t len)
+{
+ struct tls_keys keys;
+ u8 *rnd = NULL, *out;
+ int block_size;
+
+ block_size = tls_connection_get_keyblock_size(ssl_ctx, conn);
+ if (block_size < 0)
+ return NULL;
+
+ out = os_malloc(block_size + len);
+ if (out == NULL)
+ return NULL;
+
+ if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len)
+ == 0) {
+ os_memmove(out, out + block_size, len);
+ return out;
+ }
+
+ if (tls_connection_get_keys(ssl_ctx, conn, &keys))
+ goto fail;
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ if (rnd == NULL)
+ goto fail;
+
+ os_memcpy(rnd, keys.server_random, keys.server_random_len);
+ os_memcpy(rnd + keys.server_random_len, keys.client_random,
+ keys.client_random_len);
+
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key "
+ "expansion", keys.master_key, keys.master_key_len);
+ if (tls_prf(keys.master_key, keys.master_key_len,
+ label, rnd, keys.client_random_len +
+ keys.server_random_len, out, block_size + len))
+ goto fail;
+ os_free(rnd);
+ os_memmove(out, out + block_size, len);
+ return out;
+
+fail:
+ os_free(rnd);
+ os_free(out);
+ return NULL;
+}
+
+
+void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk)
+{
+ /*
+ * RFC 4851, Section 5.4: EAP Master Session Key Generation
+ * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64)
+ */
+
+ sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
+ "Session Key Generating Function", (u8 *) "", 0,
+ msk, EAP_FAST_KEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
+ msk, EAP_FAST_KEY_LEN);
+}
+
+
+void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
+{
+ /*
+ * RFC 4851, Section 5.4: EAP Master Session Key Genreration
+ * EMSK = T-PRF(S-IMCK[j],
+ * "Extended Session Key Generating Function", 64)
+ */
+
+ sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
+ "Extended Session Key Generating Function", (u8 *) "", 0,
+ emsk, EAP_EMSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)",
+ emsk, EAP_EMSK_LEN);
+}
+
+
+int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
+ int tlv_type, u8 *pos, int len)
+{
+ switch (tlv_type) {
+ case EAP_TLV_EAP_PAYLOAD_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV",
+ pos, len);
+ if (tlv->eap_payload_tlv) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+ "EAP-Payload TLV in the message");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ tlv->eap_payload_tlv = pos;
+ tlv->eap_payload_tlv_len = len;
+ break;
+ case EAP_TLV_RESULT_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len);
+ if (tlv->result) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+ "Result TLV in the message");
+ tlv->result = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Result TLV");
+ tlv->result = EAP_TLV_RESULT_FAILURE;
+ break;
+ }
+ tlv->result = WPA_GET_BE16(pos);
+ if (tlv->result != EAP_TLV_RESULT_SUCCESS &&
+ tlv->result != EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d",
+ tlv->result);
+ tlv->result = EAP_TLV_RESULT_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s",
+ tlv->result == EAP_TLV_RESULT_SUCCESS ?
+ "Success" : "Failure");
+ break;
+ case EAP_TLV_INTERMEDIATE_RESULT_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV",
+ pos, len);
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Intermediate-Result TLV");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ break;
+ }
+ if (tlv->iresult) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+ "Intermediate-Result TLV in the message");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ tlv->iresult = WPA_GET_BE16(pos);
+ if (tlv->iresult != EAP_TLV_RESULT_SUCCESS &&
+ tlv->iresult != EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate "
+ "Result %d", tlv->iresult);
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s",
+ tlv->iresult == EAP_TLV_RESULT_SUCCESS ?
+ "Success" : "Failure");
+ break;
+ case EAP_TLV_CRYPTO_BINDING_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV",
+ pos, len);
+ if (tlv->crypto_binding) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+ "Crypto-Binding TLV in the message");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len;
+ if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Crypto-Binding TLV");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *)
+ (pos - sizeof(struct eap_tlv_hdr));
+ break;
+ case EAP_TLV_REQUEST_ACTION_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV",
+ pos, len);
+ if (tlv->request_action) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+ "Request-Action TLV in the message");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+ "Request-Action TLV");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ break;
+ }
+ tlv->request_action = WPA_GET_BE16(pos);
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d",
+ tlv->request_action);
+ break;
+ case EAP_TLV_PAC_TLV:
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len);
+ if (tlv->pac) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+ "PAC TLV in the message");
+ tlv->iresult = EAP_TLV_RESULT_FAILURE;
+ return -2;
+ }
+ tlv->pac = pos;
+ tlv->pac_len = len;
+ break;
+ default:
+ /* Unknown TLV */
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h
new file mode 100644
index 0000000..c85fd37
--- /dev/null
+++ b/src/eap_common/eap_fast_common.h
@@ -0,0 +1,113 @@
+/*
+ * EAP-FAST definitions (RFC 4851)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_FAST_H
+#define EAP_FAST_H
+
+#define EAP_FAST_VERSION 1
+#define EAP_FAST_KEY_LEN 64
+#define EAP_FAST_SIMCK_LEN 40
+#define EAP_FAST_SKS_LEN 40
+#define EAP_FAST_CMK_LEN 20
+
+#define TLS_EXT_PAC_OPAQUE 35
+
+/*
+ * RFC 5422: Section 4.2.1 - Formats for PAC TLV Attributes / Type Field
+ * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined
+ * in the general PAC TLV format (Section 4.2).
+ */
+#define PAC_TYPE_PAC_KEY 1
+#define PAC_TYPE_PAC_OPAQUE 2
+#define PAC_TYPE_CRED_LIFETIME 3
+#define PAC_TYPE_A_ID 4
+#define PAC_TYPE_I_ID 5
+/*
+ * 6 was previous assigned for SERVER_PROTECTED_DATA, but
+ * draft-cam-winget-eap-fast-provisioning-02.txt changed this to Reserved.
+ */
+#define PAC_TYPE_A_ID_INFO 7
+#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8
+#define PAC_TYPE_PAC_INFO 9
+#define PAC_TYPE_PAC_TYPE 10
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct pac_tlv_hdr {
+ be16 type;
+ be16 len;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+#define EAP_FAST_PAC_KEY_LEN 32
+
+/* RFC 5422: 4.2.6 PAC-Type TLV */
+#define PAC_TYPE_TUNNEL_PAC 1
+/* Application Specific Short Lived PACs (only in volatile storage) */
+/* User Authorization PAC */
+#define PAC_TYPE_USER_AUTHORIZATION 3
+/* Application Specific Long Lived PACs */
+/* Machine Authentication PAC */
+#define PAC_TYPE_MACHINE_AUTHENTICATION 2
+
+
+/*
+ * RFC 5422:
+ * Section 3.3 - Key Derivations Used in the EAP-FAST Provisioning Exchange
+ */
+struct eap_fast_key_block_provisioning {
+ /* Extra key material after TLS key_block */
+ u8 session_key_seed[EAP_FAST_SKS_LEN];
+ u8 server_challenge[16]; /* MSCHAPv2 ServerChallenge */
+ u8 client_challenge[16]; /* MSCHAPv2 ClientChallenge */
+};
+
+
+struct wpabuf;
+struct tls_connection;
+
+struct eap_fast_tlv_parse {
+ u8 *eap_payload_tlv;
+ size_t eap_payload_tlv_len;
+ struct eap_tlv_crypto_binding_tlv *crypto_binding;
+ size_t crypto_binding_len;
+ int iresult;
+ int result;
+ int request_action;
+ u8 *pac;
+ size_t pac_len;
+};
+
+void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len);
+void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
+ u16 len);
+void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
+ const struct wpabuf *data);
+struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf);
+void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
+ const u8 *client_random, u8 *master_secret);
+u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
+ const char *label, size_t len);
+void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk);
+void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk);
+int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
+ int tlv_type, u8 *pos, int len);
+
+#endif /* EAP_FAST_H */
diff --git a/src/eap_common/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c
new file mode 100644
index 0000000..4076262
--- /dev/null
+++ b/src/eap_common/eap_gpsk_common.c
@@ -0,0 +1,423 @@
+/*
+ * EAP server/peer: EAP-GPSK shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/sha256.h"
+#include "eap_defs.h"
+#include "eap_gpsk_common.h"
+
+
+/**
+ * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * Returns: 1 if ciphersuite is support, or 0 if not
+ */
+int eap_gpsk_supported_ciphersuite(int vendor, int specifier)
+{
+ if (vendor == EAP_GPSK_VENDOR_IETF &&
+ specifier == EAP_GPSK_CIPHER_AES)
+ return 1;
+#ifdef EAP_GPSK_SHA256
+ if (vendor == EAP_GPSK_VENDOR_IETF &&
+ specifier == EAP_GPSK_CIPHER_SHA256)
+ return 1;
+#endif /* EAP_GPSK_SHA256 */
+ return 0;
+}
+
+
+static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */,
+ const u8 *data /* Z */, size_t data_len,
+ u8 *buf, size_t len /* X */)
+{
+ u8 *opos;
+ size_t i, n, hashlen, left, clen;
+ u8 ibuf[2], hash[16];
+ const u8 *addr[2];
+ size_t vlen[2];
+
+ hashlen = sizeof(hash);
+ /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */
+ addr[0] = ibuf;
+ vlen[0] = sizeof(ibuf);
+ addr[1] = data;
+ vlen[1] = data_len;
+
+ opos = buf;
+ left = len;
+ n = (len + hashlen - 1) / hashlen;
+ for (i = 1; i <= n; i++) {
+ WPA_PUT_BE16(ibuf, i);
+ if (omac1_aes_128_vector(psk, 2, addr, vlen, hash))
+ return -1;
+ clen = left > hashlen ? hashlen : left;
+ os_memcpy(opos, hash, clen);
+ opos += clen;
+ left -= clen;
+ }
+
+ return 0;
+}
+
+
+#ifdef EAP_GPSK_SHA256
+static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */,
+ const u8 *data /* Z */, size_t data_len,
+ u8 *buf, size_t len /* X */)
+{
+ u8 *opos;
+ size_t i, n, hashlen, left, clen;
+ u8 ibuf[2], hash[SHA256_MAC_LEN];
+ const u8 *addr[2];
+ size_t vlen[2];
+
+ hashlen = SHA256_MAC_LEN;
+ /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */
+ addr[0] = ibuf;
+ vlen[0] = sizeof(ibuf);
+ addr[1] = data;
+ vlen[1] = data_len;
+
+ opos = buf;
+ left = len;
+ n = (len + hashlen - 1) / hashlen;
+ for (i = 1; i <= n; i++) {
+ WPA_PUT_BE16(ibuf, i);
+ hmac_sha256_vector(psk, 32, 2, addr, vlen, hash);
+ clen = left > hashlen ? hashlen : left;
+ os_memcpy(opos, hash, clen);
+ opos += clen;
+ left -= clen;
+ }
+
+ return 0;
+}
+#endif /* EAP_GPSK_SHA256 */
+
+
+static int eap_gpsk_derive_keys_helper(u32 csuite_specifier,
+ u8 *kdf_out, size_t kdf_out_len,
+ const u8 *psk, size_t psk_len,
+ const u8 *seed, size_t seed_len,
+ u8 *msk, u8 *emsk,
+ u8 *sk, size_t sk_len,
+ u8 *pk, size_t pk_len)
+{
+ u8 mk[32], *pos, *data;
+ size_t data_len, mk_len;
+ int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
+ u8 *buf, size_t len);
+
+ gkdf = NULL;
+ switch (csuite_specifier) {
+ case EAP_GPSK_CIPHER_AES:
+ gkdf = eap_gpsk_gkdf_cmac;
+ mk_len = 16;
+ break;
+#ifdef EAP_GPSK_SHA256
+ case EAP_GPSK_CIPHER_SHA256:
+ gkdf = eap_gpsk_gkdf_sha256;
+ mk_len = SHA256_MAC_LEN;
+ break;
+#endif /* EAP_GPSK_SHA256 */
+ default:
+ return -1;
+ }
+
+ if (psk_len < mk_len)
+ return -1;
+
+ data_len = 2 + psk_len + 6 + seed_len;
+ data = os_malloc(data_len);
+ if (data == NULL)
+ return -1;
+ pos = data;
+ WPA_PUT_BE16(pos, psk_len);
+ pos += 2;
+ os_memcpy(pos, psk, psk_len);
+ pos += psk_len;
+ WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
+ pos += 4;
+ WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
+ pos += 2;
+ os_memcpy(pos, seed, seed_len); /* inputString */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation",
+ data, data_len);
+
+ if (gkdf(psk, data, data_len, mk, mk_len) < 0) {
+ os_free(data);
+ return -1;
+ }
+ os_free(data);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len);
+
+ if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0)
+ return -1;
+
+ pos = kdf_out;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN);
+ os_memcpy(msk, pos, EAP_MSK_LEN);
+ pos += EAP_MSK_LEN;
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN);
+ os_memcpy(emsk, pos, EAP_EMSK_LEN);
+ pos += EAP_EMSK_LEN;
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len);
+ os_memcpy(sk, pos, sk_len);
+ pos += sk_len;
+
+ if (pk) {
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len);
+ os_memcpy(pk, pos, pk_len);
+ }
+
+ return 0;
+}
+
+
+static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len,
+ const u8 *seed, size_t seed_len,
+ u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+ u8 *pk, size_t *pk_len)
+{
+#define EAP_GPSK_SK_LEN_AES 16
+#define EAP_GPSK_PK_LEN_AES 16
+ u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES +
+ EAP_GPSK_PK_LEN_AES];
+
+ /*
+ * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
+ * (= seed)
+ * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001
+ * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString)
+ * MSK = GKDF-160 (MK, inputString)[0..63]
+ * EMSK = GKDF-160 (MK, inputString)[64..127]
+ * SK = GKDF-160 (MK, inputString)[128..143]
+ * PK = GKDF-160 (MK, inputString)[144..159]
+ * zero = 0x00 || 0x00 || ... || 0x00 (16 times)
+ * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
+ * CSuite_Sel || inputString)
+ */
+
+ *sk_len = EAP_GPSK_SK_LEN_AES;
+ *pk_len = EAP_GPSK_PK_LEN_AES;
+
+ return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES,
+ kdf_out, sizeof(kdf_out),
+ psk, psk_len, seed, seed_len,
+ msk, emsk, sk, *sk_len,
+ pk, *pk_len);
+}
+
+
+#ifdef EAP_GPSK_SHA256
+static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len,
+ const u8 *seed, size_t seed_len,
+ u8 *msk, u8 *emsk,
+ u8 *sk, size_t *sk_len)
+{
+#define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
+#define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
+ u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 +
+ EAP_GPSK_PK_LEN_SHA256];
+
+ /*
+ * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
+ * (= seed)
+ * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002
+ * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString)
+ * MSK = GKDF-160 (MK, inputString)[0..63]
+ * EMSK = GKDF-160 (MK, inputString)[64..127]
+ * SK = GKDF-160 (MK, inputString)[128..159]
+ * zero = 0x00 || 0x00 || ... || 0x00 (32 times)
+ * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
+ * CSuite_Sel || inputString)
+ */
+
+ *sk_len = EAP_GPSK_SK_LEN_SHA256;
+
+ return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256,
+ kdf_out, sizeof(kdf_out),
+ psk, psk_len, seed, seed_len,
+ msk, emsk, sk, *sk_len,
+ NULL, 0);
+}
+#endif /* EAP_GPSK_SHA256 */
+
+
+/**
+ * eap_gpsk_derive_keys - Derive EAP-GPSK keys
+ * @psk: Pre-shared key
+ * @psk_len: Length of psk in bytes
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * @rand_peer: 32-byte RAND_Peer
+ * @rand_server: 32-byte RAND_Server
+ * @id_peer: ID_Peer
+ * @id_peer_len: Length of ID_Peer
+ * @id_server: ID_Server
+ * @id_server_len: Length of ID_Server
+ * @msk: Buffer for 64-byte MSK
+ * @emsk: Buffer for 64-byte EMSK
+ * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes)
+ * @sk_len: Buffer for returning length of SK
+ * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes)
+ * @pk_len: Buffer for returning length of PK
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
+ int specifier,
+ const u8 *rand_peer, const u8 *rand_server,
+ const u8 *id_peer, size_t id_peer_len,
+ const u8 *id_server, size_t id_server_len,
+ u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+ u8 *pk, size_t *pk_len)
+{
+ u8 *seed, *pos;
+ size_t seed_len;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
+ vendor, specifier);
+
+ if (vendor != EAP_GPSK_VENDOR_IETF)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
+
+ /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
+ seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len;
+ seed = os_malloc(seed_len);
+ if (seed == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
+ "for key derivation");
+ return -1;
+ }
+
+ pos = seed;
+ os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
+ pos += EAP_GPSK_RAND_LEN;
+ os_memcpy(pos, id_peer, id_peer_len);
+ pos += id_peer_len;
+ os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
+ pos += EAP_GPSK_RAND_LEN;
+ os_memcpy(pos, id_server, id_server_len);
+ pos += id_server_len;
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len);
+
+ switch (specifier) {
+ case EAP_GPSK_CIPHER_AES:
+ ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len,
+ msk, emsk, sk, sk_len,
+ pk, pk_len);
+ break;
+#ifdef EAP_GPSK_SHA256
+ case EAP_GPSK_CIPHER_SHA256:
+ ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len,
+ msk, emsk, sk, sk_len);
+ break;
+#endif /* EAP_GPSK_SHA256 */
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
+ "key derivation", vendor, specifier);
+ ret = -1;
+ break;
+ }
+
+ os_free(seed);
+
+ return ret;
+}
+
+
+/**
+ * eap_gpsk_mic_len - Get the length of the MIC
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * Returns: MIC length in bytes
+ */
+size_t eap_gpsk_mic_len(int vendor, int specifier)
+{
+ if (vendor != EAP_GPSK_VENDOR_IETF)
+ return 0;
+
+ switch (specifier) {
+ case EAP_GPSK_CIPHER_AES:
+ return 16;
+#ifdef EAP_GPSK_SHA256
+ case EAP_GPSK_CIPHER_SHA256:
+ return 32;
+#endif /* EAP_GPSK_SHA256 */
+ default:
+ return 0;
+ }
+}
+
+
+static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len,
+ const u8 *data, size_t len, u8 *mic)
+{
+ if (sk_len != 16) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for "
+ "AES-CMAC MIC", (unsigned long) sk_len);
+ return -1;
+ }
+
+ return omac1_aes_128(sk, data, len, mic);
+}
+
+
+/**
+ * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet
+ * @sk: Session key SK from eap_gpsk_derive_keys()
+ * @sk_len: SK length in bytes from eap_gpsk_derive_keys()
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * @data: Input data to MIC
+ * @len: Input data length in bytes
+ * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
+ int specifier, const u8 *data, size_t len, u8 *mic)
+{
+ int ret;
+
+ if (vendor != EAP_GPSK_VENDOR_IETF)
+ return -1;
+
+ switch (specifier) {
+ case EAP_GPSK_CIPHER_AES:
+ ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic);
+ break;
+#ifdef EAP_GPSK_SHA256
+ case EAP_GPSK_CIPHER_SHA256:
+ hmac_sha256(sk, sk_len, data, len, mic);
+ ret = 0;
+ break;
+#endif /* EAP_GPSK_SHA256 */
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
+ "MIC computation", vendor, specifier);
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
diff --git a/src/eap_common/eap_gpsk_common.h b/src/eap_common/eap_gpsk_common.h
new file mode 100644
index 0000000..a30ab97
--- /dev/null
+++ b/src/eap_common/eap_gpsk_common.h
@@ -0,0 +1,66 @@
+/*
+ * EAP server/peer: EAP-GPSK shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_GPSK_COMMON_H
+#define EAP_GPSK_COMMON_H
+
+#define EAP_GPSK_OPCODE_GPSK_1 1
+#define EAP_GPSK_OPCODE_GPSK_2 2
+#define EAP_GPSK_OPCODE_GPSK_3 3
+#define EAP_GPSK_OPCODE_GPSK_4 4
+#define EAP_GPSK_OPCODE_FAIL 5
+#define EAP_GPSK_OPCODE_PROTECTED_FAIL 6
+
+/* Failure-Code in GPSK-Fail and GPSK-Protected-Fail */
+#define EAP_GPSK_FAIL_PSK_NOT_FOUND 0x00000001
+#define EAP_GPSK_FAIL_AUTHENTICATION_FAILURE 0x00000002
+#define EAP_GPSK_FAIL_AUTHORIZATION_FAILURE 0x00000003
+
+#define EAP_GPSK_RAND_LEN 32
+#define EAP_GPSK_MAX_SK_LEN 32
+#define EAP_GPSK_MAX_PK_LEN 32
+#define EAP_GPSK_MAX_MIC_LEN 32
+
+#define EAP_GPSK_VENDOR_IETF 0x00000000
+#define EAP_GPSK_CIPHER_RESERVED 0x000000
+#define EAP_GPSK_CIPHER_AES 0x000001
+#define EAP_GPSK_CIPHER_SHA256 0x000002
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_gpsk_csuite {
+ u8 vendor[4];
+ u8 specifier[2];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+int eap_gpsk_supported_ciphersuite(int vendor, int specifier);
+int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
+ int specifier,
+ const u8 *rand_client, const u8 *rand_server,
+ const u8 *id_client, size_t id_client_len,
+ const u8 *id_server, size_t id_server_len,
+ u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+ u8 *pk, size_t *pk_len);
+size_t eap_gpsk_mic_len(int vendor, int specifier);
+int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
+ int specifier, const u8 *data, size_t len, u8 *mic);
+
+#endif /* EAP_GPSK_COMMON_H */
diff --git a/src/eap_common/eap_ikev2_common.c b/src/eap_common/eap_ikev2_common.c
new file mode 100644
index 0000000..e9a9c55
--- /dev/null
+++ b/src/eap_common/eap_ikev2_common.c
@@ -0,0 +1,132 @@
+/*
+ * EAP-IKEv2 common routines
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+#include "ikev2_common.h"
+#include "eap_ikev2_common.h"
+
+
+int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys,
+ const u8 *i_nonce, size_t i_nonce_len,
+ const u8 *r_nonce, size_t r_nonce_len,
+ u8 *keymat)
+{
+ u8 *nonces;
+ size_t nlen;
+
+ /* KEYMAT = prf+(SK_d, Ni | Nr) */
+ if (keys->SK_d == NULL || i_nonce == NULL || r_nonce == NULL)
+ return -1;
+
+ nlen = i_nonce_len + r_nonce_len;
+ nonces = os_malloc(nlen);
+ if (nonces == NULL)
+ return -1;
+ os_memcpy(nonces, i_nonce, i_nonce_len);
+ os_memcpy(nonces + i_nonce_len, r_nonce, r_nonce_len);
+
+ if (ikev2_prf_plus(prf, keys->SK_d, keys->SK_d_len, nonces, nlen,
+ keymat, EAP_MSK_LEN + EAP_EMSK_LEN)) {
+ os_free(nonces);
+ return -1;
+ }
+ os_free(nonces);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-IKEV2: KEYMAT",
+ keymat, EAP_MSK_LEN + EAP_EMSK_LEN);
+
+ return 0;
+}
+
+
+struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code)
+{
+ struct wpabuf *msg;
+
+#ifdef CCNS_PL
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 1, code, id);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
+ "for fragment ack");
+ return NULL;
+ }
+ wpabuf_put_u8(msg, 0); /* Flags */
+#else /* CCNS_PL */
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 0, code, id);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
+ "for fragment ack");
+ return NULL;
+ }
+#endif /* CCNS_PL */
+
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Send fragment ack");
+
+ return msg;
+}
+
+
+int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys,
+ int initiator, const struct wpabuf *msg,
+ const u8 *pos, const u8 *end)
+{
+ const struct ikev2_integ_alg *integ;
+ size_t icv_len;
+ u8 icv[IKEV2_MAX_HASH_LEN];
+ const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
+
+ integ = ikev2_get_integ(integ_alg);
+ if (integ == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
+ "transform / cannot validate ICV");
+ return -1;
+ }
+ icv_len = integ->hash_len;
+
+ if (end - pos < (int) icv_len) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Not enough room in the "
+ "message for Integrity Checksum Data");
+ return -1;
+ }
+
+ if (SK_a == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: No SK_a for ICV validation");
+ return -1;
+ }
+
+ if (ikev2_integ_hash(integ_alg, SK_a, keys->SK_integ_len,
+ wpabuf_head(msg),
+ wpabuf_len(msg) - icv_len, icv) < 0) {
+ wpa_printf(MSG_INFO, "EAP-IKEV2: Could not calculate ICV");
+ return -1;
+ }
+
+ if (os_memcmp(icv, end - icv_len, icv_len) != 0) {
+ wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid ICV");
+ wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Calculated ICV",
+ icv, icv_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Received ICV",
+ end - icv_len, icv_len);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Valid Integrity Checksum Data in "
+ "the received message");
+
+ return icv_len;
+}
diff --git a/src/eap_common/eap_ikev2_common.h b/src/eap_common/eap_ikev2_common.h
new file mode 100644
index 0000000..a9fc2ca
--- /dev/null
+++ b/src/eap_common/eap_ikev2_common.h
@@ -0,0 +1,42 @@
+/*
+ * EAP-IKEv2 definitions
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_IKEV2_COMMON_H
+#define EAP_IKEV2_COMMON_H
+
+#ifdef CCNS_PL
+/* incorrect bit order */
+#define IKEV2_FLAGS_LENGTH_INCLUDED 0x01
+#define IKEV2_FLAGS_MORE_FRAGMENTS 0x02
+#define IKEV2_FLAGS_ICV_INCLUDED 0x04
+#else /* CCNS_PL */
+#define IKEV2_FLAGS_LENGTH_INCLUDED 0x80
+#define IKEV2_FLAGS_MORE_FRAGMENTS 0x40
+#define IKEV2_FLAGS_ICV_INCLUDED 0x20
+#endif /* CCNS_PL */
+
+#define IKEV2_FRAGMENT_SIZE 1400
+
+struct ikev2_keys;
+
+int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys,
+ const u8 *i_nonce, size_t i_nonce_len,
+ const u8 *r_nonce, size_t r_nonce_len,
+ u8 *keymat);
+struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code);
+int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys,
+ int initiator, const struct wpabuf *msg,
+ const u8 *pos, const u8 *end);
+
+#endif /* EAP_IKEV2_COMMON_H */
diff --git a/src/eap_common/eap_pax_common.c b/src/eap_common/eap_pax_common.c
new file mode 100644
index 0000000..32dc80c
--- /dev/null
+++ b/src/eap_common/eap_pax_common.c
@@ -0,0 +1,150 @@
+/*
+ * EAP server/peer: EAP-PAX shared routines
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "eap_pax_common.h"
+
+
+/**
+ * eap_pax_kdf - PAX Key Derivation Function
+ * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
+ * @key: Secret key (X)
+ * @key_len: Length of the secret key in bytes
+ * @identifier: Public identifier for the key (Y)
+ * @entropy: Exchanged entropy to seed the KDF (Z)
+ * @entropy_len: Length of the entropy in bytes
+ * @output_len: Output len in bytes (W)
+ * @output: Buffer for the derived key
+ * Returns: 0 on success, -1 failed
+ *
+ * RFC 4746, Section 2.6: PAX-KDF-W(X, Y, Z)
+ */
+int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len,
+ const char *identifier,
+ const u8 *entropy, size_t entropy_len,
+ size_t output_len, u8 *output)
+{
+ u8 mac[SHA1_MAC_LEN];
+ u8 counter, *pos;
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_blocks, left;
+
+ num_blocks = (output_len + EAP_PAX_MAC_LEN - 1) / EAP_PAX_MAC_LEN;
+ if (identifier == NULL || num_blocks >= 255)
+ return -1;
+
+ /* TODO: add support for EAP_PAX_HMAC_SHA256_128 */
+ if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128)
+ return -1;
+
+ addr[0] = (const u8 *) identifier;
+ len[0] = os_strlen(identifier);
+ addr[1] = entropy;
+ len[1] = entropy_len;
+ addr[2] = &counter;
+ len[2] = 1;
+
+ pos = output;
+ left = output_len;
+ for (counter = 1; counter <= (u8) num_blocks; counter++) {
+ size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left;
+ hmac_sha1_vector(key, key_len, 3, addr, len, mac);
+ os_memcpy(pos, mac, clen);
+ pos += clen;
+ left -= clen;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_pax_mac - EAP-PAX MAC
+ * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
+ * @key: Secret key
+ * @key_len: Length of the secret key in bytes
+ * @data1: Optional data, first block; %NULL if not used
+ * @data1_len: Length of data1 in bytes
+ * @data2: Optional data, second block; %NULL if not used
+ * @data2_len: Length of data2 in bytes
+ * @data3: Optional data, third block; %NULL if not used
+ * @data3_len: Length of data3 in bytes
+ * @mac: Buffer for the MAC value (EAP_PAX_MAC_LEN = 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Wrapper function to calculate EAP-PAX MAC.
+ */
+int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len,
+ const u8 *data1, size_t data1_len,
+ const u8 *data2, size_t data2_len,
+ const u8 *data3, size_t data3_len,
+ u8 *mac)
+{
+ u8 hash[SHA1_MAC_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ size_t count;
+
+ /* TODO: add support for EAP_PAX_HMAC_SHA256_128 */
+ if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128)
+ return -1;
+
+ addr[0] = data1;
+ len[0] = data1_len;
+ addr[1] = data2;
+ len[1] = data2_len;
+ addr[2] = data3;
+ len[2] = data3_len;
+
+ count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0);
+ hmac_sha1_vector(key, key_len, count, addr, len, hash);
+ os_memcpy(mac, hash, EAP_PAX_MAC_LEN);
+
+ return 0;
+}
+
+
+/**
+ * eap_pax_initial_key_derivation - EAP-PAX initial key derivation
+ * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
+ * @ak: Authentication Key
+ * @e: Entropy
+ * @mk: Buffer for the derived Master Key
+ * @ck: Buffer for the derived Confirmation Key
+ * @ick: Buffer for the derived Integrity Check Key
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
+ u8 *mk, u8 *ck, u8 *ick)
+{
+ wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation");
+ if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key",
+ e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MK_LEN, mk) ||
+ eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key",
+ e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) ||
+ eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key",
+ e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick))
+ return -1;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN);
+
+ return 0;
+}
diff --git a/src/eap_common/eap_pax_common.h b/src/eap_common/eap_pax_common.h
new file mode 100644
index 0000000..dcc171e
--- /dev/null
+++ b/src/eap_common/eap_pax_common.h
@@ -0,0 +1,97 @@
+/*
+ * EAP server/peer: EAP-PAX shared routines
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_PAX_COMMON_H
+#define EAP_PAX_COMMON_H
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_pax_hdr {
+ u8 op_code;
+ u8 flags;
+ u8 mac_id;
+ u8 dh_group_id;
+ u8 public_key_id;
+ /* Followed by variable length payload and ICV */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* op_code: */
+enum {
+ EAP_PAX_OP_STD_1 = 0x01,
+ EAP_PAX_OP_STD_2 = 0x02,
+ EAP_PAX_OP_STD_3 = 0x03,
+ EAP_PAX_OP_SEC_1 = 0x11,
+ EAP_PAX_OP_SEC_2 = 0x12,
+ EAP_PAX_OP_SEC_3 = 0x13,
+ EAP_PAX_OP_SEC_4 = 0x14,
+ EAP_PAX_OP_SEC_5 = 0x15,
+ EAP_PAX_OP_ACK = 0x21
+};
+
+/* flags: */
+#define EAP_PAX_FLAGS_MF 0x01
+#define EAP_PAX_FLAGS_CE 0x02
+#define EAP_PAX_FLAGS_AI 0x04
+
+/* mac_id: */
+#define EAP_PAX_MAC_HMAC_SHA1_128 0x01
+#define EAP_PAX_HMAC_SHA256_128 0x02
+
+/* dh_group_id: */
+#define EAP_PAX_DH_GROUP_NONE 0x00
+#define EAP_PAX_DH_GROUP_2048_MODP 0x01
+#define EAP_PAX_DH_GROUP_3072_MODP 0x02
+#define EAP_PAX_DH_GROUP_NIST_ECC_P_256 0x03
+
+/* public_key_id: */
+#define EAP_PAX_PUBLIC_KEY_NONE 0x00
+#define EAP_PAX_PUBLIC_KEY_RSAES_OAEP 0x01
+#define EAP_PAX_PUBLIC_KEY_RSA_PKCS1_V1_5 0x02
+#define EAP_PAX_PUBLIC_KEY_EL_GAMAL_NIST_ECC 0x03
+
+/* ADE type: */
+#define EAP_PAX_ADE_VENDOR_SPECIFIC 0x01
+#define EAP_PAX_ADE_CLIENT_CHANNEL_BINDING 0x02
+#define EAP_PAX_ADE_SERVER_CHANNEL_BINDING 0x03
+
+
+#define EAP_PAX_RAND_LEN 32
+#define EAP_PAX_MAC_LEN 16
+#define EAP_PAX_ICV_LEN 16
+#define EAP_PAX_AK_LEN 16
+#define EAP_PAX_MK_LEN 16
+#define EAP_PAX_CK_LEN 16
+#define EAP_PAX_ICK_LEN 16
+
+
+int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len,
+ const char *identifier,
+ const u8 *entropy, size_t entropy_len,
+ size_t output_len, u8 *output);
+int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len,
+ const u8 *data1, size_t data1_len,
+ const u8 *data2, size_t data2_len,
+ const u8 *data3, size_t data3_len,
+ u8 *mac);
+int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
+ u8 *mk, u8 *ck, u8 *ick);
+
+#endif /* EAP_PAX_COMMON_H */
diff --git a/src/eap_common/eap_peap_common.c b/src/eap_common/eap_peap_common.c
new file mode 100644
index 0000000..3a64b8e
--- /dev/null
+++ b/src/eap_common/eap_peap_common.c
@@ -0,0 +1,88 @@
+/*
+ * EAP-PEAP common routines
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "eap_peap_common.h"
+
+void peap_prfplus(int version, const u8 *key, size_t key_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *buf, size_t buf_len)
+{
+ unsigned char counter = 0;
+ size_t pos, plen;
+ u8 hash[SHA1_MAC_LEN];
+ size_t label_len = os_strlen(label);
+ u8 extra[2];
+ const unsigned char *addr[5];
+ size_t len[5];
+
+ addr[0] = hash;
+ len[0] = 0;
+ addr[1] = (unsigned char *) label;
+ len[1] = label_len;
+ addr[2] = seed;
+ len[2] = seed_len;
+
+ if (version == 0) {
+ /*
+ * PRF+(K, S, LEN) = T1 | T2 | ... | Tn
+ * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00)
+ * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00)
+ * ...
+ * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00)
+ */
+
+ extra[0] = 0;
+ extra[1] = 0;
+
+ addr[3] = &counter;
+ len[3] = 1;
+ addr[4] = extra;
+ len[4] = 2;
+ } else {
+ /*
+ * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where:
+ * T1 = HMAC-SHA1(K, S | LEN | 0x01)
+ * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02)
+ * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03)
+ * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04)
+ * ...
+ */
+
+ extra[0] = buf_len & 0xff;
+
+ addr[3] = extra;
+ len[3] = 1;
+ addr[4] = &counter;
+ len[4] = 1;
+ }
+
+ pos = 0;
+ while (pos < buf_len) {
+ counter++;
+ plen = buf_len - pos;
+ hmac_sha1_vector(key, key_len, 5, addr, len, hash);
+ if (plen >= SHA1_MAC_LEN) {
+ os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
+ pos += SHA1_MAC_LEN;
+ } else {
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ len[0] = SHA1_MAC_LEN;
+ }
+}
diff --git a/src/eap_common/eap_peap_common.h b/src/eap_common/eap_peap_common.h
new file mode 100644
index 0000000..f59afb0
--- /dev/null
+++ b/src/eap_common/eap_peap_common.h
@@ -0,0 +1,22 @@
+/*
+ * EAP-PEAP common routines
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_PEAP_COMMON_H
+#define EAP_PEAP_COMMON_H
+
+void peap_prfplus(int version, const u8 *key, size_t key_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *buf, size_t buf_len);
+
+#endif /* EAP_PEAP_COMMON_H */
diff --git a/src/eap_common/eap_psk_common.c b/src/eap_common/eap_psk_common.c
new file mode 100644
index 0000000..7417d5c
--- /dev/null
+++ b/src/eap_common/eap_psk_common.c
@@ -0,0 +1,74 @@
+/*
+ * EAP server/peer: EAP-PSK shared routines
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "eap_defs.h"
+#include "eap_psk_common.h"
+
+#define aes_block_size 16
+
+
+int eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk)
+{
+ os_memset(ak, 0, aes_block_size);
+ if (aes_128_encrypt_block(psk, ak, ak))
+ return -1;
+ os_memcpy(kdk, ak, aes_block_size);
+ ak[aes_block_size - 1] ^= 0x01;
+ kdk[aes_block_size - 1] ^= 0x02;
+ if (aes_128_encrypt_block(psk, ak, ak) ||
+ aes_128_encrypt_block(psk, kdk, kdk))
+ return -1;
+ return 0;
+}
+
+
+int eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk,
+ u8 *emsk)
+{
+ u8 hash[aes_block_size];
+ u8 counter = 1;
+ int i;
+
+ if (aes_128_encrypt_block(kdk, rand_p, hash))
+ return -1;
+
+ hash[aes_block_size - 1] ^= counter;
+ if (aes_128_encrypt_block(kdk, hash, tek))
+ return -1;
+ hash[aes_block_size - 1] ^= counter;
+ counter++;
+
+ for (i = 0; i < EAP_MSK_LEN / aes_block_size; i++) {
+ hash[aes_block_size - 1] ^= counter;
+ if (aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]))
+ return -1;
+ hash[aes_block_size - 1] ^= counter;
+ counter++;
+ }
+
+ for (i = 0; i < EAP_EMSK_LEN / aes_block_size; i++) {
+ hash[aes_block_size - 1] ^= counter;
+ if (aes_128_encrypt_block(kdk, hash,
+ &emsk[i * aes_block_size]))
+ return -1;
+ hash[aes_block_size - 1] ^= counter;
+ counter++;
+ }
+
+ return 0;
+}
diff --git a/src/eap_common/eap_psk_common.h b/src/eap_common/eap_psk_common.h
new file mode 100644
index 0000000..8adc054
--- /dev/null
+++ b/src/eap_common/eap_psk_common.h
@@ -0,0 +1,78 @@
+/*
+ * EAP server/peer: EAP-PSK shared routines
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_PSK_COMMON_H
+#define EAP_PSK_COMMON_H
+
+
+#define EAP_PSK_RAND_LEN 16
+#define EAP_PSK_MAC_LEN 16
+#define EAP_PSK_TEK_LEN 16
+#define EAP_PSK_PSK_LEN 16
+#define EAP_PSK_AK_LEN 16
+#define EAP_PSK_KDK_LEN 16
+
+#define EAP_PSK_R_FLAG_CONT 1
+#define EAP_PSK_R_FLAG_DONE_SUCCESS 2
+#define EAP_PSK_R_FLAG_DONE_FAILURE 3
+#define EAP_PSK_E_FLAG 0x20
+
+#define EAP_PSK_FLAGS_GET_T(flags) (((flags) & 0xc0) >> 6)
+#define EAP_PSK_FLAGS_SET_T(t) ((u8) (t) << 6)
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* EAP-PSK First Message (AS -> Supplicant) */
+struct eap_psk_hdr_1 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ /* Followed by variable length ID_S */
+} STRUCT_PACKED;
+
+/* EAP-PSK Second Message (Supplicant -> AS) */
+struct eap_psk_hdr_2 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ u8 rand_p[EAP_PSK_RAND_LEN];
+ u8 mac_p[EAP_PSK_MAC_LEN];
+ /* Followed by variable length ID_P */
+} STRUCT_PACKED;
+
+/* EAP-PSK Third Message (AS -> Supplicant) */
+struct eap_psk_hdr_3 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ u8 mac_s[EAP_PSK_MAC_LEN];
+ /* Followed by variable length PCHANNEL */
+} STRUCT_PACKED;
+
+/* EAP-PSK Fourth Message (Supplicant -> AS) */
+struct eap_psk_hdr_4 {
+ u8 flags;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ /* Followed by variable length PCHANNEL */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+int __must_check eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk);
+int __must_check eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek,
+ u8 *msk, u8 *emsk);
+
+#endif /* EAP_PSK_COMMON_H */
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
new file mode 100644
index 0000000..c24b146
--- /dev/null
+++ b/src/eap_common/eap_pwd_common.c
@@ -0,0 +1,312 @@
+/*
+ * EAP server/peer: EAP-pwd shared routines
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_pwd_common.h"
+
+/* The random function H(x) = HMAC-SHA256(0^32, x) */
+void H_Init(HMAC_CTX *ctx)
+{
+ u8 allzero[SHA256_DIGEST_LENGTH];
+
+ os_memset(allzero, 0, SHA256_DIGEST_LENGTH);
+ HMAC_Init(ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256());
+}
+
+
+void H_Update(HMAC_CTX *ctx, const u8 *data, int len)
+{
+ HMAC_Update(ctx, data, len);
+}
+
+
+void H_Final(HMAC_CTX *ctx, u8 *digest)
+{
+ unsigned int mdlen = SHA256_DIGEST_LENGTH;
+
+ HMAC_Final(ctx, digest, &mdlen);
+ HMAC_CTX_cleanup(ctx);
+}
+
+
+/* a counter-based KDF based on NIST SP800-108 */
+void eap_pwd_kdf(u8 *key, int keylen, u8 *label, int labellen,
+ u8 *result, int resultbitlen)
+{
+ HMAC_CTX hctx;
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+ u16 i, ctr, L;
+ int resultbytelen, len = 0;
+ unsigned int mdlen = SHA256_DIGEST_LENGTH;
+ unsigned char mask = 0xff;
+
+ resultbytelen = (resultbitlen + 7)/8;
+ ctr = 0;
+ L = htons(resultbitlen);
+ while (len < resultbytelen) {
+ ctr++; i = htons(ctr);
+ HMAC_Init(&hctx, key, keylen, EVP_sha256());
+ if (ctr > 1)
+ HMAC_Update(&hctx, digest, mdlen);
+ HMAC_Update(&hctx, (u8 *) &i, sizeof(u16));
+ HMAC_Update(&hctx, label, labellen);
+ HMAC_Update(&hctx, (u8 *) &L, sizeof(u16));
+ HMAC_Final(&hctx, digest, &mdlen);
+ if ((len + (int) mdlen) > resultbytelen)
+ os_memcpy(result + len, digest, resultbytelen - len);
+ else
+ os_memcpy(result + len, digest, mdlen);
+ len += mdlen;
+ HMAC_CTX_cleanup(&hctx);
+ }
+
+ /* since we're expanding to a bit length, mask off the excess */
+ if (resultbitlen % 8) {
+ mask >>= ((resultbytelen * 8) - resultbitlen);
+ result[0] &= mask;
+ }
+}
+
+
+/*
+ * 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;
+ HMAC_CTX ctx;
+ unsigned char pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, ctr;
+ int nid, is_odd, primebitlen, primebytelen, ret = 0;
+
+ 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 > 10) {
+ 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)
+ */
+ H_Init(&ctx);
+ H_Update(&ctx, token, sizeof(u32));
+ H_Update(&ctx, id_peer, id_peer_len);
+ H_Update(&ctx, id_server, id_server_len);
+ H_Update(&ctx, password, password_len);
+ H_Update(&ctx, &ctr, sizeof(ctr));
+ H_Final(&ctx, pwe_digest);
+
+ BN_bin2bn(pwe_digest, SHA256_DIGEST_LENGTH, rnd);
+
+ eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH,
+ (unsigned char *) "EAP-pwd Hunting And Pecking",
+ os_strlen("EAP-pwd Hunting And Pecking"),
+ prfbuf, primebitlen);
+
+ BN_bin2bn(prfbuf, primebytelen, x_candidate);
+ 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);
+ EC_POINT_free(grp->pwe);
+ BN_free(grp->order);
+ BN_free(grp->prime);
+ os_free(grp);
+ grp = 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 *commit_peer, u8 *commit_server,
+ u32 *ciphersuite, u8 *msk, u8 *emsk)
+{
+ HMAC_CTX ctx;
+ u8 mk[SHA256_DIGEST_LENGTH], *cruft;
+ u8 session_id[SHA256_DIGEST_LENGTH + 1];
+ u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
+
+ 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;
+ H_Init(&ctx);
+ H_Update(&ctx, (u8 *)ciphersuite, sizeof(u32));
+ BN_bn2bin(peer_scalar, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(grp->order));
+ BN_bn2bin(server_scalar, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(grp->order));
+ H_Final(&ctx, &session_id[1]);
+
+ /* then compute MK = H(k | commit-peer | commit-server) */
+ H_Init(&ctx);
+ os_memset(cruft, 0, BN_num_bytes(grp->prime));
+ BN_bn2bin(k, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(grp->prime));
+ H_Update(&ctx, commit_peer, SHA256_DIGEST_LENGTH);
+ H_Update(&ctx, commit_server, SHA256_DIGEST_LENGTH);
+ H_Final(&ctx, mk);
+
+ /* stretch the mk with the session-id to get MSK | EMSK */
+ eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH,
+ session_id, SHA256_DIGEST_LENGTH+1,
+ msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8);
+
+ os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
+ os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
+
+ os_free(cruft);
+
+ return 1;
+}
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
new file mode 100644
index 0000000..971386d
--- /dev/null
+++ b/src/eap_common/eap_pwd_common.h
@@ -0,0 +1,79 @@
+/*
+ * EAP server/peer: EAP-pwd shared definitions
+ * Copyright (c) 2009, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_PWD_COMMON_H
+#define EAP_PWD_COMMON_H
+
+#include <openssl/bn.h>
+#include <openssl/sha.h>
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+
+/*
+ * 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
+ */
+struct eap_pwd_hdr {
+ u8 l_bit:1;
+ u8 m_bit:1;
+ u8 exch:6;
+ u8 total_length[0]; /* included when l_bit is set */
+} STRUCT_PACKED;
+
+#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)->lm_exch & 0x80)
+#define EAP_PWD_SET_LENGTH_BIT(x) ((x)->lm_exch |= 0x80)
+#define EAP_PWD_GET_MORE_BIT(x) ((x)->lm_exch & 0x40)
+#define EAP_PWD_SET_MORE_BIT(x) ((x)->lm_exch |= 0x40)
+#define EAP_PWD_GET_EXCHANGE(x) ((x)->lm_exch & 0x3f)
+#define EAP_PWD_SET_EXCHANGE(x,y) ((x)->lm_exch |= (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 *);
+void H_Init(HMAC_CTX *);
+void H_Update(HMAC_CTX *, const u8 *, int);
+void H_Final(HMAC_CTX *, u8 *);
+
+#endif /* EAP_PWD_COMMON_H */
diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c
new file mode 100644
index 0000000..9002b0c
--- /dev/null
+++ b/src/eap_common/eap_sake_common.c
@@ -0,0 +1,393 @@
+/*
+ * EAP server/peer: EAP-SAKE shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpabuf.h"
+#include "crypto/sha1.h"
+#include "eap_defs.h"
+#include "eap_sake_common.h"
+
+
+static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr,
+ const u8 *pos)
+{
+ size_t i;
+
+ switch (pos[0]) {
+ case EAP_SAKE_AT_RAND_S:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_S");
+ if (pos[1] != 2 + EAP_SAKE_RAND_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_S with "
+ "invalid length %d", pos[1]);
+ return -1;
+ }
+ attr->rand_s = pos + 2;
+ break;
+ case EAP_SAKE_AT_RAND_P:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_P");
+ if (pos[1] != 2 + EAP_SAKE_RAND_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_P with "
+ "invalid length %d", pos[1]);
+ return -1;
+ }
+ attr->rand_p = pos + 2;
+ break;
+ case EAP_SAKE_AT_MIC_S:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_S");
+ if (pos[1] != 2 + EAP_SAKE_MIC_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_S with "
+ "invalid length %d", pos[1]);
+ return -1;
+ }
+ attr->mic_s = pos + 2;
+ break;
+ case EAP_SAKE_AT_MIC_P:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_P");
+ if (pos[1] != 2 + EAP_SAKE_MIC_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_P with "
+ "invalid length %d", pos[1]);
+ return -1;
+ }
+ attr->mic_p = pos + 2;
+ break;
+ case EAP_SAKE_AT_SERVERID:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SERVERID");
+ attr->serverid = pos + 2;
+ attr->serverid_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_PEERID:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PEERID");
+ attr->peerid = pos + 2;
+ attr->peerid_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_SPI_S:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_S");
+ attr->spi_s = pos + 2;
+ attr->spi_s_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_SPI_P:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_P");
+ attr->spi_p = pos + 2;
+ attr->spi_p_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_ANY_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ANY_ID_REQ");
+ if (pos[1] != 4) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid AT_ANY_ID_REQ"
+ " length %d", pos[1]);
+ return -1;
+ }
+ attr->any_id_req = pos + 2;
+ break;
+ case EAP_SAKE_AT_PERM_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PERM_ID_REQ");
+ if (pos[1] != 4) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
+ "AT_PERM_ID_REQ length %d", pos[1]);
+ return -1;
+ }
+ attr->perm_id_req = pos + 2;
+ break;
+ case EAP_SAKE_AT_ENCR_DATA:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ENCR_DATA");
+ attr->encr_data = pos + 2;
+ attr->encr_data_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_IV:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
+ attr->iv = pos + 2;
+ attr->iv_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_PADDING:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PADDING");
+ for (i = 2; i < pos[1]; i++) {
+ if (pos[i]) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_PADDING "
+ "with non-zero pad byte");
+ return -1;
+ }
+ }
+ break;
+ case EAP_SAKE_AT_NEXT_TMPID:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_NEXT_TMPID");
+ attr->next_tmpid = pos + 2;
+ attr->next_tmpid_len = pos[1] - 2;
+ break;
+ case EAP_SAKE_AT_MSK_LIFE:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
+ if (pos[1] != 6) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
+ "AT_MSK_LIFE length %d", pos[1]);
+ return -1;
+ }
+ attr->msk_life = pos + 2;
+ break;
+ default:
+ if (pos[0] < 128) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown non-skippable"
+ " attribute %d", pos[0]);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring unknown skippable "
+ "attribute %d", pos[0]);
+ break;
+ }
+
+ if (attr->iv && !attr->encr_data) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_IV included without "
+ "AT_ENCR_DATA");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_sake_parse_attributes - Parse EAP-SAKE attributes
+ * @buf: Packet payload (starting with the first attribute)
+ * @len: Payload length
+ * @attr: Structure to be filled with found attributes
+ * Returns: 0 on success or -1 on failure
+ */
+int eap_sake_parse_attributes(const u8 *buf, size_t len,
+ struct eap_sake_parse_attr *attr)
+{
+ const u8 *pos = buf, *end = buf + len;
+
+ os_memset(attr, 0, sizeof(*attr));
+ while (pos < end) {
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Too short attribute");
+ return -1;
+ }
+
+ if (pos[1] < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid attribute "
+ "length (%d)", pos[1]);
+ return -1;
+ }
+
+ if (pos + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Attribute underflow");
+ return -1;
+ }
+
+ if (eap_sake_parse_add_attr(attr, pos))
+ return -1;
+
+ pos += pos[1];
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_sake_kdf - EAP-SAKE Key Derivation Function (KDF)
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF
+ * @data: Extra data (start) to bind into the key
+ * @data_len: Length of the data
+ * @data2: Extra data (end) to bind into the key
+ * @data2_len: Length of the data2
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i.
+ */
+static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len,
+ const u8 *data2, size_t data2_len,
+ u8 *buf, size_t buf_len)
+{
+ u8 counter = 0;
+ size_t pos, plen;
+ u8 hash[SHA1_MAC_LEN];
+ size_t label_len = os_strlen(label) + 1;
+ const unsigned char *addr[4];
+ size_t len[4];
+
+ addr[0] = (u8 *) label; /* Label | Y */
+ len[0] = label_len;
+ addr[1] = data; /* Msg[start] */
+ len[1] = data_len;
+ addr[2] = data2; /* Msg[end] */
+ len[2] = data2_len;
+ addr[3] = &counter; /* Length */
+ len[3] = 1;
+
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ if (plen >= SHA1_MAC_LEN) {
+ hmac_sha1_vector(key, key_len, 4, addr, len,
+ &buf[pos]);
+ pos += SHA1_MAC_LEN;
+ } else {
+ hmac_sha1_vector(key, key_len, 4, addr, len,
+ hash);
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+}
+
+
+/**
+ * eap_sake_derive_keys - Derive EAP-SAKE keys
+ * @root_secret_a: 16-byte Root-Secret-A
+ * @root_secret_b: 16-byte Root-Secret-B
+ * @rand_s: 16-byte RAND_S
+ * @rand_p: 16-byte RAND_P
+ * @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16])
+ * @msk: Buffer for 64-byte MSK
+ * @emsk: Buffer for 64-byte EMSK
+ *
+ * This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6.
+ */
+void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+ const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk,
+ u8 *emsk)
+{
+ u8 sms_a[EAP_SAKE_SMS_LEN];
+ u8 sms_b[EAP_SAKE_SMS_LEN];
+ u8 key_buf[EAP_MSK_LEN + EAP_EMSK_LEN];
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Deriving keys");
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A",
+ root_secret_a, EAP_SAKE_ROOT_SECRET_LEN);
+ eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN,
+ "SAKE Master Secret A",
+ rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
+ sms_a, EAP_SAKE_SMS_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN);
+ eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key",
+ rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
+ tek, EAP_SAKE_TEK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth",
+ tek, EAP_SAKE_TEK_AUTH_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher",
+ tek + EAP_SAKE_TEK_AUTH_LEN, EAP_SAKE_TEK_CIPHER_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B",
+ root_secret_b, EAP_SAKE_ROOT_SECRET_LEN);
+ eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN,
+ "SAKE Master Secret B",
+ rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
+ sms_b, EAP_SAKE_SMS_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN);
+ eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key",
+ rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
+ key_buf, sizeof(key_buf));
+ os_memcpy(msk, key_buf, EAP_MSK_LEN);
+ os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN);
+}
+
+
+/**
+ * eap_sake_compute_mic - Compute EAP-SAKE MIC for an EAP packet
+ * @tek_auth: 16-byte TEK-Auth
+ * @rand_s: 16-byte RAND_S
+ * @rand_p: 16-byte RAND_P
+ * @serverid: SERVERID
+ * @serverid_len: SERVERID length
+ * @peerid: PEERID
+ * @peerid_len: PEERID length
+ * @peer: MIC calculation for 0 = Server, 1 = Peer message
+ * @eap: EAP packet
+ * @eap_len: EAP packet length
+ * @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len])
+ * @mic: Buffer for the computed 16-byte MIC
+ */
+int eap_sake_compute_mic(const u8 *tek_auth,
+ const u8 *rand_s, const u8 *rand_p,
+ const u8 *serverid, size_t serverid_len,
+ const u8 *peerid, size_t peerid_len,
+ int peer, const u8 *eap, size_t eap_len,
+ const u8 *mic_pos, u8 *mic)
+{
+ u8 _rand[2 * EAP_SAKE_RAND_LEN];
+ u8 *tmp, *pos;
+ size_t tmplen;
+
+ tmplen = serverid_len + 1 + peerid_len + 1 + eap_len;
+ tmp = os_malloc(tmplen);
+ if (tmp == NULL)
+ return -1;
+ pos = tmp;
+ if (peer) {
+ if (peerid) {
+ os_memcpy(pos, peerid, peerid_len);
+ pos += peerid_len;
+ }
+ *pos++ = 0x00;
+ if (serverid) {
+ os_memcpy(pos, serverid, serverid_len);
+ pos += serverid_len;
+ }
+ *pos++ = 0x00;
+
+ os_memcpy(_rand, rand_s, EAP_SAKE_RAND_LEN);
+ os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_p,
+ EAP_SAKE_RAND_LEN);
+ } else {
+ if (serverid) {
+ os_memcpy(pos, serverid, serverid_len);
+ pos += serverid_len;
+ }
+ *pos++ = 0x00;
+ if (peerid) {
+ os_memcpy(pos, peerid, peerid_len);
+ pos += peerid_len;
+ }
+ *pos++ = 0x00;
+
+ os_memcpy(_rand, rand_p, EAP_SAKE_RAND_LEN);
+ os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_s,
+ EAP_SAKE_RAND_LEN);
+ }
+
+ os_memcpy(pos, eap, eap_len);
+ os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN);
+
+ eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN,
+ peer ? "Peer MIC" : "Server MIC",
+ _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen,
+ mic, EAP_SAKE_MIC_LEN);
+
+ os_free(tmp);
+
+ return 0;
+}
+
+
+void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data,
+ size_t len)
+{
+ wpabuf_put_u8(buf, type);
+ wpabuf_put_u8(buf, 2 + len); /* Length; including attr header */
+ if (data)
+ wpabuf_put_data(buf, data, len);
+ else
+ os_memset(wpabuf_put(buf, len), 0, len);
+}
diff --git a/src/eap_common/eap_sake_common.h b/src/eap_common/eap_sake_common.h
new file mode 100644
index 0000000..201e207
--- /dev/null
+++ b/src/eap_common/eap_sake_common.h
@@ -0,0 +1,102 @@
+/*
+ * EAP server/peer: EAP-SAKE shared routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_SAKE_COMMON_H
+#define EAP_SAKE_COMMON_H
+
+#define EAP_SAKE_VERSION 2
+
+#define EAP_SAKE_SUBTYPE_CHALLENGE 1
+#define EAP_SAKE_SUBTYPE_CONFIRM 2
+#define EAP_SAKE_SUBTYPE_AUTH_REJECT 3
+#define EAP_SAKE_SUBTYPE_IDENTITY 4
+
+#define EAP_SAKE_AT_RAND_S 1
+#define EAP_SAKE_AT_RAND_P 2
+#define EAP_SAKE_AT_MIC_S 3
+#define EAP_SAKE_AT_MIC_P 4
+#define EAP_SAKE_AT_SERVERID 5
+#define EAP_SAKE_AT_PEERID 6
+#define EAP_SAKE_AT_SPI_S 7
+#define EAP_SAKE_AT_SPI_P 8
+#define EAP_SAKE_AT_ANY_ID_REQ 9
+#define EAP_SAKE_AT_PERM_ID_REQ 10
+#define EAP_SAKE_AT_ENCR_DATA 128
+#define EAP_SAKE_AT_IV 129
+#define EAP_SAKE_AT_PADDING 130
+#define EAP_SAKE_AT_NEXT_TMPID 131
+#define EAP_SAKE_AT_MSK_LIFE 132
+
+#define EAP_SAKE_RAND_LEN 16
+#define EAP_SAKE_MIC_LEN 16
+#define EAP_SAKE_ROOT_SECRET_LEN 16
+#define EAP_SAKE_SMS_LEN 16
+#define EAP_SAKE_TEK_AUTH_LEN 16
+#define EAP_SAKE_TEK_CIPHER_LEN 16
+#define EAP_SAKE_TEK_LEN (EAP_SAKE_TEK_AUTH_LEN + EAP_SAKE_TEK_CIPHER_LEN)
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_sake_hdr {
+ u8 version; /* EAP_SAKE_VERSION */
+ u8 session_id;
+ u8 subtype;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+struct eap_sake_parse_attr {
+ const u8 *rand_s;
+ const u8 *rand_p;
+ const u8 *mic_s;
+ const u8 *mic_p;
+ const u8 *serverid;
+ size_t serverid_len;
+ const u8 *peerid;
+ size_t peerid_len;
+ const u8 *spi_s;
+ size_t spi_s_len;
+ const u8 *spi_p;
+ size_t spi_p_len;
+ const u8 *any_id_req;
+ const u8 *perm_id_req;
+ const u8 *encr_data;
+ size_t encr_data_len;
+ const u8 *iv;
+ size_t iv_len;
+ const u8 *next_tmpid;
+ size_t next_tmpid_len;
+ const u8 *msk_life;
+};
+
+int eap_sake_parse_attributes(const u8 *buf, size_t len,
+ struct eap_sake_parse_attr *attr);
+void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+ const u8 *rand_s, const u8 *rand_p,
+ u8 *tek, u8 *msk, u8 *emsk);
+int eap_sake_compute_mic(const u8 *tek_auth,
+ const u8 *rand_s, const u8 *rand_p,
+ const u8 *serverid, size_t serverid_len,
+ const u8 *peerid, size_t peerid_len,
+ int peer, const u8 *eap, size_t eap_len,
+ const u8 *mic_pos, u8 *mic);
+void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data,
+ size_t len);
+
+#endif /* EAP_SAKE_COMMON_H */
diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c
new file mode 100644
index 0000000..0b37b0b
--- /dev/null
+++ b/src/eap_common/eap_sim_common.c
@@ -0,0 +1,1215 @@
+/*
+ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpabuf.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "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, <access network identity>)
+ */
+ /* TODO: CK', IK' generation should really be moved into the actual
+ * AKA procedure with network name passed in there and option to use
+ * AMF separation bit = 1 (3GPP TS 33.401). */
+
+ /* Change Request 33.402 CR 0033 to version 8.1.1 from
+ * 3GPP TSG-SA WG3 Meeting #53 in September 2008:
+ *
+ * CK' || IK' = HMAC-SHA-256(Key, S)
+ * S = FC || P0 || L0 || P1 || L1 || ... || Pn || Ln
+ * Key = CK || IK
+ * FC = 0x20
+ * P0 = access network identity (3GPP TS 24.302)
+ * L0 = length of acceess network identity (2 octets, big endian)
+ * P1 = SQN xor AK (if AK is not used, AK is treaded as 000..0
+ * L1 = 0x00 0x06
+ */
+
+ fc = 0x20;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA': Derive (CK',IK') from (CK,IK)");
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK", ck, EAP_AKA_CK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK", ik, EAP_AKA_IK_LEN);
+ wpa_printf(MSG_DEBUG, "EAP-AKA': FC = 0x%x", fc);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': P0 = Access network identity",
+ network_name, network_name_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-AKA': P1 = SQN xor AK", sqn_ak, 6);
+
+ os_memcpy(key, ck, EAP_AKA_CK_LEN);
+ os_memcpy(key + EAP_AKA_CK_LEN, ik, EAP_AKA_IK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': Key = CK || IK",
+ key, sizeof(key));
+
+ addr[0] = &fc;
+ len[0] = 1;
+ addr[1] = network_name;
+ len[1] = network_name_len;
+ WPA_PUT_BE16(l0, network_name_len);
+ addr[2] = l0;
+ len[2] = 2;
+ addr[3] = sqn_ak;
+ len[3] = 6;
+ WPA_PUT_BE16(l1, 6);
+ addr[4] = l1;
+ len[4] = 2;
+
+ hmac_sha256_vector(key, sizeof(key), 5, addr, len, hash);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': KDF output (CK' || IK')",
+ hash, sizeof(hash));
+
+ os_memcpy(ck, hash, EAP_AKA_CK_LEN);
+ os_memcpy(ik, hash + EAP_AKA_CK_LEN, EAP_AKA_IK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK'", ck, EAP_AKA_CK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK'", ik, EAP_AKA_IK_LEN);
+}
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+
+
+int eap_sim_parse_attr(const u8 *start, const u8 *end,
+ struct eap_sim_attrs *attr, int aka, int encr)
+{
+ const u8 *pos = start, *apos;
+ size_t alen, plen, i, list_len;
+
+ os_memset(attr, 0, sizeof(*attr));
+ attr->id_req = NO_ID_REQ;
+ attr->notification = -1;
+ attr->counter = -1;
+ attr->selected_version = -1;
+ attr->client_error_code = -1;
+
+ while (pos < end) {
+ if (pos + 2 > end) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)");
+ return -1;
+ }
+ wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d",
+ pos[0], pos[1] * 4);
+ if (pos + pos[1] * 4 > end) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow "
+ "(pos=%p len=%d end=%p)",
+ pos, pos[1] * 4, end);
+ return -1;
+ }
+ if (pos[1] == 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Attribute underflow");
+ return -1;
+ }
+ apos = pos + 2;
+ alen = pos[1] * 4 - 2;
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data",
+ apos, alen);
+
+ switch (pos[0]) {
+ case EAP_SIM_AT_RAND:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND");
+ apos += 2;
+ alen -= 2;
+ if ((!aka && (alen % GSM_RAND_LEN)) ||
+ (aka && alen != EAP_AKA_RAND_LEN)) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND"
+ " (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->rand = apos;
+ attr->num_chal = alen / GSM_RAND_LEN;
+ break;
+ case EAP_SIM_AT_AUTN:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN");
+ if (!aka) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: "
+ "Unexpected AT_AUTN");
+ return -1;
+ }
+ apos += 2;
+ alen -= 2;
+ if (alen != EAP_AKA_AUTN_LEN) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN"
+ " (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->autn = apos;
+ break;
+ case EAP_SIM_AT_PADDING:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_PADDING");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING");
+ for (i = 2; i < alen; i++) {
+ if (apos[i] != 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) "
+ "AT_PADDING used a non-zero"
+ " padding byte");
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: "
+ "(encr) padding bytes",
+ apos + 2, alen - 2);
+ return -1;
+ }
+ }
+ break;
+ case EAP_SIM_AT_NONCE_MT:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT");
+ if (alen != 2 + EAP_SIM_NONCE_MT_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_NONCE_MT length");
+ return -1;
+ }
+ attr->nonce_mt = apos + 2;
+ break;
+ case EAP_SIM_AT_PERMANENT_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ");
+ attr->id_req = PERMANENT_ID;
+ break;
+ case EAP_SIM_AT_MAC:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC");
+ if (alen != 2 + EAP_SIM_MAC_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC "
+ "length");
+ return -1;
+ }
+ attr->mac = apos + 2;
+ break;
+ case EAP_SIM_AT_NOTIFICATION:
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_NOTIFICATION length %lu",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->notification = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d",
+ attr->notification);
+ break;
+ case EAP_SIM_AT_ANY_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ");
+ attr->id_req = ANY_ID;
+ break;
+ case EAP_SIM_AT_IDENTITY:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY");
+ plen = WPA_GET_BE16(apos);
+ apos += 2;
+ alen -= 2;
+ if (plen > alen) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_IDENTITY (Actual Length %lu, "
+ "remaining length %lu)",
+ (unsigned long) plen,
+ (unsigned long) alen);
+ return -1;
+ }
+
+ attr->identity = apos;
+ attr->identity_len = plen;
+ break;
+ case EAP_SIM_AT_VERSION_LIST:
+ if (aka) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: "
+ "Unexpected AT_VERSION_LIST");
+ return -1;
+ }
+ list_len = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST");
+ if (list_len < 2 || list_len > alen - 2) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
+ "AT_VERSION_LIST (list_len=%lu "
+ "attr_len=%lu)",
+ (unsigned long) list_len,
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->version_list = apos + 2;
+ attr->version_list_len = list_len;
+ break;
+ case EAP_SIM_AT_SELECTED_VERSION:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION");
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_SELECTED_VERSION length %lu",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->selected_version = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION "
+ "%d", attr->selected_version);
+ break;
+ case EAP_SIM_AT_FULLAUTH_ID_REQ:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ");
+ attr->id_req = FULLAUTH_ID;
+ break;
+ case EAP_SIM_AT_COUNTER:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_COUNTER");
+ return -1;
+ }
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+ "AT_COUNTER (alen=%lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->counter = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d",
+ attr->counter);
+ break;
+ case EAP_SIM_AT_COUNTER_TOO_SMALL:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_COUNTER_TOO_SMALL");
+ return -1;
+ }
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+ "AT_COUNTER_TOO_SMALL (alen=%lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+ "AT_COUNTER_TOO_SMALL");
+ attr->counter_too_small = 1;
+ break;
+ case EAP_SIM_AT_NONCE_S:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_NONCE_S");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+ "AT_NONCE_S");
+ if (alen != 2 + EAP_SIM_NONCE_S_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+ "AT_NONCE_S (alen=%lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->nonce_s = apos + 2;
+ break;
+ case EAP_SIM_AT_CLIENT_ERROR_CODE:
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_CLIENT_ERROR_CODE length %lu",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->client_error_code = apos[0] * 256 + apos[1];
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE "
+ "%d", attr->client_error_code);
+ break;
+ case EAP_SIM_AT_IV:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV");
+ if (alen != 2 + EAP_SIM_MAC_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV "
+ "length %lu", (unsigned long) alen);
+ return -1;
+ }
+ attr->iv = apos + 2;
+ break;
+ case EAP_SIM_AT_ENCR_DATA:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA");
+ attr->encr_data = apos + 2;
+ attr->encr_data_len = alen - 2;
+ if (attr->encr_data_len % 16) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_ENCR_DATA length %lu",
+ (unsigned long)
+ attr->encr_data_len);
+ return -1;
+ }
+ break;
+ case EAP_SIM_AT_NEXT_PSEUDONYM:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_NEXT_PSEUDONYM");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+ "AT_NEXT_PSEUDONYM");
+ plen = apos[0] * 256 + apos[1];
+ if (plen > alen - 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
+ " AT_NEXT_PSEUDONYM (actual"
+ " len %lu, attr len %lu)",
+ (unsigned long) plen,
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->next_pseudonym = pos + 4;
+ attr->next_pseudonym_len = plen;
+ break;
+ case EAP_SIM_AT_NEXT_REAUTH_ID:
+ if (!encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+ "AT_NEXT_REAUTH_ID");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+ "AT_NEXT_REAUTH_ID");
+ plen = apos[0] * 256 + apos[1];
+ if (plen > alen - 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
+ " AT_NEXT_REAUTH_ID (actual"
+ " len %lu, attr len %lu)",
+ (unsigned long) plen,
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->next_reauth_id = pos + 4;
+ attr->next_reauth_id_len = plen;
+ break;
+ case EAP_SIM_AT_RES:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES");
+ attr->res_len_bits = WPA_GET_BE16(apos);
+ apos += 2;
+ alen -= 2;
+ if (!aka || alen < EAP_AKA_MIN_RES_LEN ||
+ alen > EAP_AKA_MAX_RES_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RES "
+ "(len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->res = apos;
+ attr->res_len = alen;
+ break;
+ case EAP_SIM_AT_AUTS:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTS");
+ if (!aka) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: "
+ "Unexpected AT_AUTS");
+ return -1;
+ }
+ if (alen != EAP_AKA_AUTS_LEN) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTS"
+ " (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->auts = apos;
+ break;
+ case EAP_SIM_AT_CHECKCODE:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_CHECKCODE");
+ if (!aka) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: "
+ "Unexpected AT_CHECKCODE");
+ return -1;
+ }
+ apos += 2;
+ alen -= 2;
+ if (alen != 0 && alen != EAP_AKA_CHECKCODE_LEN &&
+ alen != EAP_AKA_PRIME_CHECKCODE_LEN) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
+ "AT_CHECKCODE (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->checkcode = apos;
+ attr->checkcode_len = alen;
+ break;
+ case EAP_SIM_AT_RESULT_IND:
+ if (encr) {
+ wpa_printf(MSG_ERROR, "EAP-SIM: Encrypted "
+ "AT_RESULT_IND");
+ return -1;
+ }
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+ "AT_RESULT_IND (alen=%lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND");
+ attr->result_ind = 1;
+ break;
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+ case EAP_SIM_AT_KDF_INPUT:
+ if (aka != 2) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
+ "AT_KDF_INPUT");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT");
+ plen = WPA_GET_BE16(apos);
+ apos += 2;
+ alen -= 2;
+ if (plen > alen) {
+ wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
+ "AT_KDF_INPUT (Actual Length %lu, "
+ "remaining length %lu)",
+ (unsigned long) plen,
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->kdf_input = apos;
+ attr->kdf_input_len = plen;
+ break;
+ case EAP_SIM_AT_KDF:
+ if (aka != 2) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
+ "AT_KDF");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF");
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
+ "AT_KDF (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA': Too many "
+ "AT_KDF attributes - ignore this");
+ continue;
+ }
+ attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos);
+ attr->kdf_count++;
+ break;
+ case EAP_SIM_AT_BIDDING:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AT_BIDDING");
+ if (alen != 2) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
+ "AT_BIDDING (len %lu)",
+ (unsigned long) alen);
+ return -1;
+ }
+ attr->bidding = apos;
+ break;
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+ default:
+ if (pos[0] < 128) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized "
+ "non-skippable attribute %d",
+ pos[0]);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable"
+ " attribute %d ignored", pos[0]);
+ break;
+ }
+
+ pos += pos[1] * 4;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully "
+ "(aka=%d encr=%d)", aka, encr);
+
+ return 0;
+}
+
+
+u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
+ size_t encr_data_len, const u8 *iv,
+ struct eap_sim_attrs *attr, int aka)
+{
+ u8 *decrypted;
+
+ if (!iv) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV");
+ return NULL;
+ }
+
+ decrypted = os_malloc(encr_data_len);
+ if (decrypted == NULL)
+ return NULL;
+ os_memcpy(decrypted, encr_data, encr_data_len);
+
+ if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) {
+ os_free(decrypted);
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA",
+ decrypted, encr_data_len);
+
+ if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr,
+ aka, 1)) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse "
+ "decrypted AT_ENCR_DATA");
+ os_free(decrypted);
+ return NULL;
+ }
+
+ return decrypted;
+}
+
+
+#define EAP_SIM_INIT_LEN 128
+
+struct eap_sim_msg {
+ struct wpabuf *buf;
+ size_t mac, iv, encr; /* index from buf */
+ int type;
+};
+
+
+struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype)
+{
+ struct eap_sim_msg *msg;
+ struct eap_hdr *eap;
+ u8 *pos;
+
+ msg = os_zalloc(sizeof(*msg));
+ if (msg == NULL)
+ return NULL;
+
+ msg->type = type;
+ msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN);
+ if (msg->buf == NULL) {
+ os_free(msg);
+ return NULL;
+ }
+ eap = wpabuf_put(msg->buf, sizeof(*eap));
+ eap->code = code;
+ eap->identifier = id;
+
+ pos = wpabuf_put(msg->buf, 4);
+ *pos++ = type;
+ *pos++ = subtype;
+ *pos++ = 0; /* Reserved */
+ *pos++ = 0; /* Reserved */
+
+ return msg;
+}
+
+
+struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
+ const u8 *extra, size_t extra_len)
+{
+ struct eap_hdr *eap;
+ struct wpabuf *buf;
+
+ if (msg == NULL)
+ return NULL;
+
+ eap = wpabuf_mhead(msg->buf);
+ eap->length = host_to_be16(wpabuf_len(msg->buf));
+
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+ if (k_aut && msg->mac && msg->type == EAP_TYPE_AKA_PRIME) {
+ eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf),
+ (u8 *) wpabuf_mhead(msg->buf) +
+ msg->mac, extra, extra_len);
+ } else
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+ if (k_aut && msg->mac) {
+ eap_sim_add_mac(k_aut, (u8 *) wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf),
+ (u8 *) wpabuf_mhead(msg->buf) + msg->mac,
+ extra, extra_len);
+ }
+
+ buf = msg->buf;
+ os_free(msg);
+ return buf;
+}
+
+
+void eap_sim_msg_free(struct eap_sim_msg *msg)
+{
+ if (msg) {
+ wpabuf_free(msg->buf);
+ os_free(msg);
+ }
+}
+
+
+u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
+ const u8 *data, size_t len)
+{
+ int attr_len = 2 + len;
+ int pad_len;
+ u8 *start;
+
+ if (msg == NULL)
+ return NULL;
+
+ pad_len = (4 - attr_len % 4) % 4;
+ attr_len += pad_len;
+ if (wpabuf_resize(&msg->buf, attr_len))
+ return NULL;
+ start = wpabuf_put(msg->buf, 0);
+ wpabuf_put_u8(msg->buf, attr);
+ wpabuf_put_u8(msg->buf, attr_len / 4);
+ wpabuf_put_data(msg->buf, data, len);
+ if (pad_len)
+ os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
+ return start;
+}
+
+
+u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value,
+ const u8 *data, size_t len)
+{
+ int attr_len = 4 + len;
+ int pad_len;
+ u8 *start;
+
+ if (msg == NULL)
+ return NULL;
+
+ pad_len = (4 - attr_len % 4) % 4;
+ attr_len += pad_len;
+ if (wpabuf_resize(&msg->buf, attr_len))
+ return NULL;
+ start = wpabuf_put(msg->buf, 0);
+ wpabuf_put_u8(msg->buf, attr);
+ wpabuf_put_u8(msg->buf, attr_len / 4);
+ wpabuf_put_be16(msg->buf, value);
+ if (data)
+ wpabuf_put_data(msg->buf, data, len);
+ else
+ wpabuf_put(msg->buf, len);
+ if (pad_len)
+ os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
+ return start;
+}
+
+
+u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr)
+{
+ u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN);
+ if (pos)
+ msg->mac = (pos - wpabuf_head_u8(msg->buf)) + 4;
+ return pos;
+}
+
+
+int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
+ u8 attr_encr)
+{
+ u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN);
+ if (pos == NULL)
+ return -1;
+ msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4;
+ if (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/src/eap_common/eap_sim_common.h b/src/eap_common/eap_sim_common.h
new file mode 100644
index 0000000..48c8eaa
--- /dev/null
+++ b/src/eap_common/eap_sim_common.h
@@ -0,0 +1,235 @@
+/*
+ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_SIM_COMMON_H
+#define EAP_SIM_COMMON_H
+
+#define EAP_SIM_NONCE_S_LEN 16
+#define EAP_SIM_NONCE_MT_LEN 16
+#define EAP_SIM_MAC_LEN 16
+#define EAP_SIM_MK_LEN 20
+#define EAP_SIM_K_AUT_LEN 16
+#define EAP_SIM_K_ENCR_LEN 16
+#define EAP_SIM_KEYING_DATA_LEN 64
+#define EAP_SIM_IV_LEN 16
+#define EAP_SIM_KC_LEN 8
+#define EAP_SIM_SRES_LEN 4
+
+#define GSM_RAND_LEN 16
+
+#define EAP_SIM_VERSION 1
+
+/* EAP-SIM Subtypes */
+#define EAP_SIM_SUBTYPE_START 10
+#define EAP_SIM_SUBTYPE_CHALLENGE 11
+#define EAP_SIM_SUBTYPE_NOTIFICATION 12
+#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13
+#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14
+
+/* AT_CLIENT_ERROR_CODE error codes */
+#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0
+#define EAP_SIM_UNSUPPORTED_VERSION 1
+#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2
+#define EAP_SIM_RAND_NOT_FRESH 3
+
+#define EAP_SIM_MAX_FAST_REAUTHS 1000
+
+#define EAP_SIM_MAX_CHAL 3
+
+
+/* EAP-AKA Subtypes */
+#define EAP_AKA_SUBTYPE_CHALLENGE 1
+#define EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT 2
+#define EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE 4
+#define EAP_AKA_SUBTYPE_IDENTITY 5
+#define EAP_AKA_SUBTYPE_NOTIFICATION 12
+#define EAP_AKA_SUBTYPE_REAUTHENTICATION 13
+#define EAP_AKA_SUBTYPE_CLIENT_ERROR 14
+
+/* AT_CLIENT_ERROR_CODE error codes */
+#define EAP_AKA_UNABLE_TO_PROCESS_PACKET 0
+
+#define EAP_AKA_RAND_LEN 16
+#define EAP_AKA_AUTN_LEN 16
+#define EAP_AKA_AUTS_LEN 14
+#define EAP_AKA_RES_MAX_LEN 16
+#define EAP_AKA_IK_LEN 16
+#define EAP_AKA_CK_LEN 16
+#define EAP_AKA_MAX_FAST_REAUTHS 1000
+#define EAP_AKA_MIN_RES_LEN 4
+#define EAP_AKA_MAX_RES_LEN 16
+#define EAP_AKA_CHECKCODE_LEN 20
+
+#define EAP_AKA_PRIME_K_AUT_LEN 32
+#define EAP_AKA_PRIME_CHECKCODE_LEN 32
+#define EAP_AKA_PRIME_K_RE_LEN 32
+
+struct wpabuf;
+
+void eap_sim_derive_mk(const u8 *identity, size_t identity_len,
+ const u8 *nonce_mt, u16 selected_version,
+ const u8 *ver_list, size_t ver_list_len,
+ int num_chal, const u8 *kc, u8 *mk);
+void eap_aka_derive_mk(const u8 *identity, size_t identity_len,
+ const u8 *ik, const u8 *ck, u8 *mk);
+int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk,
+ u8 *emsk);
+int eap_sim_derive_keys_reauth(u16 _counter,
+ const u8 *identity, size_t identity_len,
+ const u8 *nonce_s, const u8 *mk, u8 *msk,
+ u8 *emsk);
+int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
+ const u8 *mac, const u8 *extra, size_t extra_len);
+void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
+ const u8 *extra, size_t extra_len);
+
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
+ const u8 *ik, const u8 *ck, u8 *k_encr,
+ u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk);
+int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+ const u8 *identity, size_t identity_len,
+ const u8 *nonce_s, u8 *msk, u8 *emsk);
+int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
+ const u8 *mac, const u8 *extra,
+ size_t extra_len);
+void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
+ u8 *mac, const u8 *extra, size_t extra_len);
+
+void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak,
+ const u8 *network_name,
+ size_t network_name_len);
+#else /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+static inline void eap_aka_prime_derive_keys(const u8 *identity,
+ size_t identity_len,
+ const u8 *ik, const u8 *ck,
+ u8 *k_encr, u8 *k_aut, u8 *k_re,
+ u8 *msk, u8 *emsk)
+{
+}
+
+static inline int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+ const u8 *identity,
+ size_t identity_len,
+ const u8 *nonce_s, u8 *msk,
+ u8 *emsk)
+{
+ return -1;
+}
+
+static inline int eap_sim_verify_mac_sha256(const u8 *k_aut,
+ const struct wpabuf *req,
+ const u8 *mac, const u8 *extra,
+ size_t extra_len)
+{
+ return -1;
+}
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+
+
+/* EAP-SIM/AKA Attributes (0..127 non-skippable) */
+#define EAP_SIM_AT_RAND 1
+#define EAP_SIM_AT_AUTN 2 /* only AKA */
+#define EAP_SIM_AT_RES 3 /* only AKA, only peer->server */
+#define EAP_SIM_AT_AUTS 4 /* only AKA, only peer->server */
+#define EAP_SIM_AT_PADDING 6 /* only encrypted */
+#define EAP_SIM_AT_NONCE_MT 7 /* only SIM, only send */
+#define EAP_SIM_AT_PERMANENT_ID_REQ 10
+#define EAP_SIM_AT_MAC 11
+#define EAP_SIM_AT_NOTIFICATION 12
+#define EAP_SIM_AT_ANY_ID_REQ 13
+#define EAP_SIM_AT_IDENTITY 14 /* only send */
+#define EAP_SIM_AT_VERSION_LIST 15 /* only SIM */
+#define EAP_SIM_AT_SELECTED_VERSION 16 /* only SIM */
+#define EAP_SIM_AT_FULLAUTH_ID_REQ 17
+#define EAP_SIM_AT_COUNTER 19 /* only encrypted */
+#define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */
+#define EAP_SIM_AT_NONCE_S 21 /* only encrypted */
+#define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */
+#define EAP_SIM_AT_KDF_INPUT 23 /* only AKA' */
+#define EAP_SIM_AT_KDF 24 /* only AKA' */
+#define EAP_SIM_AT_IV 129
+#define EAP_SIM_AT_ENCR_DATA 130
+#define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */
+#define EAP_SIM_AT_NEXT_REAUTH_ID 133 /* only encrypted */
+#define EAP_SIM_AT_CHECKCODE 134 /* only AKA */
+#define EAP_SIM_AT_RESULT_IND 135
+#define EAP_SIM_AT_BIDDING 136
+
+/* AT_NOTIFICATION notification code values */
+#define EAP_SIM_GENERAL_FAILURE_AFTER_AUTH 0
+#define EAP_SIM_TEMPORARILY_DENIED 1026
+#define EAP_SIM_NOT_SUBSCRIBED 1031
+#define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384
+#define EAP_SIM_SUCCESS 32768
+
+/* EAP-AKA' AT_KDF Key Derivation Function values */
+#define EAP_AKA_PRIME_KDF 1
+
+/* AT_BIDDING flags */
+#define EAP_AKA_BIDDING_FLAG_D 0x8000
+
+
+enum eap_sim_id_req {
+ NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID
+};
+
+
+struct eap_sim_attrs {
+ const u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s;
+ const u8 *next_pseudonym, *next_reauth_id;
+ const u8 *nonce_mt, *identity, *res, *auts;
+ const u8 *checkcode;
+ const u8 *kdf_input;
+ const u8 *bidding;
+ size_t num_chal, version_list_len, encr_data_len;
+ size_t next_pseudonym_len, next_reauth_id_len, identity_len, res_len;
+ size_t res_len_bits;
+ size_t checkcode_len;
+ size_t kdf_input_len;
+ enum eap_sim_id_req id_req;
+ int notification, counter, selected_version, client_error_code;
+ int counter_too_small;
+ int result_ind;
+#define EAP_AKA_PRIME_KDF_MAX 10
+ u16 kdf[EAP_AKA_PRIME_KDF_MAX];
+ size_t kdf_count;
+};
+
+int eap_sim_parse_attr(const u8 *start, const u8 *end,
+ struct eap_sim_attrs *attr, int aka, int encr);
+u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
+ size_t encr_data_len, const u8 *iv,
+ struct eap_sim_attrs *attr, int aka);
+
+
+struct eap_sim_msg;
+
+struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype);
+struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
+ const u8 *extra, size_t extra_len);
+void eap_sim_msg_free(struct eap_sim_msg *msg);
+u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
+ const u8 *data, size_t len);
+u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr,
+ u16 value, const u8 *data, size_t len);
+u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr);
+int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
+ u8 attr_encr);
+int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr,
+ int attr_pad);
+
+void eap_sim_report_notification(void *msg_ctx, int notification, int aka);
+
+#endif /* EAP_SIM_COMMON_H */
diff --git a/src/eap_common/eap_tlv_common.h b/src/eap_common/eap_tlv_common.h
new file mode 100644
index 0000000..f86015d
--- /dev/null
+++ b/src/eap_common/eap_tlv_common.h
@@ -0,0 +1,118 @@
+/*
+ * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_TLV_COMMON_H
+#define EAP_TLV_COMMON_H
+
+/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */
+#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */
+#define EAP_TLV_NAK_TLV 4
+#define EAP_TLV_ERROR_CODE_TLV 5
+#define EAP_TLV_CONNECTION_BINDING_TLV 6
+#define EAP_TLV_VENDOR_SPECIFIC_TLV 7
+#define EAP_TLV_URI_TLV 8
+#define EAP_TLV_EAP_PAYLOAD_TLV 9
+#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10
+#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */
+#define EAP_TLV_CRYPTO_BINDING_TLV 12
+#define EAP_TLV_CALLING_STATION_ID_TLV 13
+#define EAP_TLV_CALLED_STATION_ID_TLV 14
+#define EAP_TLV_NAS_PORT_TYPE_TLV 15
+#define EAP_TLV_SERVER_IDENTIFIER_TLV 16
+#define EAP_TLV_IDENTITY_TYPE_TLV 17
+#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18
+#define EAP_TLV_REQUEST_ACTION_TLV 19
+#define EAP_TLV_PKCS7_TLV 20
+
+#define EAP_TLV_RESULT_SUCCESS 1
+#define EAP_TLV_RESULT_FAILURE 2
+
+#define EAP_TLV_TYPE_MANDATORY 0x8000
+#define EAP_TLV_TYPE_MASK 0x3fff
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_tlv_hdr {
+ be16 tlv_type;
+ be16 length;
+} STRUCT_PACKED;
+
+struct eap_tlv_nak_tlv {
+ be16 tlv_type;
+ be16 length;
+ be32 vendor_id;
+ be16 nak_type;
+} STRUCT_PACKED;
+
+struct eap_tlv_result_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 status;
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */
+struct eap_tlv_intermediate_result_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 status;
+ /* Followed by optional TLVs */
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */
+struct eap_tlv_crypto_binding_tlv {
+ be16 tlv_type;
+ be16 length;
+ u8 reserved;
+ u8 version;
+ u8 received_version;
+ u8 subtype;
+ u8 nonce[32];
+ u8 compound_mac[20];
+} STRUCT_PACKED;
+
+struct eap_tlv_pac_ack_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 pac_type;
+ be16 pac_len;
+ be16 result;
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.9 - Request-Action TLV */
+struct eap_tlv_request_action_tlv {
+ be16 tlv_type;
+ be16 length;
+ be16 action;
+} STRUCT_PACKED;
+
+/* RFC 5422, Section 4.2.6 - PAC-Type TLV */
+struct eap_tlv_pac_type_tlv {
+ be16 tlv_type; /* PAC_TYPE_PAC_TYPE */
+ be16 length;
+ be16 pac_type;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1
+
+#define EAP_TLV_ACTION_PROCESS_TLV 1
+#define EAP_TLV_ACTION_NEGOTIATE_EAP 2
+
+#endif /* EAP_TLV_COMMON_H */
diff --git a/src/eap_common/eap_ttls.h b/src/eap_common/eap_ttls.h
new file mode 100644
index 0000000..797d084
--- /dev/null
+++ b/src/eap_common/eap_ttls.h
@@ -0,0 +1,71 @@
+/*
+ * EAP server/peer: EAP-TTLS (RFC 5281)
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_TTLS_H
+#define EAP_TTLS_H
+
+struct ttls_avp {
+ be32 avp_code;
+ be32 avp_length; /* 8-bit flags, 24-bit length;
+ * length includes AVP header */
+ /* optional 32-bit Vendor-ID */
+ /* Data */
+};
+
+struct ttls_avp_vendor {
+ be32 avp_code;
+ be32 avp_length; /* 8-bit flags, 24-bit length;
+ * length includes AVP header */
+ be32 vendor_id;
+ /* Data */
+};
+
+#define AVP_FLAGS_VENDOR 0x80
+#define AVP_FLAGS_MANDATORY 0x40
+
+#define AVP_PAD(start, pos) \
+do { \
+ int __pad; \
+ __pad = (4 - (((pos) - (start)) & 3)) & 3; \
+ os_memset((pos), 0, __pad); \
+ pos += __pad; \
+} while (0)
+
+
+/* RFC 2865 */
+#define RADIUS_ATTR_USER_NAME 1
+#define RADIUS_ATTR_USER_PASSWORD 2
+#define RADIUS_ATTR_CHAP_PASSWORD 3
+#define RADIUS_ATTR_REPLY_MESSAGE 18
+#define RADIUS_ATTR_CHAP_CHALLENGE 60
+#define RADIUS_ATTR_EAP_MESSAGE 79
+
+/* RFC 2548 */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+#define RADIUS_ATTR_MS_CHAP_RESPONSE 1
+#define RADIUS_ATTR_MS_CHAP_ERROR 2
+#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6
+#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11
+#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25
+#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26
+#define RADIUS_ATTR_MS_CHAP2_CPW 27
+
+#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16
+#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50
+#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8
+#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50
+#define EAP_TTLS_CHAP_CHALLENGE_LEN 16
+#define EAP_TTLS_CHAP_PASSWORD_LEN 16
+
+#endif /* EAP_TTLS_H */
diff --git a/src/eap_common/eap_wsc_common.c b/src/eap_common/eap_wsc_common.c
new file mode 100644
index 0000000..5d4e8cc
--- /dev/null
+++ b/src/eap_common/eap_wsc_common.c
@@ -0,0 +1,39 @@
+/*
+ * EAP-WSC common routines for Wi-Fi Protected Setup
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+#include "wps/wps.h"
+#include "eap_wsc_common.h"
+
+struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code)
+{
+ struct wpabuf *msg;
+
+ msg = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, code, id);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
+ "FRAG_ACK");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/FRAG_ACK");
+ wpabuf_put_u8(msg, WSC_FRAG_ACK); /* Op-Code */
+ wpabuf_put_u8(msg, 0); /* Flags */
+
+ return msg;
+}
diff --git a/src/eap_common/eap_wsc_common.h b/src/eap_common/eap_wsc_common.h
new file mode 100644
index 0000000..fdf61d3
--- /dev/null
+++ b/src/eap_common/eap_wsc_common.h
@@ -0,0 +1,33 @@
+/*
+ * EAP-WSC definitions for Wi-Fi Protected Setup
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_WSC_COMMON_H
+#define EAP_WSC_COMMON_H
+
+#define EAP_VENDOR_TYPE_WSC 1
+
+#define WSC_FLAGS_MF 0x01
+#define WSC_FLAGS_LF 0x02
+
+#define WSC_ID_REGISTRAR "WFA-SimpleConfig-Registrar-1-0"
+#define WSC_ID_REGISTRAR_LEN 30
+#define WSC_ID_ENROLLEE "WFA-SimpleConfig-Enrollee-1-0"
+#define WSC_ID_ENROLLEE_LEN 29
+
+#define WSC_FRAGMENT_SIZE 1400
+
+
+struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code);
+
+#endif /* EAP_WSC_COMMON_H */
diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c
new file mode 100644
index 0000000..003c288
--- /dev/null
+++ b/src/eap_common/ikev2_common.c
@@ -0,0 +1,797 @@
+/*
+ * IKEv2 common routines for initiator and responder
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "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 (sizeof(ikev2_integ_algs) / sizeof(ikev2_integ_algs[0]))
+
+
+static struct ikev2_prf_alg ikev2_prf_algs[] = {
+ { PRF_HMAC_SHA1, 20, 20 },
+ { PRF_HMAC_MD5, 16, 16 }
+};
+
+#define NUM_PRF_ALGS (sizeof(ikev2_prf_algs) / sizeof(ikev2_prf_algs[0]))
+
+
+static struct ikev2_encr_alg ikev2_encr_algs[] = {
+ { ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */
+ { ENCR_3DES, 24, 8 }
+};
+
+#define NUM_ENCR_ALGS (sizeof(ikev2_encr_algs) / sizeof(ikev2_encr_algs[0]))
+
+
+const struct ikev2_integ_alg * ikev2_get_integ(int id)
+{
+ size_t i;
+
+ for (i = 0; i < NUM_INTEG_ALGS; i++) {
+ if (ikev2_integ_algs[i].id == id)
+ return &ikev2_integ_algs[i];
+ }
+
+ return NULL;
+}
+
+
+int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *hash)
+{
+ u8 tmphash[IKEV2_MAX_HASH_LEN];
+
+ switch (alg) {
+ case AUTH_HMAC_SHA1_96:
+ if (key_len != 20)
+ return -1;
+ hmac_sha1(key, key_len, data, data_len, tmphash);
+ os_memcpy(hash, tmphash, 12);
+ break;
+ case AUTH_HMAC_MD5_96:
+ if (key_len != 16)
+ return -1;
+ hmac_md5(key, key_len, data, data_len, tmphash);
+ os_memcpy(hash, tmphash, 12);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+const struct ikev2_prf_alg * ikev2_get_prf(int id)
+{
+ size_t i;
+
+ for (i = 0; i < NUM_PRF_ALGS; i++) {
+ if (ikev2_prf_algs[i].id == id)
+ return &ikev2_prf_algs[i];
+ }
+
+ return NULL;
+}
+
+
+int ikev2_prf_hash(int alg, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *hash)
+{
+ switch (alg) {
+ case PRF_HMAC_SHA1:
+ hmac_sha1_vector(key, key_len, num_elem, addr, len, hash);
+ break;
+ case PRF_HMAC_MD5:
+ hmac_md5_vector(key, key_len, num_elem, addr, len, hash);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int ikev2_prf_plus(int alg, const u8 *key, size_t key_len,
+ const u8 *data, size_t data_len,
+ u8 *out, size_t out_len)
+{
+ u8 hash[IKEV2_MAX_HASH_LEN];
+ size_t hash_len;
+ u8 iter, *pos, *end;
+ const u8 *addr[3];
+ size_t len[3];
+ const struct ikev2_prf_alg *prf;
+ int res;
+
+ prf = ikev2_get_prf(alg);
+ if (prf == NULL)
+ return -1;
+ hash_len = prf->hash_len;
+
+ addr[0] = hash;
+ len[0] = hash_len;
+ addr[1] = data;
+ len[1] = data_len;
+ addr[2] = &iter;
+ len[2] = 1;
+
+ pos = out;
+ end = out + out_len;
+ iter = 1;
+ while (pos < end) {
+ size_t clen;
+ if (iter == 1)
+ res = ikev2_prf_hash(alg, key, key_len, 2, &addr[1],
+ &len[1], hash);
+ else
+ res = ikev2_prf_hash(alg, key, key_len, 3, addr, len,
+ hash);
+ if (res < 0)
+ return -1;
+ clen = hash_len;
+ if ((int) clen > end - pos)
+ clen = end - pos;
+ os_memcpy(pos, hash, clen);
+ pos += clen;
+ iter++;
+ }
+
+ return 0;
+}
+
+
+const struct ikev2_encr_alg * ikev2_get_encr(int id)
+{
+ size_t i;
+
+ for (i = 0; i < NUM_ENCR_ALGS; i++) {
+ if (ikev2_encr_algs[i].id == id)
+ return &ikev2_encr_algs[i];
+ }
+
+ return NULL;
+}
+
+
+#ifdef CCNS_PL
+/* from des.c */
+struct des3_key_s {
+ u32 ek[3][32];
+ u32 dk[3][32];
+};
+
+void des3_key_setup(const u8 *key, struct des3_key_s *dkey);
+void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt);
+void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain);
+#endif /* CCNS_PL */
+
+
+int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+ const u8 *plain, u8 *crypt, size_t len)
+{
+ struct crypto_cipher *cipher;
+ int encr_alg;
+
+#ifdef CCNS_PL
+ if (alg == ENCR_3DES) {
+ struct des3_key_s des3key;
+ size_t i, blocks;
+ u8 *pos;
+
+ /* ECB mode is used incorrectly for 3DES!? */
+ if (key_len != 24) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length");
+ return -1;
+ }
+ des3_key_setup(key, &des3key);
+
+ blocks = len / 8;
+ pos = crypt;
+ for (i = 0; i < blocks; i++) {
+ des3_encrypt(pos, &des3key, pos);
+ pos += 8;
+ }
+ } else {
+#endif /* CCNS_PL */
+ switch (alg) {
+ case ENCR_3DES:
+ encr_alg = CRYPTO_CIPHER_ALG_3DES;
+ break;
+ case ENCR_AES_CBC:
+ encr_alg = CRYPTO_CIPHER_ALG_AES;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg);
+ return -1;
+ }
+
+ cipher = crypto_cipher_init(encr_alg, iv, key, key_len);
+ if (cipher == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher");
+ return -1;
+ }
+
+ if (crypto_cipher_encrypt(cipher, plain, crypt, len) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Encryption failed");
+ crypto_cipher_deinit(cipher);
+ return -1;
+ }
+ crypto_cipher_deinit(cipher);
+#ifdef CCNS_PL
+ }
+#endif /* CCNS_PL */
+
+ return 0;
+}
+
+
+int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+ const u8 *crypt, u8 *plain, size_t len)
+{
+ struct crypto_cipher *cipher;
+ int encr_alg;
+
+#ifdef CCNS_PL
+ if (alg == ENCR_3DES) {
+ struct des3_key_s des3key;
+ size_t i, blocks;
+
+ /* ECB mode is used incorrectly for 3DES!? */
+ if (key_len != 24) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length");
+ return -1;
+ }
+ des3_key_setup(key, &des3key);
+
+ if (len % 8) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid encrypted "
+ "length");
+ return -1;
+ }
+ blocks = len / 8;
+ for (i = 0; i < blocks; i++) {
+ des3_decrypt(crypt, &des3key, plain);
+ plain += 8;
+ crypt += 8;
+ }
+ } else {
+#endif /* CCNS_PL */
+ switch (alg) {
+ case ENCR_3DES:
+ encr_alg = CRYPTO_CIPHER_ALG_3DES;
+ break;
+ case ENCR_AES_CBC:
+ encr_alg = CRYPTO_CIPHER_ALG_AES;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg);
+ return -1;
+ }
+
+ cipher = crypto_cipher_init(encr_alg, iv, key, key_len);
+ if (cipher == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher");
+ return -1;
+ }
+
+ if (crypto_cipher_decrypt(cipher, crypt, plain, len) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Decryption failed");
+ crypto_cipher_deinit(cipher);
+ return -1;
+ }
+ crypto_cipher_deinit(cipher);
+#ifdef CCNS_PL
+ }
+#endif /* CCNS_PL */
+
+ return 0;
+}
+
+
+int ikev2_parse_payloads(struct ikev2_payloads *payloads,
+ u8 next_payload, const u8 *pos, const u8 *end)
+{
+ const struct ikev2_payload_hdr *phdr;
+
+ os_memset(payloads, 0, sizeof(*payloads));
+
+ while (next_payload != IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) {
+ int plen, pdatalen;
+ const u8 *pdata;
+ wpa_printf(MSG_DEBUG, "IKEV2: Processing payload %u",
+ next_payload);
+ if (end - pos < (int) sizeof(*phdr)) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short message for "
+ "payload header (left=%ld)",
+ (long) (end - pos));
+ }
+ phdr = (const struct ikev2_payload_hdr *) pos;
+ plen = WPA_GET_BE16(phdr->payload_length);
+ if (plen < (int) sizeof(*phdr) || pos + plen > end) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid payload header "
+ "length %d", plen);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Flags: 0x%x"
+ " Payload Length: %d",
+ phdr->next_payload, phdr->flags, plen);
+
+ pdata = (const u8 *) (phdr + 1);
+ pdatalen = plen - sizeof(*phdr);
+
+ switch (next_payload) {
+ case IKEV2_PAYLOAD_SA:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: Security "
+ "Association");
+ payloads->sa = pdata;
+ payloads->sa_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_KEY_EXCHANGE:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: Key "
+ "Exchange");
+ payloads->ke = pdata;
+ payloads->ke_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_IDi:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: IDi");
+ payloads->idi = pdata;
+ payloads->idi_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_IDr:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: IDr");
+ payloads->idr = pdata;
+ payloads->idr_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_CERTIFICATE:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: Certificate");
+ payloads->cert = pdata;
+ payloads->cert_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_AUTHENTICATION:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: "
+ "Authentication");
+ payloads->auth = pdata;
+ payloads->auth_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_NONCE:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: Nonce");
+ payloads->nonce = pdata;
+ payloads->nonce_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_ENCRYPTED:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: Encrypted");
+ payloads->encrypted = pdata;
+ payloads->encrypted_len = pdatalen;
+ break;
+ case IKEV2_PAYLOAD_NOTIFICATION:
+ wpa_printf(MSG_DEBUG, "IKEV2: Payload: "
+ "Notification");
+ payloads->notification = pdata;
+ payloads->notification_len = pdatalen;
+ break;
+ default:
+ if (phdr->flags & IKEV2_PAYLOAD_FLAGS_CRITICAL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported "
+ "critical payload %u - reject the "
+ "entire message", next_payload);
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "IKEV2: Skipped "
+ "unsupported payload %u",
+ next_payload);
+ }
+ }
+
+ if (next_payload == IKEV2_PAYLOAD_ENCRYPTED &&
+ pos + plen == end) {
+ /*
+ * Next Payload in the case of Encrypted Payload is
+ * actually the payload type for the first embedded
+ * payload.
+ */
+ payloads->encr_next_payload = phdr->next_payload;
+ next_payload = IKEV2_PAYLOAD_NO_NEXT_PAYLOAD;
+ } else
+ next_payload = phdr->next_payload;
+
+ pos += plen;
+ }
+
+ if (pos != end) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected extra data after "
+ "payloads");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg,
+ const u8 *ID, size_t ID_len, u8 ID_type,
+ struct ikev2_keys *keys, int initiator,
+ const u8 *shared_secret, size_t shared_secret_len,
+ const u8 *nonce, size_t nonce_len,
+ const u8 *key_pad, size_t key_pad_len,
+ u8 *auth_data)
+{
+ size_t sign_len, buf_len;
+ u8 *sign_data, *pos, *buf, hash[IKEV2_MAX_HASH_LEN];
+ const struct ikev2_prf_alg *prf;
+ const u8 *SK_p = initiator ? keys->SK_pi : keys->SK_pr;
+
+ prf = ikev2_get_prf(prf_alg);
+ if (sign_msg == NULL || ID == NULL || SK_p == NULL ||
+ shared_secret == NULL || nonce == NULL || prf == NULL)
+ return -1;
+
+ /* prf(SK_pi/r,IDi/r') */
+ buf_len = 4 + ID_len;
+ buf = os_zalloc(buf_len);
+ if (buf == NULL)
+ return -1;
+ buf[0] = ID_type;
+ os_memcpy(buf + 4, ID, ID_len);
+ if (ikev2_prf_hash(prf->id, SK_p, keys->SK_prf_len,
+ 1, (const u8 **) &buf, &buf_len, hash) < 0) {
+ os_free(buf);
+ return -1;
+ }
+ os_free(buf);
+
+ /* sign_data = msg | Nr/i | prf(SK_pi/r,IDi/r') */
+ sign_len = wpabuf_len(sign_msg) + nonce_len + prf->hash_len;
+ sign_data = os_malloc(sign_len);
+ if (sign_data == NULL)
+ return -1;
+ pos = sign_data;
+ os_memcpy(pos, wpabuf_head(sign_msg), wpabuf_len(sign_msg));
+ pos += wpabuf_len(sign_msg);
+ os_memcpy(pos, nonce, nonce_len);
+ pos += nonce_len;
+ os_memcpy(pos, hash, prf->hash_len);
+
+ /* AUTH = prf(prf(Shared Secret, key pad, sign_data) */
+ if (ikev2_prf_hash(prf->id, shared_secret, shared_secret_len, 1,
+ &key_pad, &key_pad_len, hash) < 0 ||
+ ikev2_prf_hash(prf->id, hash, prf->hash_len, 1,
+ (const u8 **) &sign_data, &sign_len, auth_data) < 0)
+ {
+ os_free(sign_data);
+ return -1;
+ }
+ os_free(sign_data);
+
+ return 0;
+}
+
+
+u8 * ikev2_decrypt_payload(int encr_id, int integ_id,
+ struct ikev2_keys *keys, int initiator,
+ const struct ikev2_hdr *hdr,
+ const u8 *encrypted, size_t encrypted_len,
+ size_t *res_len)
+{
+ size_t iv_len;
+ const u8 *pos, *end, *iv, *integ;
+ u8 hash[IKEV2_MAX_HASH_LEN], *decrypted;
+ size_t decrypted_len, pad_len;
+ const struct ikev2_integ_alg *integ_alg;
+ const struct ikev2_encr_alg *encr_alg;
+ const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er;
+ const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
+
+ if (encrypted == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No Encrypted payload in SA_AUTH");
+ return NULL;
+ }
+
+ encr_alg = ikev2_get_encr(encr_id);
+ if (encr_alg == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type");
+ return NULL;
+ }
+ iv_len = encr_alg->block_size;
+
+ integ_alg = ikev2_get_integ(integ_id);
+ if (integ_alg == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type");
+ return NULL;
+ }
+
+ if (encrypted_len < iv_len + 1 + integ_alg->hash_len) {
+ wpa_printf(MSG_INFO, "IKEV2: No room for IV or Integrity "
+ "Checksum");
+ return NULL;
+ }
+
+ iv = encrypted;
+ pos = iv + iv_len;
+ end = encrypted + encrypted_len;
+ integ = end - integ_alg->hash_len;
+
+ if (SK_a == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No SK_a available");
+ return NULL;
+ }
+ if (ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len,
+ (const u8 *) hdr,
+ integ - (const u8 *) hdr, hash) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Failed to calculate integrity "
+ "hash");
+ return NULL;
+ }
+ if (os_memcmp(integ, hash, integ_alg->hash_len) != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum "
+ "Data");
+ return NULL;
+ }
+
+ if (SK_e == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No SK_e available");
+ return NULL;
+ }
+
+ decrypted_len = integ - pos;
+ decrypted = os_malloc(decrypted_len);
+ if (decrypted == NULL)
+ return NULL;
+
+ if (ikev2_encr_decrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, pos,
+ decrypted, decrypted_len) < 0) {
+ os_free(decrypted);
+ return NULL;
+ }
+
+ pad_len = decrypted[decrypted_len - 1];
+ if (decrypted_len < pad_len + 1) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid padding in encrypted "
+ "payload");
+ os_free(decrypted);
+ return NULL;
+ }
+
+ decrypted_len -= pad_len + 1;
+
+ *res_len = decrypted_len;
+ return decrypted;
+}
+
+
+void ikev2_update_hdr(struct wpabuf *msg)
+{
+ struct ikev2_hdr *hdr;
+
+ /* Update lenth field in HDR */
+ hdr = wpabuf_mhead(msg);
+ WPA_PUT_BE32(hdr->length, wpabuf_len(msg));
+}
+
+
+int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys,
+ int initiator, struct wpabuf *msg,
+ struct wpabuf *plain, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+ size_t iv_len, pad_len;
+ u8 *icv, *iv;
+ const struct ikev2_integ_alg *integ_alg;
+ const struct ikev2_encr_alg *encr_alg;
+ const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er;
+ const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding Encrypted payload");
+
+ /* Encr - RFC 4306, Sect. 3.14 */
+
+ encr_alg = ikev2_get_encr(encr_id);
+ if (encr_alg == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type");
+ return -1;
+ }
+ iv_len = encr_alg->block_size;
+
+ integ_alg = ikev2_get_integ(integ_id);
+ if (integ_alg == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type");
+ return -1;
+ }
+
+ if (SK_e == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No SK_e available");
+ return -1;
+ }
+
+ if (SK_a == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No SK_a available");
+ return -1;
+ }
+
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+
+ iv = wpabuf_put(msg, iv_len);
+ if (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/src/eap_common/ikev2_common.h b/src/eap_common/ikev2_common.h
new file mode 100644
index 0000000..c96a070
--- /dev/null
+++ b/src/eap_common/ikev2_common.h
@@ -0,0 +1,344 @@
+/*
+ * IKEv2 definitions
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef IKEV2_COMMON_H
+#define IKEV2_COMMON_H
+
+/*
+ * Nonce length must be at least 16 octets. It must also be at least half the
+ * key size of the negotiated PRF.
+ */
+#define IKEV2_NONCE_MIN_LEN 16
+#define IKEV2_NONCE_MAX_LEN 256
+
+/* IKE Header - RFC 4306, Sect. 3.1 */
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#define IKEV2_SPI_LEN 8
+
+struct ikev2_hdr {
+ u8 i_spi[IKEV2_SPI_LEN]; /* IKE_SA Initiator's SPI */
+ u8 r_spi[IKEV2_SPI_LEN]; /* IKE_SA Responder's SPI */
+ u8 next_payload;
+ u8 version; /* MjVer | MnVer */
+ u8 exchange_type;
+ u8 flags;
+ u8 message_id[4];
+ u8 length[4]; /* total length of HDR + payloads */
+} STRUCT_PACKED;
+
+struct ikev2_payload_hdr {
+ u8 next_payload;
+ u8 flags;
+ u8 payload_length[2]; /* this payload, including the payload header */
+} STRUCT_PACKED;
+
+struct ikev2_proposal {
+ u8 type; /* 0 (last) or 2 (more) */
+ u8 reserved;
+ u8 proposal_length[2]; /* including all transform and attributes */
+ u8 proposal_num;
+ u8 protocol_id; /* IKEV2_PROTOCOL_* */
+ u8 spi_size;
+ u8 num_transforms;
+ /* SPI of spi_size octets */
+ /* Transforms */
+} STRUCT_PACKED;
+
+struct ikev2_transform {
+ u8 type; /* 0 (last) or 3 (more) */
+ u8 reserved;
+ u8 transform_length[2]; /* including Header and Attributes */
+ u8 transform_type;
+ u8 reserved2;
+ u8 transform_id[2];
+ /* Transform Attributes */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* Current IKEv2 version from RFC 4306 */
+#define IKEV2_MjVer 2
+#define IKEV2_MnVer 0
+#ifdef CCNS_PL
+#define IKEV2_VERSION ((IKEV2_MjVer) | ((IKEV2_MnVer) << 4))
+#else /* CCNS_PL */
+#define IKEV2_VERSION (((IKEV2_MjVer) << 4) | (IKEV2_MnVer))
+#endif /* CCNS_PL */
+
+/* IKEv2 Exchange Types */
+enum {
+ /* 0-33 RESERVED */
+ IKE_SA_INIT = 34,
+ IKE_SA_AUTH = 35,
+ CREATE_CHILD_SA = 36,
+ INFORMATION = 37
+ /* 38-239 RESERVED TO IANA */
+ /* 240-255 Reserved for private use */
+};
+
+/* IKEv2 Flags */
+#define IKEV2_HDR_INITIATOR 0x08
+#define IKEV2_HDR_VERSION 0x10
+#define IKEV2_HDR_RESPONSE 0x20
+
+/* Payload Header Flags */
+#define IKEV2_PAYLOAD_FLAGS_CRITICAL 0x01
+
+
+/* EAP-IKEv2 Payload Types (in Next Payload Type field)
+ * http://www.iana.org/assignments/eap-ikev2-payloads */
+enum {
+ IKEV2_PAYLOAD_NO_NEXT_PAYLOAD = 0,
+ IKEV2_PAYLOAD_SA = 33,
+ IKEV2_PAYLOAD_KEY_EXCHANGE = 34,
+ IKEV2_PAYLOAD_IDi = 35,
+ IKEV2_PAYLOAD_IDr = 36,
+ IKEV2_PAYLOAD_CERTIFICATE = 37,
+ IKEV2_PAYLOAD_CERT_REQ = 38,
+ IKEV2_PAYLOAD_AUTHENTICATION = 39,
+ IKEV2_PAYLOAD_NONCE = 40,
+ IKEV2_PAYLOAD_NOTIFICATION = 41,
+ IKEV2_PAYLOAD_VENDOD_ID = 43,
+ IKEV2_PAYLOAD_ENCRYPTED = 46,
+ IKEV2_PAYLOAD_NEXT_FAST_ID = 121
+};
+
+
+/* IKEv2 Proposal - Protocol ID */
+enum {
+ IKEV2_PROTOCOL_RESERVED = 0,
+ IKEV2_PROTOCOL_IKE = 1, /* IKE is the only one allowed for EAP-IKEv2 */
+ IKEV2_PROTOCOL_AH = 2,
+ IKEV2_PROTOCOL_ESP = 3
+};
+
+
+/* IKEv2 Transform Types */
+enum {
+ IKEV2_TRANSFORM_ENCR = 1,
+ IKEV2_TRANSFORM_PRF = 2,
+ IKEV2_TRANSFORM_INTEG = 3,
+ IKEV2_TRANSFORM_DH = 4,
+ IKEV2_TRANSFORM_ESN = 5
+};
+
+/* IKEv2 Tranform Type 1 (Encryption Algorithm) */
+enum {
+ ENCR_DES_IV64 = 1,
+ ENCR_DES = 2,
+ ENCR_3DES = 3,
+ ENCR_RC5 = 4,
+ ENCR_IDEA = 5,
+ ENCR_CAST = 6,
+ ENCR_BLOWFISH = 7,
+ ENCR_3IDEA = 8,
+ ENCR_DES_IV32 = 9,
+ ENCR_NULL = 11,
+ ENCR_AES_CBC = 12,
+ ENCR_AES_CTR = 13
+};
+
+/* IKEv2 Transform Type 2 (Pseudo-random Function) */
+enum {
+ PRF_HMAC_MD5 = 1,
+ PRF_HMAC_SHA1 = 2,
+ PRF_HMAC_TIGER = 3,
+ PRF_AES128_XCBC = 4
+};
+
+/* IKEv2 Transform Type 3 (Integrity Algorithm) */
+enum {
+ AUTH_HMAC_MD5_96 = 1,
+ AUTH_HMAC_SHA1_96 = 2,
+ AUTH_DES_MAC = 3,
+ AUTH_KPDK_MD5 = 4,
+ AUTH_AES_XCBC_96 = 5
+};
+
+/* IKEv2 Transform Type 4 (Diffie-Hellman Group) */
+enum {
+ DH_GROUP1_768BIT_MODP = 1, /* RFC 4306 */
+ DH_GROUP2_1024BIT_MODP = 2, /* RFC 4306 */
+ DH_GROUP5_1536BIT_MODP = 5, /* RFC 3526 */
+ DH_GROUP5_2048BIT_MODP = 14, /* RFC 3526 */
+ DH_GROUP5_3072BIT_MODP = 15, /* RFC 3526 */
+ DH_GROUP5_4096BIT_MODP = 16, /* RFC 3526 */
+ DH_GROUP5_6144BIT_MODP = 17, /* RFC 3526 */
+ DH_GROUP5_8192BIT_MODP = 18 /* RFC 3526 */
+};
+
+
+/* Identification Data Types (RFC 4306, Sect. 3.5) */
+enum {
+ ID_IPV4_ADDR = 1,
+ ID_FQDN = 2,
+ ID_RFC822_ADDR = 3,
+ ID_IPV6_ADDR = 5,
+ ID_DER_ASN1_DN = 9,
+ ID_DER_ASN1_GN= 10,
+ ID_KEY_ID = 11
+};
+
+
+/* Certificate Encoding (RFC 4306, Sect. 3.6) */
+enum {
+ CERT_ENCODING_PKCS7_X509 = 1,
+ CERT_ENCODING_PGP_CERT = 2,
+ CERT_ENCODING_DNS_SIGNED_KEY = 3,
+ /* X.509 Certificate - Signature: DER encoded X.509 certificate whose
+ * public key is used to validate the sender's AUTH payload */
+ CERT_ENCODING_X509_CERT_SIGN = 4,
+ CERT_ENCODING_KERBEROS_TOKEN = 6,
+ /* DER encoded X.509 certificate revocation list */
+ CERT_ENCODING_CRL = 7,
+ CERT_ENCODING_ARL = 8,
+ CERT_ENCODING_SPKI_CERT = 9,
+ CERT_ENCODING_X509_CERT_ATTR = 10,
+ /* PKCS #1 encoded RSA key */
+ CERT_ENCODING_RAW_RSA_KEY = 11,
+ CERT_ENCODING_HASH_AND_URL_X509_CERT = 12,
+ CERT_ENCODING_HASH_AND_URL_X509_BUNDLE = 13
+};
+
+
+/* Authentication Method (RFC 4306, Sect. 3.8) */
+enum {
+ AUTH_RSA_SIGN = 1,
+ AUTH_SHARED_KEY_MIC = 2,
+ AUTH_DSS_SIGN = 3
+};
+
+
+/* Notify Message Types (RFC 4306, Sect. 3.10.1) */
+enum {
+ UNSUPPORTED_CRITICAL_PAYLOAD = 1,
+ INVALID_IKE_SPI = 4,
+ INVALID_MAJOR_VERSION = 5,
+ INVALID_SYNTAX = 7,
+ INVALID_MESSAGE_ID = 9,
+ INVALID_SPI = 11,
+ NO_PROPOSAL_CHOSEN = 14,
+ INVALID_KE_PAYLOAD = 17,
+ AUTHENTICATION_FAILED = 24,
+ SINGLE_PAIR_REQUIRED = 34,
+ NO_ADDITIONAL_SAS = 35,
+ INTERNAL_ADDRESS_FAILURE = 36,
+ FAILED_CP_REQUIRED = 37,
+ TS_UNACCEPTABLE = 38,
+ INVALID_SELECTORS = 39
+};
+
+
+struct ikev2_keys {
+ u8 *SK_d, *SK_ai, *SK_ar, *SK_ei, *SK_er, *SK_pi, *SK_pr;
+ size_t SK_d_len, SK_integ_len, SK_encr_len, SK_prf_len;
+};
+
+
+int ikev2_keys_set(struct ikev2_keys *keys);
+void ikev2_free_keys(struct ikev2_keys *keys);
+
+
+/* Maximum hash length for supported hash algorithms */
+#define IKEV2_MAX_HASH_LEN 20
+
+struct ikev2_integ_alg {
+ int id;
+ size_t key_len;
+ size_t hash_len;
+};
+
+struct ikev2_prf_alg {
+ int id;
+ size_t key_len;
+ size_t hash_len;
+};
+
+struct ikev2_encr_alg {
+ int id;
+ size_t key_len;
+ size_t block_size;
+};
+
+const struct ikev2_integ_alg * ikev2_get_integ(int id);
+int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *hash);
+const struct ikev2_prf_alg * ikev2_get_prf(int id);
+int ikev2_prf_hash(int alg, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *hash);
+int ikev2_prf_plus(int alg, const u8 *key, size_t key_len,
+ const u8 *data, size_t data_len,
+ u8 *out, size_t out_len);
+const struct ikev2_encr_alg * ikev2_get_encr(int id);
+int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+ const u8 *plain, u8 *crypt, size_t len);
+int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+ const u8 *crypt, u8 *plain, size_t len);
+
+int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg,
+ const u8 *ID, size_t ID_len, u8 ID_type,
+ struct ikev2_keys *keys, int initiator,
+ const u8 *shared_secret, size_t shared_secret_len,
+ const u8 *nonce, size_t nonce_len,
+ const u8 *key_pad, size_t key_pad_len,
+ u8 *auth_data);
+
+
+struct ikev2_payloads {
+ const u8 *sa;
+ size_t sa_len;
+ const u8 *ke;
+ size_t ke_len;
+ const u8 *idi;
+ size_t idi_len;
+ const u8 *idr;
+ size_t idr_len;
+ const u8 *cert;
+ size_t cert_len;
+ const u8 *auth;
+ size_t auth_len;
+ const u8 *nonce;
+ size_t nonce_len;
+ const u8 *encrypted;
+ size_t encrypted_len;
+ u8 encr_next_payload;
+ const u8 *notification;
+ size_t notification_len;
+};
+
+int ikev2_parse_payloads(struct ikev2_payloads *payloads,
+ u8 next_payload, const u8 *pos, const u8 *end);
+
+u8 * ikev2_decrypt_payload(int encr_id, int integ_id, struct ikev2_keys *keys,
+ int initiator, const struct ikev2_hdr *hdr,
+ const u8 *encrypted, size_t encrypted_len,
+ size_t *res_len);
+void ikev2_update_hdr(struct wpabuf *msg);
+int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys,
+ int initiator, struct wpabuf *msg,
+ struct wpabuf *plain, u8 next_payload);
+int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf,
+ const struct ikev2_integ_alg *integ,
+ const struct ikev2_encr_alg *encr,
+ const u8 *skeyseed, const u8 *data, size_t data_len,
+ struct ikev2_keys *keys);
+
+#endif /* IKEV2_COMMON_H */
diff --git a/src/eap_peer/Makefile b/src/eap_peer/Makefile
new file mode 100644
index 0000000..3651056
--- /dev/null
+++ b/src/eap_peer/Makefile
@@ -0,0 +1,11 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.so *.d
+
+install:
+ if ls *.so >/dev/null 2>&1; then \
+ install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
+ cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
+ ; fi
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
new file mode 100644
index 0000000..8a9826f
--- /dev/null
+++ b/src/eap_peer/eap.c
@@ -0,0 +1,2159 @@
+/*
+ * EAP peer state machines (RFC 4137)
+ * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file implements the Peer State Machine as defined in RFC 4137. The used
+ * states and state transitions match mostly with the RFC. However, there are
+ * couple of additional transitions for working around small issues noticed
+ * during testing. These exceptions are explained in comments within the
+ * functions in this file. The method functions, m.func(), are similar to the
+ * ones used in RFC 4137, but some small changes have used here to optimize
+ * operations and to add functionality needed for fast re-authentication
+ * (session resumption).
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "pcsc_funcs.h"
+#include "state_machine.h"
+#include "crypto/crypto.h"
+#include "crypto/tls.h"
+#include "common/wpa_ctrl.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_i.h"
+#include "eap_config.h"
+
+#define STATE_MACHINE_DATA struct eap_sm
+#define STATE_MACHINE_DEBUG_PREFIX "EAP"
+
+#define EAP_MAX_AUTH_ROUNDS 50
+#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_deinit_prev_method(struct eap_sm *sm, const char *txt)
+{
+ if (sm->m == NULL || sm->eap_method_priv == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method "
+ "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt);
+ sm->m->deinit(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ sm->m = NULL;
+}
+
+
+/**
+ * eap_allowed_method - Check whether EAP method is allowed
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types
+ * @method: EAP type
+ * Returns: 1 = allowed EAP method, 0 = not allowed
+ */
+int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ int i;
+ struct eap_method_type *m;
+
+ if (config == NULL || config->eap_methods == NULL)
+ return 1;
+
+ m = config->eap_methods;
+ for (i = 0; m[i].vendor != EAP_VENDOR_IETF ||
+ m[i].method != EAP_TYPE_NONE; i++) {
+ if (m[i].vendor == vendor && m[i].method == method)
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * This state initializes state machine variables when the machine is
+ * activated (portEnabled = TRUE). This is also used when re-starting
+ * authentication (eapRestart == TRUE).
+ */
+SM_STATE(EAP, INITIALIZE)
+{
+ SM_ENTRY(EAP, INITIALIZE);
+ if (sm->fast_reauth && sm->m && sm->m->has_reauth_data &&
+ sm->m->has_reauth_data(sm, sm->eap_method_priv) &&
+ !sm->prev_failure) {
+ wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for "
+ "fast reauthentication");
+ sm->m->deinit_for_reauth(sm, sm->eap_method_priv);
+ } else {
+ eap_deinit_prev_method(sm, "INITIALIZE");
+ }
+ sm->selectedMethod = EAP_TYPE_NONE;
+ sm->methodState = METHOD_NONE;
+ sm->allowNotifications = TRUE;
+ sm->decision = DECISION_FAIL;
+ eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+ eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
+ eapol_set_bool(sm, EAPOL_eapFail, FALSE);
+ os_free(sm->eapKeyData);
+ sm->eapKeyData = NULL;
+ sm->eapKeyAvailable = FALSE;
+ eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
+ sm->lastId = -1; /* new session - make sure this does not match with
+ * the first EAP-Packet */
+ /*
+ * RFC 4137 does not reset eapResp and eapNoResp here. However, this
+ * seemed to be able to trigger cases where both were set and if EAPOL
+ * state machine uses eapNoResp first, it may end up not sending a real
+ * reply correctly. This occurred when the workaround in FAIL state set
+ * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do
+ * something else(?)
+ */
+ eapol_set_bool(sm, EAPOL_eapResp, FALSE);
+ eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
+ sm->num_rounds = 0;
+ sm->prev_failure = 0;
+}
+
+
+/*
+ * This state is reached whenever service from the lower layer is interrupted
+ * or unavailable (portEnabled == FALSE). Immediate transition to INITIALIZE
+ * occurs when the port becomes enabled.
+ */
+SM_STATE(EAP, DISABLED)
+{
+ SM_ENTRY(EAP, DISABLED);
+ sm->num_rounds = 0;
+}
+
+
+/*
+ * The state machine spends most of its time here, waiting for something to
+ * happen. This state is entered unconditionally from INITIALIZE, DISCARD, and
+ * SEND_RESPONSE states.
+ */
+SM_STATE(EAP, IDLE)
+{
+ SM_ENTRY(EAP, IDLE);
+}
+
+
+/*
+ * This state is entered when an EAP packet is received (eapReq == TRUE) to
+ * parse the packet header.
+ */
+SM_STATE(EAP, RECEIVED)
+{
+ const struct wpabuf *eapReqData;
+
+ SM_ENTRY(EAP, RECEIVED);
+ eapReqData = eapol_get_eapReqData(sm);
+ /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */
+ eap_sm_parseEapReq(sm, eapReqData);
+ sm->num_rounds++;
+}
+
+
+/*
+ * This state is entered when a request for a new type comes in. Either the
+ * correct method is started, or a Nak response is built.
+ */
+SM_STATE(EAP, GET_METHOD)
+{
+ int reinit;
+ EapType method;
+
+ SM_ENTRY(EAP, GET_METHOD);
+
+ if (sm->reqMethod == EAP_TYPE_EXPANDED)
+ method = sm->reqVendorMethod;
+ else
+ method = sm->reqMethod;
+
+ if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) {
+ wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed",
+ sm->reqVendor, method);
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+ "vendor=%u method=%u -> NAK",
+ sm->reqVendor, method);
+ goto nak;
+ }
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+ "vendor=%u method=%u", sm->reqVendor, method);
+
+ /*
+ * RFC 4137 does not define specific operation for fast
+ * re-authentication (session resumption). The design here is to allow
+ * the previously used method data to be maintained for
+ * re-authentication if the method support session resumption.
+ * Otherwise, the previously used method data is freed and a new method
+ * is allocated here.
+ */
+ if (sm->fast_reauth &&
+ sm->m && sm->m->vendor == sm->reqVendor &&
+ sm->m->method == method &&
+ sm->m->has_reauth_data &&
+ sm->m->has_reauth_data(sm, sm->eap_method_priv)) {
+ wpa_printf(MSG_DEBUG, "EAP: Using previous method data"
+ " for fast re-authentication");
+ reinit = 1;
+ } else {
+ eap_deinit_prev_method(sm, "GET_METHOD");
+ reinit = 0;
+ }
+
+ sm->selectedMethod = sm->reqMethod;
+ if (sm->m == NULL)
+ sm->m = eap_peer_get_eap_method(sm->reqVendor, method);
+ if (!sm->m) {
+ wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: "
+ "vendor %d method %d",
+ sm->reqVendor, method);
+ goto nak;
+ }
+
+ 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;
+
+ SM_ENTRY(EAP, METHOD);
+ if (sm->m == NULL) {
+ wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected");
+ return;
+ }
+
+ eapReqData = eapol_get_eapReqData(sm);
+
+ /*
+ * Get ignore, methodState, decision, allowNotifications, and
+ * eapRespData. RFC 4137 uses three separate method procedure (check,
+ * process, and buildResp) in this state. These have been combined into
+ * a single function call to m->process() in order to optimize EAP
+ * method implementation interface a bit. These procedures are only
+ * used from within this METHOD state, so there is no need to keep
+ * these as separate C functions.
+ *
+ * The RFC 4137 procedures return values as follows:
+ * ignore = m.check(eapReqData)
+ * (methodState, decision, allowNotifications) = m.process(eapReqData)
+ * eapRespData = m.buildResp(reqId)
+ */
+ os_memset(&ret, 0, sizeof(ret));
+ ret.ignore = sm->ignore;
+ ret.methodState = sm->methodState;
+ ret.decision = sm->decision;
+ ret.allowNotifications = sm->allowNotifications;
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret,
+ eapReqData);
+ wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s "
+ "methodState=%s decision=%s",
+ ret.ignore ? "TRUE" : "FALSE",
+ eap_sm_method_state_txt(ret.methodState),
+ eap_sm_decision_txt(ret.decision));
+
+ sm->ignore = ret.ignore;
+ if (sm->ignore)
+ return;
+ sm->methodState = ret.methodState;
+ sm->decision = ret.decision;
+ sm->allowNotifications = ret.allowNotifications;
+
+ if (sm->m->isKeyAvailable && sm->m->getKey &&
+ sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
+ os_free(sm->eapKeyData);
+ sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
+ &sm->eapKeyDataLen);
+ }
+}
+
+
+/*
+ * This state signals the lower layer that a response packet is ready to be
+ * sent.
+ */
+SM_STATE(EAP, SEND_RESPONSE)
+{
+ SM_ENTRY(EAP, SEND_RESPONSE);
+ wpabuf_free(sm->lastRespData);
+ if (sm->eapRespData) {
+ if (sm->workaround)
+ os_memcpy(sm->last_md5, sm->req_md5, 16);
+ sm->lastId = sm->reqId;
+ sm->lastRespData = wpabuf_dup(sm->eapRespData);
+ eapol_set_bool(sm, EAPOL_eapResp, TRUE);
+ } else
+ sm->lastRespData = NULL;
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+}
+
+
+/*
+ * This state signals the lower layer that the request was discarded, and no
+ * response packet will be sent at this time.
+ */
+SM_STATE(EAP, DISCARD)
+{
+ SM_ENTRY(EAP, DISCARD);
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+}
+
+
+/*
+ * Handles requests for Identity method and builds a response.
+ */
+SM_STATE(EAP, IDENTITY)
+{
+ const struct wpabuf *eapReqData;
+
+ SM_ENTRY(EAP, IDENTITY);
+ eapReqData = eapol_get_eapReqData(sm);
+ eap_sm_processIdentity(sm, eapReqData);
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, 0);
+}
+
+
+/*
+ * Handles requests for Notification method and builds a response.
+ */
+SM_STATE(EAP, NOTIFICATION)
+{
+ const struct wpabuf *eapReqData;
+
+ SM_ENTRY(EAP, NOTIFICATION);
+ eapReqData = eapol_get_eapReqData(sm);
+ eap_sm_processNotify(sm, eapReqData);
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ sm->eapRespData = eap_sm_buildNotify(sm->reqId);
+}
+
+
+/*
+ * This state retransmits the previous response packet.
+ */
+SM_STATE(EAP, RETRANSMIT)
+{
+ SM_ENTRY(EAP, RETRANSMIT);
+ wpabuf_free(sm->eapRespData);
+ if (sm->lastRespData)
+ sm->eapRespData = wpabuf_dup(sm->lastRespData);
+ else
+ sm->eapRespData = NULL;
+}
+
+
+/*
+ * This state is entered in case of a successful completion of authentication
+ * and state machine waits here until port is disabled or EAP authentication is
+ * restarted.
+ */
+SM_STATE(EAP, SUCCESS)
+{
+ SM_ENTRY(EAP, SUCCESS);
+ if (sm->eapKeyData != NULL)
+ sm->eapKeyAvailable = TRUE;
+ eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+
+ /*
+ * RFC 4137 does not clear eapReq here, but this seems to be required
+ * to avoid processing the same request twice when state machine is
+ * initialized.
+ */
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+
+ /*
+ * RFC 4137 does not set eapNoResp here, but this seems to be required
+ * to get EAPOL Supplicant backend state machine into SUCCESS state. In
+ * addition, either eapResp or eapNoResp is required to be set after
+ * processing the received EAP frame.
+ */
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+ "EAP authentication completed successfully");
+}
+
+
+/*
+ * This state is entered in case of a failure and state machine waits here
+ * until port is disabled or EAP authentication is restarted.
+ */
+SM_STATE(EAP, FAILURE)
+{
+ SM_ENTRY(EAP, FAILURE);
+ eapol_set_bool(sm, EAPOL_eapFail, TRUE);
+
+ /*
+ * RFC 4137 does not clear eapReq here, but this seems to be required
+ * to avoid processing the same request twice when state machine is
+ * initialized.
+ */
+ eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+
+ /*
+ * RFC 4137 does not set eapNoResp here. However, either eapResp or
+ * eapNoResp is required to be set after processing the received EAP
+ * frame.
+ */
+ eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+ "EAP authentication failed");
+
+ sm->prev_failure = 1;
+}
+
+
+static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId)
+{
+ /*
+ * At least Microsoft IAS and Meetinghouse Aegis seem to be sending
+ * EAP-Success/Failure with lastId + 1 even though RFC 3748 and
+ * RFC 4137 require that reqId == lastId. In addition, it looks like
+ * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success.
+ *
+ * Accept this kind of Id if EAP workarounds are enabled. These are
+ * unauthenticated plaintext messages, so this should have minimal
+ * security implications (bit easier to fake EAP-Success/Failure).
+ */
+ if (sm->workaround && (reqId == ((lastId + 1) & 0xff) ||
+ reqId == ((lastId + 2) & 0xff))) {
+ wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected "
+ "identifier field in EAP Success: "
+ "reqId=%d lastId=%d (these are supposed to be "
+ "same)", reqId, lastId);
+ return 1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d "
+ "lastId=%d", reqId, lastId);
+ return 0;
+}
+
+
+/*
+ * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions
+ */
+
+static void eap_peer_sm_step_idle(struct eap_sm *sm)
+{
+ /*
+ * The first three transitions are from RFC 4137. The last two are
+ * local additions to handle special cases with LEAP and PEAP server
+ * not sending EAP-Success in some cases.
+ */
+ if (eapol_get_bool(sm, EAPOL_eapReq))
+ SM_ENTER(EAP, RECEIVED);
+ else if ((eapol_get_bool(sm, EAPOL_altAccept) &&
+ sm->decision != DECISION_FAIL) ||
+ (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
+ sm->decision == DECISION_UNCOND_SUCC))
+ SM_ENTER(EAP, SUCCESS);
+ else if (eapol_get_bool(sm, EAPOL_altReject) ||
+ (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
+ sm->decision != DECISION_UNCOND_SUCC) ||
+ (eapol_get_bool(sm, EAPOL_altAccept) &&
+ sm->methodState != METHOD_CONT &&
+ sm->decision == DECISION_FAIL))
+ SM_ENTER(EAP, FAILURE);
+ else if (sm->selectedMethod == EAP_TYPE_LEAP &&
+ sm->leap_done && sm->decision != DECISION_FAIL &&
+ sm->methodState == METHOD_DONE)
+ SM_ENTER(EAP, SUCCESS);
+ else if (sm->selectedMethod == EAP_TYPE_PEAP &&
+ sm->peap_done && sm->decision != DECISION_FAIL &&
+ sm->methodState == METHOD_DONE)
+ SM_ENTER(EAP, SUCCESS);
+}
+
+
+static int eap_peer_req_is_duplicate(struct eap_sm *sm)
+{
+ int duplicate;
+
+ duplicate = (sm->reqId == sm->lastId) && sm->rxReq;
+ if (sm->workaround && duplicate &&
+ os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) {
+ /*
+ * RFC 4137 uses (reqId == lastId) as the only verification for
+ * duplicate EAP requests. However, this misses cases where the
+ * AS is incorrectly using the same id again; and
+ * unfortunately, such implementations exist. Use MD5 hash as
+ * an extra verification for the packets being duplicate to
+ * workaround these issues.
+ */
+ wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again, but "
+ "EAP packets were not identical");
+ wpa_printf(MSG_DEBUG, "EAP: workaround - assume this is not a "
+ "duplicate packet");
+ duplicate = 0;
+ }
+
+ return duplicate;
+}
+
+
+static void eap_peer_sm_step_received(struct eap_sm *sm)
+{
+ int duplicate = eap_peer_req_is_duplicate(sm);
+
+ /*
+ * Two special cases below for LEAP are local additions to work around
+ * odd LEAP behavior (EAP-Success in the middle of authentication and
+ * then swapped roles). Other transitions are based on RFC 4137.
+ */
+ if (sm->rxSuccess && sm->decision != DECISION_FAIL &&
+ (sm->reqId == sm->lastId ||
+ eap_success_workaround(sm, sm->reqId, sm->lastId)))
+ SM_ENTER(EAP, SUCCESS);
+ else if (sm->methodState != METHOD_CONT &&
+ ((sm->rxFailure &&
+ sm->decision != DECISION_UNCOND_SUCC) ||
+ (sm->rxSuccess && sm->decision == DECISION_FAIL &&
+ (sm->selectedMethod != EAP_TYPE_LEAP ||
+ sm->methodState != METHOD_MAY_CONT))) &&
+ (sm->reqId == sm->lastId ||
+ eap_success_workaround(sm, sm->reqId, sm->lastId)))
+ SM_ENTER(EAP, FAILURE);
+ else if (sm->rxReq && duplicate)
+ SM_ENTER(EAP, RETRANSMIT);
+ else if (sm->rxReq && !duplicate &&
+ sm->reqMethod == EAP_TYPE_NOTIFICATION &&
+ sm->allowNotifications)
+ SM_ENTER(EAP, NOTIFICATION);
+ else if (sm->rxReq && !duplicate &&
+ sm->selectedMethod == EAP_TYPE_NONE &&
+ sm->reqMethod == EAP_TYPE_IDENTITY)
+ SM_ENTER(EAP, IDENTITY);
+ else if (sm->rxReq && !duplicate &&
+ sm->selectedMethod == EAP_TYPE_NONE &&
+ sm->reqMethod != EAP_TYPE_IDENTITY &&
+ sm->reqMethod != EAP_TYPE_NOTIFICATION)
+ SM_ENTER(EAP, GET_METHOD);
+ else if (sm->rxReq && !duplicate &&
+ sm->reqMethod == sm->selectedMethod &&
+ sm->methodState != METHOD_DONE)
+ SM_ENTER(EAP, METHOD);
+ else if (sm->selectedMethod == EAP_TYPE_LEAP &&
+ (sm->rxSuccess || sm->rxResp))
+ SM_ENTER(EAP, METHOD);
+ else
+ SM_ENTER(EAP, DISCARD);
+}
+
+
+static void eap_peer_sm_step_local(struct eap_sm *sm)
+{
+ switch (sm->EAP_state) {
+ case EAP_INITIALIZE:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_DISABLED:
+ if (eapol_get_bool(sm, EAPOL_portEnabled) &&
+ !sm->force_disabled)
+ SM_ENTER(EAP, INITIALIZE);
+ break;
+ case EAP_IDLE:
+ eap_peer_sm_step_idle(sm);
+ break;
+ case EAP_RECEIVED:
+ eap_peer_sm_step_received(sm);
+ break;
+ case EAP_GET_METHOD:
+ if (sm->selectedMethod == sm->reqMethod)
+ SM_ENTER(EAP, METHOD);
+ else
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_METHOD:
+ if (sm->ignore)
+ SM_ENTER(EAP, DISCARD);
+ else
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_SEND_RESPONSE:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_DISCARD:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_IDENTITY:
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_NOTIFICATION:
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_RETRANSMIT:
+ SM_ENTER(EAP, SEND_RESPONSE);
+ break;
+ case EAP_SUCCESS:
+ break;
+ case EAP_FAILURE:
+ break;
+ }
+}
+
+
+SM_STEP(EAP)
+{
+ /* Global transitions */
+ if (eapol_get_bool(sm, EAPOL_eapRestart) &&
+ eapol_get_bool(sm, EAPOL_portEnabled))
+ SM_ENTER_GLOBAL(EAP, INITIALIZE);
+ else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled)
+ SM_ENTER_GLOBAL(EAP, DISABLED);
+ else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
+ /* RFC 4137 does not place any limit on number of EAP messages
+ * in an authentication session. However, some error cases have
+ * ended up in a state were EAP messages were sent between the
+ * peer and server in a loop (e.g., TLS ACK frame in both
+ * direction). Since this is quite undesired outcome, limit the
+ * total number of EAP round-trips and abort authentication if
+ * this limit is exceeded.
+ */
+ if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
+ wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d "
+ "authentication rounds - abort",
+ EAP_MAX_AUTH_ROUNDS);
+ sm->num_rounds++;
+ SM_ENTER_GLOBAL(EAP, FAILURE);
+ }
+ } else {
+ /* Local transitions */
+ eap_peer_sm_step_local(sm);
+ }
+}
+
+
+static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
+ EapType method)
+{
+ if (!eap_allowed_method(sm, vendor, method)) {
+ wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: "
+ "vendor %u method %u", vendor, method);
+ return FALSE;
+ }
+ if (eap_peer_get_eap_method(vendor, method))
+ return TRUE;
+ wpa_printf(MSG_DEBUG, "EAP: not included in build: "
+ "vendor %u method %u", vendor, method);
+ return FALSE;
+}
+
+
+static struct wpabuf * eap_sm_build_expanded_nak(
+ struct eap_sm *sm, int id, const struct eap_method *methods,
+ size_t count)
+{
+ struct wpabuf *resp;
+ int found = 0;
+ const struct eap_method *m;
+
+ wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak");
+
+ /* RFC 3748 - 5.3.2: Expanded Nak */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED,
+ 8 + 8 * (count + 1), EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+ wpabuf_put_be32(resp, EAP_TYPE_NAK);
+
+ for (m = methods; m; m = m->next) {
+ if (sm->reqVendor == m->vendor &&
+ sm->reqVendorMethod == m->method)
+ continue; /* do not allow the current method again */
+ if (eap_allowed_method(sm, m->vendor, m->method)) {
+ wpa_printf(MSG_DEBUG, "EAP: allowed type: "
+ "vendor=%u method=%u",
+ m->vendor, m->method);
+ wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+ wpabuf_put_be24(resp, m->vendor);
+ wpabuf_put_be32(resp, m->method);
+
+ found++;
+ }
+ }
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "EAP: no more allowed methods");
+ wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+ wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+ wpabuf_put_be32(resp, EAP_TYPE_NONE);
+ }
+
+ eap_update_len(resp);
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id)
+{
+ struct wpabuf *resp;
+ u8 *start;
+ int found = 0, expanded_found = 0;
+ size_t count;
+ const struct eap_method *methods, *m;
+
+ wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u "
+ "vendor=%u method=%u not allowed)", sm->reqMethod,
+ sm->reqVendor, sm->reqVendorMethod);
+ methods = eap_peer_get_methods(&count);
+ if (methods == NULL)
+ return NULL;
+ if (sm->reqMethod == EAP_TYPE_EXPANDED)
+ return eap_sm_build_expanded_nak(sm, id, methods, count);
+
+ /* RFC 3748 - 5.3.1: Legacy Nak */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK,
+ sizeof(struct eap_hdr) + 1 + count + 1,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ start = wpabuf_put(resp, 0);
+ for (m = methods; m; m = m->next) {
+ if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod)
+ continue; /* do not allow the current method again */
+ if (eap_allowed_method(sm, m->vendor, m->method)) {
+ if (m->vendor != EAP_VENDOR_IETF) {
+ if (expanded_found)
+ continue;
+ expanded_found = 1;
+ wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+ } else
+ wpabuf_put_u8(resp, m->method);
+ found++;
+ }
+ }
+ if (!found)
+ wpabuf_put_u8(resp, EAP_TYPE_NONE);
+ wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found);
+
+ eap_update_len(resp);
+
+ return resp;
+}
+
+
+static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req)
+{
+ const struct eap_hdr *hdr = wpabuf_head(req);
+ const u8 *pos = (const u8 *) (hdr + 1);
+ pos++;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
+ "EAP authentication started");
+
+ /*
+ * RFC 3748 - 5.1: Identity
+ * Data field may contain a displayable message in UTF-8. If this
+ * includes NUL-character, only the data before that should be
+ * displayed. Some EAP implementasitons may piggy-back additional
+ * options after the NUL.
+ */
+ /* TODO: could save displayable message so that it can be shown to the
+ * user in case of interaction is required */
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data",
+ pos, be_to_host16(hdr->length) - 5);
+}
+
+
+#ifdef PCSC_FUNCS
+static int eap_sm_imsi_identity(struct eap_sm *sm,
+ struct eap_peer_config *conf)
+{
+ int aka = 0;
+ char imsi[100];
+ size_t imsi_len;
+ struct eap_method_type *m = conf->eap_methods;
+ int i;
+
+ imsi_len = sizeof(imsi);
+ if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) {
+ wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len);
+
+ for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF ||
+ m[i].method != EAP_TYPE_NONE); i++) {
+ if (m[i].vendor == EAP_VENDOR_IETF &&
+ m[i].method == EAP_TYPE_AKA) {
+ aka = 1;
+ break;
+ }
+ }
+
+ os_free(conf->identity);
+ conf->identity = os_malloc(1 + imsi_len);
+ if (conf->identity == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to allocate buffer for "
+ "IMSI-based identity");
+ return -1;
+ }
+
+ conf->identity[0] = aka ? '0' : '1';
+ os_memcpy(conf->identity + 1, imsi, imsi_len);
+ conf->identity_len = 1 + imsi_len;
+
+ return 0;
+}
+#endif /* PCSC_FUNCS */
+
+
+static int eap_sm_set_scard_pin(struct eap_sm *sm,
+ struct eap_peer_config *conf)
+{
+#ifdef PCSC_FUNCS
+ if (scard_set_pin(sm->scard_ctx, conf->pin)) {
+ /*
+ * Make sure the same PIN is not tried again in order to avoid
+ * blocking SIM.
+ */
+ os_free(conf->pin);
+ conf->pin = NULL;
+
+ wpa_printf(MSG_WARNING, "PIN validation failed");
+ eap_sm_request_pin(sm);
+ return -1;
+ }
+ return 0;
+#else /* PCSC_FUNCS */
+ return -1;
+#endif /* PCSC_FUNCS */
+}
+
+static int eap_sm_get_scard_identity(struct eap_sm *sm,
+ struct eap_peer_config *conf)
+{
+#ifdef PCSC_FUNCS
+ if (eap_sm_set_scard_pin(sm, conf))
+ return -1;
+
+ return eap_sm_imsi_identity(sm, conf);
+#else /* PCSC_FUNCS */
+ return -1;
+#endif /* PCSC_FUNCS */
+}
+
+
+/**
+ * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @id: EAP identifier for the packet
+ * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2)
+ * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on
+ * failure
+ *
+ * This function allocates and builds an EAP-Identity/Response packet for the
+ * current network. The caller is responsible for freeing the returned data.
+ */
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ struct wpabuf *resp;
+ const u8 *identity;
+ size_t identity_len;
+
+ if (config == NULL) {
+ wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration "
+ "was not available");
+ return NULL;
+ }
+
+ if (sm->m && sm->m->get_identity &&
+ (identity = sm->m->get_identity(sm, sm->eap_method_priv,
+ &identity_len)) != NULL) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth "
+ "identity", identity, identity_len);
+ } else if (!encrypted && config->anonymous_identity) {
+ identity = config->anonymous_identity;
+ identity_len = config->anonymous_identity_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity",
+ identity, identity_len);
+ } else {
+ identity = config->identity;
+ identity_len = config->identity_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity",
+ identity, identity_len);
+ }
+
+ if (identity == NULL) {
+ wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity "
+ "configuration was not available");
+ if (config->pcsc) {
+ if (eap_sm_get_scard_identity(sm, config) < 0)
+ return NULL;
+ identity = config->identity;
+ identity_len = config->identity_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from "
+ "IMSI", identity, identity_len);
+ } else {
+ eap_sm_request_identity(sm);
+ return NULL;
+ }
+ } else if (config->pcsc) {
+ if (eap_sm_set_scard_pin(sm, config) < 0)
+ return NULL;
+ }
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_data(resp, identity, identity_len);
+
+ return resp;
+}
+
+
+static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req)
+{
+ const u8 *pos;
+ char *msg;
+ size_t i, msg_len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req,
+ &msg_len);
+ if (pos == NULL)
+ return;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data",
+ pos, msg_len);
+
+ msg = os_malloc(msg_len + 1);
+ if (msg == NULL)
+ return;
+ for (i = 0; i < msg_len; i++)
+ msg[i] = isprint(pos[i]) ? (char) pos[i] : '_';
+ msg[msg_len] = '\0';
+ wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s",
+ WPA_EVENT_EAP_NOTIFICATION, msg);
+ os_free(msg);
+}
+
+
+static struct wpabuf * eap_sm_buildNotify(int id)
+{
+ struct wpabuf *resp;
+
+ wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification");
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ return resp;
+}
+
+
+static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
+{
+ const struct eap_hdr *hdr;
+ size_t plen;
+ const u8 *pos;
+
+ sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE;
+ sm->reqId = 0;
+ sm->reqMethod = EAP_TYPE_NONE;
+ sm->reqVendor = EAP_VENDOR_IETF;
+ sm->reqVendorMethod = EAP_TYPE_NONE;
+
+ if (req == NULL || wpabuf_len(req) < sizeof(*hdr))
+ return;
+
+ hdr = wpabuf_head(req);
+ plen = be_to_host16(hdr->length);
+ if (plen > wpabuf_len(req)) {
+ wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
+ "(len=%lu plen=%lu)",
+ (unsigned long) wpabuf_len(req),
+ (unsigned long) plen);
+ return;
+ }
+
+ sm->reqId = hdr->identifier;
+
+ if (sm->workaround) {
+ const u8 *addr[1];
+ addr[0] = wpabuf_head(req);
+ md5_vector(1, addr, &plen, sm->req_md5);
+ }
+
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (plen < sizeof(*hdr) + 1) {
+ wpa_printf(MSG_DEBUG, "EAP: Too short EAP-Request - "
+ "no Type field");
+ return;
+ }
+ sm->rxReq = TRUE;
+ pos = (const u8 *) (hdr + 1);
+ sm->reqMethod = *pos++;
+ if (sm->reqMethod == EAP_TYPE_EXPANDED) {
+ if (plen < sizeof(*hdr) + 8) {
+ wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
+ "expanded EAP-Packet (plen=%lu)",
+ (unsigned long) plen);
+ return;
+ }
+ sm->reqVendor = WPA_GET_BE24(pos);
+ pos += 3;
+ sm->reqVendorMethod = WPA_GET_BE32(pos);
+ }
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request id=%d "
+ "method=%u vendor=%u vendorMethod=%u",
+ sm->reqId, sm->reqMethod, sm->reqVendor,
+ sm->reqVendorMethod);
+ break;
+ case EAP_CODE_RESPONSE:
+ if (sm->selectedMethod == EAP_TYPE_LEAP) {
+ /*
+ * LEAP differs from RFC 4137 by using reversed roles
+ * for mutual authentication and because of this, we
+ * need to accept EAP-Response frames if LEAP is used.
+ */
+ if (plen < sizeof(*hdr) + 1) {
+ wpa_printf(MSG_DEBUG, "EAP: Too short "
+ "EAP-Response - no Type field");
+ return;
+ }
+ sm->rxResp = TRUE;
+ pos = (const u8 *) (hdr + 1);
+ sm->reqMethod = *pos;
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for "
+ "LEAP method=%d id=%d",
+ sm->reqMethod, sm->reqId);
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response");
+ break;
+ case EAP_CODE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success");
+ sm->rxSuccess = TRUE;
+ break;
+ case EAP_CODE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
+ sm->rxFailure = TRUE;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown "
+ "code %d", hdr->code);
+ break;
+ }
+}
+
+
+static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
+ union tls_event_data *data)
+{
+ struct eap_sm *sm = ctx;
+ char *hash_hex = NULL;
+ char *cert_hex = NULL;
+
+ switch (ev) {
+ case TLS_CERT_CHAIN_FAILURE:
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR
+ "reason=%d depth=%d subject='%s' err='%s'",
+ data->cert_fail.reason,
+ data->cert_fail.depth,
+ data->cert_fail.subject,
+ data->cert_fail.reason_txt);
+ break;
+ case TLS_PEER_CERTIFICATE:
+ if (data->peer_cert.hash) {
+ size_t len = data->peer_cert.hash_len * 2 + 1;
+ hash_hex = os_malloc(len);
+ if (hash_hex) {
+ wpa_snprintf_hex(hash_hex, len,
+ data->peer_cert.hash,
+ data->peer_cert.hash_len);
+ }
+ }
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
+ "depth=%d subject='%s'%s%s",
+ data->peer_cert.depth, data->peer_cert.subject,
+ hash_hex ? " hash=" : "", hash_hex ? hash_hex : "");
+
+ if (data->peer_cert.cert) {
+ size_t len = wpabuf_len(data->peer_cert.cert) * 2 + 1;
+ cert_hex = os_malloc(len);
+ if (cert_hex == NULL)
+ break;
+ wpa_snprintf_hex(cert_hex, len,
+ wpabuf_head(data->peer_cert.cert),
+ wpabuf_len(data->peer_cert.cert));
+ wpa_msg_ctrl(sm->msg_ctx, MSG_INFO,
+ WPA_EVENT_EAP_PEER_CERT
+ "depth=%d subject='%s' cert=%s",
+ data->peer_cert.depth,
+ data->peer_cert.subject,
+ cert_hex);
+ }
+ break;
+ }
+
+ os_free(hash_hex);
+ os_free(cert_hex);
+}
+
+
+/**
+ * eap_peer_sm_init - Allocate and initialize EAP peer state machine
+ * @eapol_ctx: Context data to be used with eapol_cb calls
+ * @eapol_cb: Pointer to EAPOL callback functions
+ * @msg_ctx: Context data for wpa_msg() calls
+ * @conf: EAP configuration
+ * Returns: Pointer to the allocated EAP state machine or %NULL on failure
+ *
+ * This function allocates and initializes an EAP state machine. In addition,
+ * this initializes TLS library for the new EAP state machine. eapol_cb pointer
+ * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP
+ * state machine. Consequently, the caller must make sure that this data
+ * structure remains alive while the EAP state machine is active.
+ */
+struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
+ struct eapol_callbacks *eapol_cb,
+ void *msg_ctx, struct eap_config *conf)
+{
+ struct eap_sm *sm;
+ struct tls_config tlsconf;
+
+ sm = os_zalloc(sizeof(*sm));
+ if (sm == NULL)
+ return NULL;
+ sm->eapol_ctx = eapol_ctx;
+ sm->eapol_cb = eapol_cb;
+ sm->msg_ctx = msg_ctx;
+ sm->ClientTimeout = 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;
+ sm->ssl_ctx = tls_init(&tlsconf);
+ if (sm->ssl_ctx == NULL) {
+ wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
+ "context.");
+ os_free(sm);
+ return NULL;
+ }
+
+ return sm;
+}
+
+
+/**
+ * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function deinitializes EAP state machine and frees all allocated
+ * resources.
+ */
+void eap_peer_sm_deinit(struct eap_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ eap_deinit_prev_method(sm, "EAP deinit");
+ eap_sm_abort(sm);
+ tls_deinit(sm->ssl_ctx);
+ os_free(sm);
+}
+
+
+/**
+ * eap_peer_sm_step - Step EAP peer state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: 1 if EAP state was changed or 0 if not
+ *
+ * This function advances EAP state machine to a new state to match with the
+ * current variables. This should be called whenever variables used by the EAP
+ * state machine have changed.
+ */
+int eap_peer_sm_step(struct eap_sm *sm)
+{
+ int res = 0;
+ do {
+ sm->changed = FALSE;
+ SM_STEP_RUN(EAP);
+ if (sm->changed)
+ res = 1;
+ } while (sm->changed);
+ return res;
+}
+
+
+/**
+ * eap_sm_abort - Abort EAP authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Release system resources that have been allocated for the authentication
+ * session without fully deinitializing the EAP state machine.
+ */
+void eap_sm_abort(struct eap_sm *sm)
+{
+ wpabuf_free(sm->lastRespData);
+ sm->lastRespData = NULL;
+ wpabuf_free(sm->eapRespData);
+ sm->eapRespData = NULL;
+ os_free(sm->eapKeyData);
+ sm->eapKeyData = NULL;
+
+ /* This is not clearly specified in the EAP statemachines draft, but
+ * it seems necessary to make sure that some of the EAPOL variables get
+ * cleared for the next authentication. */
+ eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static const char * eap_sm_state_txt(int state)
+{
+ switch (state) {
+ case EAP_INITIALIZE:
+ return "INITIALIZE";
+ case EAP_DISABLED:
+ return "DISABLED";
+ case EAP_IDLE:
+ return "IDLE";
+ case EAP_RECEIVED:
+ return "RECEIVED";
+ case EAP_GET_METHOD:
+ return "GET_METHOD";
+ case EAP_METHOD:
+ return "METHOD";
+ case EAP_SEND_RESPONSE:
+ return "SEND_RESPONSE";
+ case EAP_DISCARD:
+ return "DISCARD";
+ case EAP_IDENTITY:
+ return "IDENTITY";
+ case EAP_NOTIFICATION:
+ return "NOTIFICATION";
+ case EAP_RETRANSMIT:
+ return "RETRANSMIT";
+ case EAP_SUCCESS:
+ return "SUCCESS";
+ case EAP_FAILURE:
+ return "FAILURE";
+ default:
+ return "UNKNOWN";
+ }
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eap_sm_method_state_txt(EapMethodState state)
+{
+ switch (state) {
+ case METHOD_NONE:
+ return "NONE";
+ case METHOD_INIT:
+ return "INIT";
+ case METHOD_CONT:
+ return "CONT";
+ case METHOD_MAY_CONT:
+ return "MAY_CONT";
+ case METHOD_DONE:
+ return "DONE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static const char * eap_sm_decision_txt(EapDecision decision)
+{
+ switch (decision) {
+ case DECISION_FAIL:
+ return "FAIL";
+ case DECISION_COND_SUCC:
+ return "COND_SUCC";
+ case DECISION_UNCOND_SUCC:
+ return "UNCOND_SUCC";
+ default:
+ return "UNKNOWN";
+ }
+}
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_CTRL_IFACE
+
+/**
+ * eap_sm_get_status - Get EAP state machine status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query EAP state machine for status information. This function fills in a
+ * text area with current status information from the EAPOL state machine. If
+ * the buffer (buf) is not large enough, status information will be truncated
+ * to fit the buffer.
+ */
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
+{
+ int len, ret;
+
+ if (sm == NULL)
+ return 0;
+
+ len = os_snprintf(buf, buflen,
+ "EAP state=%s\n",
+ eap_sm_state_txt(sm->EAP_state));
+ if (len < 0 || (size_t) len >= buflen)
+ return 0;
+
+ if (sm->selectedMethod != EAP_TYPE_NONE) {
+ const char *name;
+ if (sm->m) {
+ name = sm->m->name;
+ } else {
+ const struct eap_method *m =
+ eap_peer_get_eap_method(EAP_VENDOR_IETF,
+ sm->selectedMethod);
+ if (m)
+ name = m->name;
+ else
+ name = "?";
+ }
+ ret = os_snprintf(buf + len, buflen - len,
+ "selectedMethod=%d (EAP-%s)\n",
+ sm->selectedMethod, name);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ if (sm->m && sm->m->get_status) {
+ len += sm->m->get_status(sm, sm->eap_method_priv,
+ buf + len, buflen - len,
+ verbose);
+ }
+ }
+
+ if (verbose) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "reqMethod=%d\n"
+ "methodState=%s\n"
+ "decision=%s\n"
+ "ClientTimeout=%d\n",
+ sm->reqMethod,
+ eap_sm_method_state_txt(sm->methodState),
+ eap_sm_decision_txt(sm->decision),
+ sm->ClientTimeout);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+ }
+
+ return len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+typedef enum {
+ TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP, TYPE_PIN, TYPE_NEW_PASSWORD,
+ TYPE_PASSPHRASE
+} eap_ctrl_req_type;
+
+static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
+ const char *msg, size_t msglen)
+{
+ struct eap_peer_config *config;
+ char *field, *txt, *tmp;
+
+ if (sm == NULL)
+ return;
+ config = eap_get_config(sm);
+ if (config == NULL)
+ return;
+
+ switch (type) {
+ case TYPE_IDENTITY:
+ field = "IDENTITY";
+ txt = "Identity";
+ config->pending_req_identity++;
+ break;
+ case TYPE_PASSWORD:
+ field = "PASSWORD";
+ txt = "Password";
+ config->pending_req_password++;
+ break;
+ case TYPE_NEW_PASSWORD:
+ field = "NEW_PASSWORD";
+ txt = "New Password";
+ config->pending_req_new_password++;
+ break;
+ case TYPE_PIN:
+ field = "PIN";
+ txt = "PIN";
+ config->pending_req_pin++;
+ break;
+ case TYPE_OTP:
+ field = "OTP";
+ if (msg) {
+ tmp = os_malloc(msglen + 3);
+ if (tmp == NULL)
+ return;
+ tmp[0] = '[';
+ os_memcpy(tmp + 1, msg, msglen);
+ tmp[msglen + 1] = ']';
+ tmp[msglen + 2] = '\0';
+ txt = tmp;
+ os_free(config->pending_req_otp);
+ config->pending_req_otp = tmp;
+ config->pending_req_otp_len = msglen + 3;
+ } else {
+ if (config->pending_req_otp == NULL)
+ return;
+ txt = config->pending_req_otp;
+ }
+ break;
+ case TYPE_PASSPHRASE:
+ field = "PASSPHRASE";
+ txt = "Private key passphrase";
+ config->pending_req_passphrase++;
+ break;
+ default:
+ return;
+ }
+
+ if (sm->eapol_cb->eap_param_needed)
+ sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eap_sm_request(sm, type, msg, msglen) do { } while (0)
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+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, TYPE_IDENTITY, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_password - Request password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request password information for the
+ * current network. This is normally called when the password is not included
+ * in the network configuration. The request will be sent to monitor programs
+ * through the control interface.
+ */
+void eap_sm_request_password(struct eap_sm *sm)
+{
+ eap_sm_request(sm, TYPE_PASSWORD, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_new_password - Request new password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request new password information for
+ * the current network. This is normally called when the EAP method indicates
+ * that the current password has expired and password change is required. The
+ * request will be sent to monitor programs through the control interface.
+ */
+void eap_sm_request_new_password(struct eap_sm *sm)
+{
+ eap_sm_request(sm, TYPE_NEW_PASSWORD, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_pin - Request SIM or smart card PIN from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request SIM or smart card PIN
+ * information for the current network. This is normally called when the PIN is
+ * not included in the network configuration. The request will be sent to
+ * monitor programs through the control interface.
+ */
+void eap_sm_request_pin(struct eap_sm *sm)
+{
+ eap_sm_request(sm, TYPE_PIN, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_otp - Request one time password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @msg: Message to be displayed to the user when asking for OTP
+ * @msg_len: Length of the user displayable message
+ *
+ * EAP methods can call this function to request open time password (OTP) for
+ * the current network. The request will be sent to monitor programs through
+ * the control interface.
+ */
+void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len)
+{
+ eap_sm_request(sm, TYPE_OTP, msg, msg_len);
+}
+
+
+/**
+ * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request passphrase for a private key
+ * for the current network. This is normally called when the passphrase is not
+ * included in the network configuration. The request will be sent to monitor
+ * programs through the control interface.
+ */
+void eap_sm_request_passphrase(struct eap_sm *sm)
+{
+ eap_sm_request(sm, TYPE_PASSPHRASE, NULL, 0);
+}
+
+
+/**
+ * eap_sm_notify_ctrl_attached - Notification of attached monitor
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Notify EAP state machines that a monitor was attached to the control
+ * interface to trigger re-sending of pending requests for user input.
+ */
+void eap_sm_notify_ctrl_attached(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (config == NULL)
+ return;
+
+ /* Re-send any pending requests for user data since a new control
+ * interface was added. This handles cases where the EAP authentication
+ * starts immediately after system startup when the user interface is
+ * not yet running. */
+ if (config->pending_req_identity)
+ eap_sm_request_identity(sm);
+ if (config->pending_req_password)
+ eap_sm_request_password(sm);
+ if (config->pending_req_new_password)
+ eap_sm_request_new_password(sm);
+ if (config->pending_req_otp)
+ eap_sm_request_otp(sm, NULL, 0);
+ if (config->pending_req_pin)
+ eap_sm_request_pin(sm);
+ if (config->pending_req_passphrase)
+ eap_sm_request_passphrase(sm);
+}
+
+
+static int eap_allowed_phase2_type(int vendor, int type)
+{
+ if (vendor != EAP_VENDOR_IETF)
+ return 0;
+ return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
+ type != EAP_TYPE_FAST;
+}
+
+
+/**
+ * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name
+ * @name: EAP method name, e.g., MD5
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers that are allowed for
+ * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with
+ * EAP-PEAP, EAP-TTLS, and EAP-FAST.
+ */
+u32 eap_get_phase2_type(const char *name, int *vendor)
+{
+ int v;
+ u8 type = eap_peer_get_type(name, &v);
+ if (eap_allowed_phase2_type(v, type)) {
+ *vendor = v;
+ return type;
+ }
+ *vendor = EAP_VENDOR_IETF;
+ return EAP_TYPE_NONE;
+}
+
+
+/**
+ * eap_get_phase2_types - Get list of allowed EAP phase 2 types
+ * @config: Pointer to a network configuration
+ * @count: Pointer to a variable to be filled with number of returned EAP types
+ * Returns: Pointer to allocated type list or %NULL on failure
+ *
+ * This function generates an array of allowed EAP phase 2 (tunneled) types for
+ * the given network configuration.
+ */
+struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
+ size_t *count)
+{
+ struct eap_method_type *buf;
+ u32 method;
+ int vendor;
+ size_t mcount;
+ const struct eap_method *methods, *m;
+
+ methods = eap_peer_get_methods(&mcount);
+ if (methods == NULL)
+ return NULL;
+ *count = 0;
+ buf = os_malloc(mcount * sizeof(struct eap_method_type));
+ if (buf == NULL)
+ return NULL;
+
+ for (m = methods; m; m = m->next) {
+ vendor = m->vendor;
+ method = m->method;
+ if (eap_allowed_phase2_type(vendor, method)) {
+ if (vendor == EAP_VENDOR_IETF &&
+ method == EAP_TYPE_TLS && config &&
+ config->private_key2 == NULL)
+ continue;
+ buf[*count].vendor = vendor;
+ buf[*count].method = method;
+ (*count)++;
+ }
+ }
+
+ return buf;
+}
+
+
+/**
+ * eap_set_fast_reauth - Update fast_reauth setting
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @enabled: 1 = Fast reauthentication is enabled, 0 = Disabled
+ */
+void eap_set_fast_reauth(struct eap_sm *sm, int enabled)
+{
+ sm->fast_reauth = enabled;
+}
+
+
+/**
+ * eap_set_workaround - Update EAP workarounds setting
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds
+ */
+void eap_set_workaround(struct eap_sm *sm, unsigned int workaround)
+{
+ sm->workaround = workaround;
+}
+
+
+/**
+ * eap_get_config - Get current network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the current network configuration or %NULL if not found
+ *
+ * EAP peer methods should avoid using this function if they can use other
+ * access functions, like eap_get_config_identity() and
+ * eap_get_config_password(), that do not require direct access to
+ * struct eap_peer_config.
+ */
+struct eap_peer_config * eap_get_config(struct eap_sm *sm)
+{
+ return sm->eapol_cb->get_config(sm->eapol_ctx);
+}
+
+
+/**
+ * eap_get_config_identity - Get identity from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the identity
+ * Returns: Pointer to the identity or %NULL if not found
+ */
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->identity_len;
+ return config->identity;
+}
+
+
+/**
+ * eap_get_config_password - Get password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the password
+ * Returns: Pointer to the password or %NULL if not found
+ */
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->password_len;
+ return config->password;
+}
+
+
+/**
+ * eap_get_config_password2 - Get password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the password
+ * @hash: Buffer for returning whether the password is stored as a
+ * NtPasswordHash instead of plaintext password; can be %NULL if this
+ * information is not needed
+ * Returns: Pointer to the password or %NULL if not found
+ */
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->password_len;
+ if (hash)
+ *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
+ return config->password;
+}
+
+
+/**
+ * eap_get_config_new_password - Get new password from network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the new password
+ * Returns: Pointer to the new password or %NULL if not found
+ */
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->new_password_len;
+ return config->new_password;
+}
+
+
+/**
+ * eap_get_config_otp - Get one-time password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the one-time password
+ * Returns: Pointer to the one-time password or %NULL if not found
+ */
+const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ *len = config->otp_len;
+ return config->otp;
+}
+
+
+/**
+ * eap_clear_config_otp - Clear used one-time password
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function clears a used one-time password (OTP) from the current network
+ * configuration. This should be called when the OTP has been used and is not
+ * needed anymore.
+ */
+void eap_clear_config_otp(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return;
+ os_memset(config->otp, 0, config->otp_len);
+ os_free(config->otp);
+ config->otp = NULL;
+ config->otp_len = 0;
+}
+
+
+/**
+ * eap_get_config_phase1 - Get phase1 data from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the phase1 data or %NULL if not found
+ */
+const char * eap_get_config_phase1(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ return config->phase1;
+}
+
+
+/**
+ * eap_get_config_phase2 - Get phase2 data from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the phase1 data or %NULL if not found
+ */
+const char * eap_get_config_phase2(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return NULL;
+ return config->phase2;
+}
+
+
+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_eapKeyData - Get master session key (MSK) from EAP state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Pointer to variable that will be set to number of bytes in the key
+ * Returns: Pointer to the EAP keying data or %NULL on failure
+ *
+ * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The
+ * key is available only after a successful authentication. EAP state machine
+ * continues to manage the key data and the caller must not change or free the
+ * returned data.
+ */
+const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len)
+{
+ if (sm == NULL || sm->eapKeyData == NULL) {
+ *len = 0;
+ return NULL;
+ }
+
+ *len = sm->eapKeyDataLen;
+ return sm->eapKeyData;
+}
+
+
+/**
+ * eap_get_eapKeyData - Get EAP response data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure
+ *
+ * Fetch EAP response (eapRespData) from the EAP state machine. This data is
+ * available when EAP state machine has processed an incoming EAP request. The
+ * EAP state machine does not maintain a reference to the response after this
+ * function is called and the caller is responsible for freeing the data.
+ */
+struct wpabuf * eap_get_eapRespData(struct eap_sm *sm)
+{
+ struct wpabuf *resp;
+
+ if (sm == NULL || sm->eapRespData == NULL)
+ return NULL;
+
+ resp = sm->eapRespData;
+ sm->eapRespData = NULL;
+
+ return resp;
+}
+
+
+/**
+ * eap_sm_register_scard_ctx - Notification of smart card context
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @ctx: Context data for smart card operations
+ *
+ * Notify EAP state machines of context data for smart card operations. This
+ * context data will be used as a parameter for scard_*() functions.
+ */
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx)
+{
+ if (sm)
+ sm->scard_ctx = ctx;
+}
+
+
+/**
+ * eap_set_config_blob - Set or add a named configuration blob
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an existing
+ * blob.
+ */
+void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+/**
+ * eap_get_config_blob - Get a named configuration blob
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm,
+ const char *name)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name);
+#else /* CONFIG_NO_CONFIG_BLOBS */
+ return NULL;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+/**
+ * eap_set_force_disabled - Set force_disabled flag
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @disabled: 1 = EAP disabled, 0 = EAP enabled
+ *
+ * This function is used to force EAP state machine to be disabled when it is
+ * not in use (e.g., with WPA-PSK or plaintext connections).
+ */
+void eap_set_force_disabled(struct eap_sm *sm, int disabled)
+{
+ sm->force_disabled = disabled;
+}
+
+
+ /**
+ * eap_notify_pending - Notify that EAP method is ready to re-process a request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * An EAP method can perform a pending operation (e.g., to get a response from
+ * an external process). Once the response is available, this function can be
+ * used to request EAPOL state machine to retry delivering the previously
+ * received (and still unanswered) EAP request to EAP state machine.
+ */
+void eap_notify_pending(struct eap_sm *sm)
+{
+ sm->eapol_cb->notify_pending(sm->eapol_ctx);
+}
+
+
+/**
+ * eap_invalidate_cached_session - Mark cached session data invalid
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ */
+void eap_invalidate_cached_session(struct eap_sm *sm)
+{
+ if (sm)
+ eap_deinit_prev_method(sm, "invalidate");
+}
+
+
+int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf)
+{
+ if (conf->identity_len != WSC_ID_ENROLLEE_LEN ||
+ os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN))
+ return 0; /* Not a WPS Enrollee */
+
+ if (conf->phase1 == NULL || os_strstr(conf->phase1, "pbc=1") == NULL)
+ return 0; /* Not using PBC */
+
+ return 1;
+}
+
+
+int eap_is_wps_pin_enrollee(struct eap_peer_config *conf)
+{
+ if (conf->identity_len != WSC_ID_ENROLLEE_LEN ||
+ os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN))
+ return 0; /* Not a WPS Enrollee */
+
+ if (conf->phase1 == NULL || os_strstr(conf->phase1, "pin=") == NULL)
+ return 0; /* Not using PIN */
+
+ return 1;
+}
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
new file mode 100644
index 0000000..3550909
--- /dev/null
+++ b/src/eap_peer/eap.h
@@ -0,0 +1,292 @@
+/*
+ * EAP peer state machine functions (RFC 4137)
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_H
+#define EAP_H
+
+#include "common/defs.h"
+#include "eap_common/eap_defs.h"
+#include "eap_peer/eap_methods.h"
+
+struct eap_sm;
+struct wpa_config_blob;
+struct wpabuf;
+
+struct eap_method_type {
+ int vendor;
+ u32 method;
+};
+
+#ifdef IEEE8021X_EAPOL
+
+/**
+ * enum eapol_bool_var - EAPOL boolean state variables for EAP state machine
+ *
+ * These variables are used in the interface between EAP peer state machine and
+ * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
+ * expected to maintain these variables and register a callback functions for
+ * EAP state machine to get and set the variables.
+ */
+enum eapol_bool_var {
+ /**
+ * EAPOL_eapSuccess - EAP SUCCESS state reached
+ *
+ * EAP state machine reads and writes this value.
+ */
+ EAPOL_eapSuccess,
+
+ /**
+ * EAPOL_eapRestart - Lower layer request to restart authentication
+ *
+ * Set to TRUE in lower layer, FALSE in EAP state machine.
+ */
+ EAPOL_eapRestart,
+
+ /**
+ * EAPOL_eapFail - EAP FAILURE state reached
+ *
+ * EAP state machine writes this value.
+ */
+ EAPOL_eapFail,
+
+ /**
+ * EAPOL_eapResp - Response to send
+ *
+ * Set to TRUE in EAP state machine, FALSE in lower layer.
+ */
+ EAPOL_eapResp,
+
+ /**
+ * EAPOL_eapNoResp - Request has been process; no response to send
+ *
+ * Set to TRUE in EAP state machine, FALSE in lower layer.
+ */
+ EAPOL_eapNoResp,
+
+ /**
+ * EAPOL_eapReq - EAP request available from lower layer
+ *
+ * Set to TRUE in lower layer, FALSE in EAP state machine.
+ */
+ EAPOL_eapReq,
+
+ /**
+ * EAPOL_portEnabled - Lower layer is ready for communication
+ *
+ * EAP state machines reads this value.
+ */
+ EAPOL_portEnabled,
+
+ /**
+ * EAPOL_altAccept - Alternate indication of success (RFC3748)
+ *
+ * EAP state machines reads this value.
+ */
+ EAPOL_altAccept,
+
+ /**
+ * EAPOL_altReject - Alternate indication of failure (RFC3748)
+ *
+ * EAP state machines reads this value.
+ */
+ EAPOL_altReject
+};
+
+/**
+ * enum eapol_int_var - EAPOL integer state variables for EAP state machine
+ *
+ * These variables are used in the interface between EAP peer state machine and
+ * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
+ * expected to maintain these variables and register a callback functions for
+ * EAP state machine to get and set the variables.
+ */
+enum eapol_int_var {
+ /**
+ * EAPOL_idleWhile - Outside time for EAP peer timeout
+ *
+ * This integer variable is used to provide an outside timer that the
+ * external (to EAP state machine) code must decrement by one every
+ * second until the value reaches zero. This is used in the same way as
+ * EAPOL state machine timers. EAP state machine reads and writes this
+ * value.
+ */
+ EAPOL_idleWhile
+};
+
+/**
+ * struct eapol_callbacks - Callback functions from EAP to lower layer
+ *
+ * This structure defines the callback functions that EAP state machine
+ * requires from the lower layer (usually EAPOL state machine) for updating
+ * state variables and requesting information. eapol_ctx from
+ * eap_peer_sm_init() call will be used as the ctx parameter for these
+ * callback functions.
+ */
+struct eapol_callbacks {
+ /**
+ * get_config - Get pointer to the current network configuration
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ */
+ struct eap_peer_config * (*get_config)(void *ctx);
+
+ /**
+ * get_bool - Get a boolean EAPOL state variable
+ * @variable: EAPOL boolean variable to get
+ * Returns: Value of the EAPOL variable
+ */
+ Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable);
+
+ /**
+ * set_bool - Set a boolean EAPOL state variable
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @variable: EAPOL boolean variable to set
+ * @value: Value for the EAPOL variable
+ */
+ void (*set_bool)(void *ctx, enum eapol_bool_var variable,
+ Boolean value);
+
+ /**
+ * get_int - Get an integer EAPOL state variable
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @variable: EAPOL integer variable to get
+ * Returns: Value of the EAPOL variable
+ */
+ unsigned int (*get_int)(void *ctx, enum eapol_int_var variable);
+
+ /**
+ * set_int - Set an integer EAPOL state variable
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @variable: EAPOL integer variable to set
+ * @value: Value for the EAPOL variable
+ */
+ void (*set_int)(void *ctx, enum eapol_int_var variable,
+ unsigned int value);
+
+ /**
+ * get_eapReqData - Get EAP-Request data
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @len: Pointer to variable that will be set to eapReqDataLen
+ * Returns: Reference to eapReqData (EAP state machine will not free
+ * this) or %NULL if eapReqData not available.
+ */
+ struct wpabuf * (*get_eapReqData)(void *ctx);
+
+ /**
+ * set_config_blob - Set named configuration blob
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an
+ * existing blob.
+ */
+ void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+
+ /**
+ * get_config_blob - Get a named configuration blob
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+ const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+ const char *name);
+
+ /**
+ * notify_pending - Notify that a pending request can be retried
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ *
+ * An EAP method can perform a pending operation (e.g., to get a
+ * response from an external process). Once the response is available,
+ * this callback function can be used to request EAPOL state machine to
+ * retry delivering the previously received (and still unanswered) EAP
+ * request to EAP state machine.
+ */
+ void (*notify_pending)(void *ctx);
+
+ /**
+ * eap_param_needed - Notify that EAP parameter is needed
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @field: Field name (e.g., "IDENTITY")
+ * @txt: User readable text describing the required parameter
+ */
+ void (*eap_param_needed)(void *ctx, const char *field,
+ const char *txt);
+};
+
+/**
+ * struct eap_config - Configuration for EAP state machine
+ */
+struct eap_config {
+ /**
+ * opensc_engine_path - OpenSC engine for OpenSSL engine support
+ *
+ * Usually, path to engine_opensc.so.
+ */
+ const char *opensc_engine_path;
+ /**
+ * pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support
+ *
+ * Usually, path to engine_pkcs11.so.
+ */
+ const char *pkcs11_engine_path;
+ /**
+ * pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine
+ *
+ * Usually, path to opensc-pkcs11.so.
+ */
+ const char *pkcs11_module_path;
+ /**
+ * wps - WPS context data
+ *
+ * This is only used by EAP-WSC and can be left %NULL if not available.
+ */
+ struct wps_context *wps;
+};
+
+struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
+ struct eapol_callbacks *eapol_cb,
+ void *msg_ctx, struct eap_config *conf);
+void eap_peer_sm_deinit(struct eap_sm *sm);
+int eap_peer_sm_step(struct eap_sm *sm);
+void eap_sm_abort(struct eap_sm *sm);
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen,
+ int verbose);
+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_notify_ctrl_attached(struct eap_sm *sm);
+u32 eap_get_phase2_type(const char *name, int *vendor);
+struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
+ size_t *count);
+void eap_set_fast_reauth(struct eap_sm *sm, int enabled);
+void eap_set_workaround(struct eap_sm *sm, unsigned int workaround);
+void eap_set_force_disabled(struct eap_sm *sm, int disabled);
+int eap_key_available(struct eap_sm *sm);
+void eap_notify_success(struct eap_sm *sm);
+void eap_notify_lower_layer_success(struct eap_sm *sm);
+const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
+struct wpabuf * eap_get_eapRespData(struct eap_sm *sm);
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
+void eap_invalidate_cached_session(struct eap_sm *sm);
+
+int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf);
+int eap_is_wps_pin_enrollee(struct eap_peer_config *conf);
+
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* EAP_H */
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
new file mode 100644
index 0000000..182f01a
--- /dev/null
+++ b/src/eap_peer/eap_aka.c
@@ -0,0 +1,1389 @@
+/*
+ * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "pcsc_funcs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/milenage.h"
+#include "eap_common/eap_sim_common.h"
+#include "eap_config.h"
+#include "eap_i.h"
+
+
+struct eap_aka_data {
+ u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN];
+ size_t res_len;
+ u8 nonce_s[EAP_SIM_NONCE_S_LEN];
+ u8 mk[EAP_SIM_MK_LEN];
+ u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
+ u8 k_encr[EAP_SIM_K_ENCR_LEN];
+ u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */
+ u8 msk[EAP_SIM_KEYING_DATA_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+ u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN];
+ u8 auts[EAP_AKA_AUTS_LEN];
+
+ int num_id_req, num_notification;
+ u8 *pseudonym;
+ size_t pseudonym_len;
+ u8 *reauth_id;
+ size_t reauth_id_len;
+ int reauth;
+ unsigned int counter, counter_too_small;
+ u8 *last_eap_identity;
+ size_t last_eap_identity_len;
+ enum {
+ CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE
+ } state;
+
+ struct wpabuf *id_msgs;
+ int prev_id;
+ int result_ind, use_result_ind;
+ u8 eap_method;
+ u8 *network_name;
+ size_t network_name_len;
+ u16 kdf;
+ int kdf_negotiation;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_aka_state_txt(int state)
+{
+ switch (state) {
+ case CONTINUE:
+ return "CONTINUE";
+ case RESULT_SUCCESS:
+ return "RESULT_SUCCESS";
+ case RESULT_FAILURE:
+ return "RESULT_FAILURE";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "?";
+ }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_aka_state(struct eap_aka_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s",
+ eap_aka_state_txt(data->state),
+ eap_aka_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_aka_init(struct eap_sm *sm)
+{
+ struct eap_aka_data *data;
+ const char *phase1 = eap_get_config_phase1(sm);
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->eap_method = EAP_TYPE_AKA;
+
+ eap_aka_state(data, CONTINUE);
+ data->prev_id = -1;
+
+ data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL;
+
+ return data;
+}
+
+
+#ifdef EAP_AKA_PRIME
+static void * eap_aka_prime_init(struct eap_sm *sm)
+{
+ struct eap_aka_data *data = eap_aka_init(sm);
+ if (data == NULL)
+ return NULL;
+ data->eap_method = EAP_TYPE_AKA_PRIME;
+ return data;
+}
+#endif /* EAP_AKA_PRIME */
+
+
+static void eap_aka_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ if (data) {
+ os_free(data->pseudonym);
+ os_free(data->reauth_id);
+ os_free(data->last_eap_identity);
+ wpabuf_free(data->id_msgs);
+ os_free(data->network_name);
+ os_free(data);
+ }
+}
+
+
+static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
+{
+ struct eap_peer_config *conf;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm");
+
+ conf = eap_get_config(sm);
+ if (conf == NULL)
+ return -1;
+ if (conf->pcsc) {
+ return scard_umts_auth(sm->scard_ctx, data->rand,
+ data->autn, data->res, &data->res_len,
+ data->ik, data->ck, data->auts);
+ }
+
+#ifdef CONFIG_USIM_SIMULATOR
+ if (conf->password) {
+ u8 opc[16], k[16], sqn[6];
+ const char *pos;
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Use internal Milenage "
+ "implementation for UMTS authentication");
+ if (conf->password_len < 78) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: invalid Milenage "
+ "password");
+ return -1;
+ }
+ pos = (const char *) conf->password;
+ if (hexstr2bin(pos, k, 16))
+ return -1;
+ pos += 32;
+ if (*pos != ':')
+ return -1;
+ pos++;
+
+ if (hexstr2bin(pos, opc, 16))
+ return -1;
+ pos += 32;
+ if (*pos != ':')
+ return -1;
+ pos++;
+
+ if (hexstr2bin(pos, sqn, 6))
+ return -1;
+
+ return milenage_check(opc, k, sqn, data->rand, data->autn,
+ data->ik, data->ck,
+ data->res, &data->res_len, data->auts);
+ }
+#endif /* CONFIG_USIM_SIMULATOR */
+
+#ifdef CONFIG_USIM_HARDCODED
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Use hardcoded Kc and SRES values for "
+ "testing");
+
+ /* These hardcoded Kc and SRES values are used for testing.
+ * Could consider making them configurable. */
+ os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN);
+ data->res_len = EAP_AKA_RES_MAX_LEN;
+ os_memset(data->ik, '3', EAP_AKA_IK_LEN);
+ os_memset(data->ck, '4', EAP_AKA_CK_LEN);
+ {
+ u8 autn[EAP_AKA_AUTN_LEN];
+ os_memset(autn, '1', EAP_AKA_AUTN_LEN);
+ if (os_memcmp(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match "
+ "with expected value");
+ return -1;
+ }
+ }
+#if 0
+ {
+ static int test_resync = 1;
+ if (test_resync) {
+ /* Test Resynchronization */
+ test_resync = 0;
+ return -2;
+ }
+ }
+#endif
+ return 0;
+
+#else /* CONFIG_USIM_HARDCODED */
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorith "
+ "enabled");
+ return -1;
+
+#endif /* CONFIG_USIM_HARDCODED */
+}
+
+
+#define CLEAR_PSEUDONYM 0x01
+#define CLEAR_REAUTH_ID 0x02
+#define CLEAR_EAP_ID 0x04
+
+static void eap_aka_clear_identities(struct eap_aka_data *data, int id)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s",
+ id & CLEAR_PSEUDONYM ? " pseudonym" : "",
+ id & CLEAR_REAUTH_ID ? " reauth_id" : "",
+ id & CLEAR_EAP_ID ? " eap_id" : "");
+ if (id & CLEAR_PSEUDONYM) {
+ os_free(data->pseudonym);
+ data->pseudonym = NULL;
+ data->pseudonym_len = 0;
+ }
+ if (id & CLEAR_REAUTH_ID) {
+ os_free(data->reauth_id);
+ data->reauth_id = NULL;
+ data->reauth_id_len = 0;
+ }
+ if (id & CLEAR_EAP_ID) {
+ os_free(data->last_eap_identity);
+ data->last_eap_identity = NULL;
+ data->last_eap_identity_len = 0;
+ }
+}
+
+
+static int eap_aka_learn_ids(struct eap_aka_data *data,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->next_pseudonym) {
+ os_free(data->pseudonym);
+ data->pseudonym = os_malloc(attr->next_pseudonym_len);
+ if (data->pseudonym == NULL) {
+ wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
+ "next pseudonym");
+ return -1;
+ }
+ os_memcpy(data->pseudonym, attr->next_pseudonym,
+ attr->next_pseudonym_len);
+ data->pseudonym_len = attr->next_pseudonym_len;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
+ data->pseudonym,
+ data->pseudonym_len);
+ }
+
+ if (attr->next_reauth_id) {
+ os_free(data->reauth_id);
+ data->reauth_id = os_malloc(attr->next_reauth_id_len);
+ if (data->reauth_id == NULL) {
+ wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
+ "next reauth_id");
+ return -1;
+ }
+ os_memcpy(data->reauth_id, attr->next_reauth_id,
+ attr->next_reauth_id_len);
+ data->reauth_id_len = attr->next_reauth_id_len;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-AKA: (encr) AT_NEXT_REAUTH_ID",
+ data->reauth_id,
+ data->reauth_id_len);
+ }
+
+ return 0;
+}
+
+
+static int eap_aka_add_id_msg(struct eap_aka_data *data,
+ const struct wpabuf *msg)
+{
+ if (msg == NULL)
+ return -1;
+
+ if (data->id_msgs == NULL) {
+ data->id_msgs = wpabuf_dup(msg);
+ return data->id_msgs == NULL ? -1 : 0;
+ }
+
+ if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0)
+ return -1;
+ wpabuf_put_buf(data->id_msgs, msg);
+
+ return 0;
+}
+
+
+static void eap_aka_add_checkcode(struct eap_aka_data *data,
+ struct eap_sim_msg *msg)
+{
+ const u8 *addr;
+ size_t len;
+ u8 hash[SHA256_MAC_LEN];
+
+ wpa_printf(MSG_DEBUG, " AT_CHECKCODE");
+
+ if (data->id_msgs == NULL) {
+ /*
+ * No EAP-AKA/Identity packets were exchanged - send empty
+ * checkcode.
+ */
+ eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0);
+ return;
+ }
+
+ /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */
+ addr = wpabuf_head(data->id_msgs);
+ len = wpabuf_len(data->id_msgs);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len);
+#ifdef EAP_AKA_PRIME
+ if (data->eap_method == EAP_TYPE_AKA_PRIME)
+ sha256_vector(1, &addr, &len, hash);
+ else
+#endif /* EAP_AKA_PRIME */
+ sha1_vector(1, &addr, &len, hash);
+
+ eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash,
+ data->eap_method == EAP_TYPE_AKA_PRIME ?
+ EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN);
+}
+
+
+static int eap_aka_verify_checkcode(struct eap_aka_data *data,
+ const u8 *checkcode, size_t checkcode_len)
+{
+ const u8 *addr;
+ size_t len;
+ u8 hash[SHA256_MAC_LEN];
+ size_t hash_len;
+
+ if (checkcode == NULL)
+ return -1;
+
+ if (data->id_msgs == NULL) {
+ if (checkcode_len != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
+ "indicates that AKA/Identity messages were "
+ "used, but they were not");
+ return -1;
+ }
+ return 0;
+ }
+
+ hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ?
+ EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN;
+
+ if (checkcode_len != hash_len) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
+ "indicates that AKA/Identity message were not "
+ "used, but they were");
+ return -1;
+ }
+
+ /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */
+ addr = wpabuf_head(data->id_msgs);
+ len = wpabuf_len(data->id_msgs);
+#ifdef EAP_AKA_PRIME
+ if (data->eap_method == EAP_TYPE_AKA_PRIME)
+ sha256_vector(1, &addr, &len, hash);
+ else
+#endif /* EAP_AKA_PRIME */
+ sha1_vector(1, &addr, &len, hash);
+
+ if (os_memcmp(hash, checkcode, hash_len) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id,
+ int err)
+{
+ struct eap_sim_msg *msg;
+
+ eap_aka_state(data, FAILURE);
+ data->num_id_req = 0;
+ data->num_notification = 0;
+
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+ EAP_AKA_SUBTYPE_CLIENT_ERROR);
+ eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data,
+ u8 id)
+{
+ struct eap_sim_msg *msg;
+
+ eap_aka_state(data, FAILURE);
+ data->num_id_req = 0;
+ data->num_notification = 0;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject "
+ "(id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+ EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT);
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_synchronization_failure(
+ struct eap_aka_data *data, u8 id)
+{
+ struct eap_sim_msg *msg;
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure "
+ "(id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+ EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE);
+ wpa_printf(MSG_DEBUG, " AT_AUTS");
+ eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts,
+ EAP_AKA_AUTS_LEN);
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ u8 id,
+ enum eap_sim_id_req id_req)
+{
+ const u8 *identity = NULL;
+ size_t identity_len = 0;
+ struct eap_sim_msg *msg;
+
+ data->reauth = 0;
+ if (id_req == ANY_ID && data->reauth_id) {
+ identity = data->reauth_id;
+ identity_len = data->reauth_id_len;
+ data->reauth = 1;
+ } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
+ data->pseudonym) {
+ identity = data->pseudonym;
+ identity_len = data->pseudonym_len;
+ eap_aka_clear_identities(data, CLEAR_REAUTH_ID);
+ } else if (id_req != NO_ID_REQ) {
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ eap_aka_clear_identities(data, CLEAR_PSEUDONYM |
+ CLEAR_REAUTH_ID);
+ }
+ }
+ if (id_req != NO_ID_REQ)
+ eap_aka_clear_identities(data, CLEAR_EAP_ID);
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+ EAP_AKA_SUBTYPE_IDENTITY);
+
+ if (identity) {
+ wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY",
+ identity, identity_len);
+ eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+ identity, identity_len);
+ }
+
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data,
+ u8 id)
+{
+ struct eap_sim_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+ EAP_AKA_SUBTYPE_CHALLENGE);
+ wpa_printf(MSG_DEBUG, " AT_RES");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8,
+ data->res, data->res_len);
+ eap_aka_add_checkcode(data, msg);
+ if (data->use_result_ind) {
+ wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+ }
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, data->k_aut, (u8 *) "", 0);
+}
+
+
+static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data,
+ u8 id, int counter_too_small,
+ const u8 *nonce_s)
+{
+ struct eap_sim_msg *msg;
+ unsigned int counter;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)",
+ id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+ EAP_AKA_SUBTYPE_REAUTHENTICATION);
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+ if (counter_too_small) {
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL");
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
+ counter = data->counter_too_small;
+ } else
+ counter = data->counter;
+
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+ "AT_ENCR_DATA");
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+ eap_aka_add_checkcode(data, msg);
+ if (data->use_result_ind) {
+ wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+ }
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, data->k_aut, nonce_s,
+ EAP_SIM_NONCE_S_LEN);
+}
+
+
+static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data,
+ u8 id, u16 notification)
+{
+ struct eap_sim_msg *msg;
+ u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+ EAP_AKA_SUBTYPE_NOTIFICATION);
+ if (k_aut && data->reauth) {
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+ EAP_SIM_AT_ENCR_DATA);
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+ NULL, 0);
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+ EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+ "AT_ENCR_DATA");
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+ }
+ if (k_aut) {
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ }
+ return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0);
+}
+
+
+static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ u8 id,
+ const struct wpabuf *reqData,
+ struct eap_sim_attrs *attr)
+{
+ int id_error;
+ struct wpabuf *buf;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity");
+
+ id_error = 0;
+ switch (attr->id_req) {
+ case NO_ID_REQ:
+ break;
+ case ANY_ID:
+ if (data->num_id_req > 0)
+ id_error++;
+ data->num_id_req++;
+ break;
+ case FULLAUTH_ID:
+ if (data->num_id_req > 1)
+ id_error++;
+ data->num_id_req++;
+ break;
+ case PERMANENT_ID:
+ if (data->num_id_req > 2)
+ id_error++;
+ data->num_id_req++;
+ break;
+ }
+ if (id_error) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests "
+ "used within one authentication");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ buf = eap_aka_response_identity(sm, data, id, attr->id_req);
+
+ if (data->prev_id != id) {
+ eap_aka_add_id_msg(data, reqData);
+ eap_aka_add_id_msg(data, buf);
+ data->prev_id = id;
+ }
+
+ return buf;
+}
+
+
+static int eap_aka_verify_mac(struct eap_aka_data *data,
+ const struct wpabuf *req,
+ const u8 *mac, const u8 *extra,
+ size_t extra_len)
+{
+ if (data->eap_method == EAP_TYPE_AKA_PRIME)
+ return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra,
+ extra_len);
+ return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len);
+}
+
+
+#ifdef EAP_AKA_PRIME
+static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data,
+ u8 id, u16 kdf)
+{
+ struct eap_sim_msg *msg;
+
+ data->kdf_negotiation = 1;
+ data->kdf = kdf;
+ wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF "
+ "select)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+ EAP_AKA_SUBTYPE_CHALLENGE);
+ wpa_printf(MSG_DEBUG, " AT_KDF");
+ eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0);
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data,
+ u8 id, struct eap_sim_attrs *attr)
+{
+ size_t i;
+
+ for (i = 0; i < attr->kdf_count; i++) {
+ if (attr->kdf[i] == EAP_AKA_PRIME_KDF)
+ return eap_aka_prime_kdf_select(data, id,
+ EAP_AKA_PRIME_KDF);
+ }
+
+ /* No matching KDF found - fail authentication as if AUTN had been
+ * incorrect */
+ return eap_aka_authentication_reject(data, id);
+}
+
+
+static int eap_aka_prime_kdf_valid(struct eap_aka_data *data,
+ struct eap_sim_attrs *attr)
+{
+ size_t i, j;
+
+ if (attr->kdf_count == 0)
+ return 0;
+
+ /* The only allowed (and required) duplication of a KDF is the addition
+ * of the selected KDF into the beginning of the list. */
+
+ if (data->kdf_negotiation) {
+ if (attr->kdf[0] != data->kdf) {
+ wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
+ "accept the selected KDF");
+ return 0;
+ }
+
+ for (i = 1; i < attr->kdf_count; i++) {
+ if (attr->kdf[i] == data->kdf)
+ break;
+ }
+ if (i == attr->kdf_count &&
+ attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) {
+ wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
+ "duplicate the selected KDF");
+ return 0;
+ }
+
+ /* TODO: should check that the list is identical to the one
+ * used in the previous Challenge message apart from the added
+ * entry in the beginning. */
+ }
+
+ for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) {
+ for (j = i + 1; j < attr->kdf_count; j++) {
+ if (attr->kdf[i] == attr->kdf[j]) {
+ wpa_printf(MSG_WARNING, "EAP-AKA': The server "
+ "included a duplicated KDF");
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+#endif /* EAP_AKA_PRIME */
+
+
+static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ u8 id,
+ const struct wpabuf *reqData,
+ struct eap_sim_attrs *attr)
+{
+ const u8 *identity;
+ size_t identity_len;
+ int res;
+ struct eap_sim_attrs eattr;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge");
+
+ if (attr->checkcode &&
+ eap_aka_verify_checkcode(data, attr->checkcode,
+ attr->checkcode_len)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
+ "message");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+#ifdef EAP_AKA_PRIME
+ if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+ if (!attr->kdf_input || attr->kdf_input_len == 0) {
+ wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message "
+ "did not include non-empty AT_KDF_INPUT");
+ /* Fail authentication as if AUTN had been incorrect */
+ return eap_aka_authentication_reject(data, id);
+ }
+ os_free(data->network_name);
+ data->network_name = os_malloc(attr->kdf_input_len);
+ if (data->network_name == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA': No memory for "
+ "storing Network Name");
+ return eap_aka_authentication_reject(data, id);
+ }
+ os_memcpy(data->network_name, attr->kdf_input,
+ attr->kdf_input_len);
+ data->network_name_len = attr->kdf_input_len;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name "
+ "(AT_KDF_INPUT)",
+ data->network_name, data->network_name_len);
+ /* TODO: check Network Name per 3GPP.33.402 */
+
+ if (!eap_aka_prime_kdf_valid(data, attr))
+ return eap_aka_authentication_reject(data, id);
+
+ if (attr->kdf[0] != EAP_AKA_PRIME_KDF)
+ return eap_aka_prime_kdf_neg(data, id, attr);
+
+ data->kdf = EAP_AKA_PRIME_KDF;
+ wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
+ }
+
+ if (data->eap_method == EAP_TYPE_AKA && attr->bidding) {
+ u16 flags = WPA_GET_BE16(attr->bidding);
+ if ((flags & EAP_AKA_BIDDING_FLAG_D) &&
+ eap_allowed_method(sm, EAP_VENDOR_IETF,
+ EAP_TYPE_AKA_PRIME)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from "
+ "AKA' to AKA detected");
+ /* Fail authentication as if AUTN had been incorrect */
+ return eap_aka_authentication_reject(data, id);
+ }
+ }
+#endif /* EAP_AKA_PRIME */
+
+ data->reauth = 0;
+ if (!attr->mac || !attr->rand || !attr->autn) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+ "did not include%s%s%s",
+ !attr->mac ? " AT_MAC" : "",
+ !attr->rand ? " AT_RAND" : "",
+ !attr->autn ? " AT_AUTN" : "");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+ os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN);
+ os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN);
+
+ res = eap_aka_umts_auth(sm, data);
+ if (res == -1) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
+ "failed (AUTN)");
+ return eap_aka_authentication_reject(data, id);
+ } else if (res == -2) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
+ "failed (AUTN seq# -> AUTS)");
+ return eap_aka_synchronization_failure(data, id);
+ } else if (res) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+#ifdef EAP_AKA_PRIME
+ if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+ /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
+ * needed 6-octet SQN ^ AK for CK',IK' derivation */
+ u16 amf = WPA_GET_BE16(data->autn + 6);
+ if (!(amf & 0x8000)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit "
+ "not set (AMF=0x%4x)", amf);
+ return eap_aka_authentication_reject(data, id);
+ }
+ eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik,
+ data->autn,
+ data->network_name,
+ data->network_name_len);
+ }
+#endif /* EAP_AKA_PRIME */
+ if (data->last_eap_identity) {
+ identity = data->last_eap_identity;
+ identity_len = data->last_eap_identity_len;
+ } else if (data->pseudonym) {
+ identity = data->pseudonym;
+ identity_len = data->pseudonym_len;
+ } else
+ identity = eap_get_config_identity(sm, &identity_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK "
+ "derivation", identity, identity_len);
+ if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+ eap_aka_prime_derive_keys(identity, identity_len, data->ik,
+ data->ck, data->k_encr, data->k_aut,
+ data->k_re, data->msk, data->emsk);
+ } else {
+ eap_aka_derive_mk(identity, identity_len, data->ik, data->ck,
+ data->mk);
+ eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
+ data->msk, data->emsk);
+ }
+ if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+ "used invalid AT_MAC");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ /* Old reauthentication and pseudonym identities must not be used
+ * anymore. In other words, if no new identities are received, full
+ * authentication will be used on next reauthentication. */
+ eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
+ CLEAR_EAP_ID);
+
+ if (attr->encr_data) {
+ u8 *decrypted;
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv,
+ &eattr, 0);
+ if (decrypted == NULL) {
+ return eap_aka_client_error(
+ data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+ eap_aka_learn_ids(data, &eattr);
+ os_free(decrypted);
+ }
+
+ if (data->result_ind && attr->result_ind)
+ data->use_result_ind = 1;
+
+ if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+ eap_aka_state(data, data->use_result_ind ?
+ RESULT_SUCCESS : SUCCESS);
+ }
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ /* RFC 4187 specifies that counter is initialized to one after
+ * fullauth, but initializing it to zero makes it easier to implement
+ * reauth verification. */
+ data->counter = 0;
+ return eap_aka_response_challenge(data, id);
+}
+
+
+static int eap_aka_process_notification_reauth(struct eap_aka_data *data,
+ struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+ u8 *decrypted;
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after "
+ "reauth did not include encrypted data");
+ return -1;
+ }
+
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr,
+ 0);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+ "data from notification message");
+ return -1;
+ }
+
+ if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification "
+ "message does not match with counter in reauth "
+ "message");
+ os_free(decrypted);
+ return -1;
+ }
+
+ os_free(decrypted);
+ return 0;
+}
+
+
+static int eap_aka_process_notification_auth(struct eap_aka_data *data,
+ const struct wpabuf *reqData,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->mac == NULL) {
+ wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth "
+ "Notification message");
+ return -1;
+ }
+
+ if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Notification message "
+ "used invalid AT_MAC");
+ return -1;
+ }
+
+ if (data->reauth &&
+ eap_aka_process_notification_reauth(data, attr)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification "
+ "message after reauth");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_aka_process_notification(
+ struct eap_sm *sm, struct eap_aka_data *data, u8 id,
+ const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification");
+ if (data->num_notification > 0) {
+ wpa_printf(MSG_INFO, "EAP-AKA: too many notification "
+ "rounds (only one allowed)");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+ data->num_notification++;
+ if (attr->notification == -1) {
+ wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in "
+ "Notification message");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if ((attr->notification & 0x4000) == 0 &&
+ eap_aka_process_notification_auth(data, reqData, attr)) {
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ eap_sim_report_notification(sm->msg_ctx, attr->notification, 1);
+ if (attr->notification >= 0 && attr->notification < 32768) {
+ eap_aka_state(data, FAILURE);
+ } else if (attr->notification == EAP_SIM_SUCCESS &&
+ data->state == RESULT_SUCCESS)
+ eap_aka_state(data, SUCCESS);
+ return eap_aka_response_notification(data, id, attr->notification);
+}
+
+
+static struct wpabuf * eap_aka_process_reauthentication(
+ struct eap_sm *sm, struct eap_aka_data *data, u8 id,
+ const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+ u8 *decrypted;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication");
+
+ if (attr->checkcode &&
+ eap_aka_verify_checkcode(data, attr->checkcode,
+ attr->checkcode_len)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
+ "message");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (data->reauth_id == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying "
+ "reauthentication, but no reauth_id available");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ data->reauth = 1;
+ if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+ "did not have valid AT_MAC");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+ "message did not include encrypted data");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr,
+ 0);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+ "data from reauthentication message");
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eattr.nonce_s == NULL || eattr.counter < 0) {
+ wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet",
+ !eattr.nonce_s ? " AT_NONCE_S" : "",
+ eattr.counter < 0 ? " AT_COUNTER" : "");
+ os_free(decrypted);
+ return eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
+ struct wpabuf *res;
+ wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter "
+ "(%d <= %d)", eattr.counter, data->counter);
+ data->counter_too_small = eattr.counter;
+
+ /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
+ * reauth_id must not be used to start a new reauthentication.
+ * However, since it was used in the last EAP-Response-Identity
+ * packet, it has to saved for the following fullauth to be
+ * used in MK derivation. */
+ os_free(data->last_eap_identity);
+ data->last_eap_identity = data->reauth_id;
+ data->last_eap_identity_len = data->reauth_id_len;
+ data->reauth_id = NULL;
+ data->reauth_id_len = 0;
+
+ res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s);
+ os_free(decrypted);
+
+ return res;
+ }
+ data->counter = eattr.counter;
+
+ os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S",
+ data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+ if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+ eap_aka_prime_derive_keys_reauth(data->k_re, data->counter,
+ data->reauth_id,
+ data->reauth_id_len,
+ data->nonce_s,
+ data->msk, data->emsk);
+ } else {
+ eap_sim_derive_keys_reauth(data->counter, data->reauth_id,
+ data->reauth_id_len,
+ data->nonce_s, data->mk,
+ data->msk, data->emsk);
+ }
+ eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_aka_learn_ids(data, &eattr);
+
+ if (data->result_ind && attr->result_ind)
+ data->use_result_ind = 1;
+
+ if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+ eap_aka_state(data, data->use_result_ind ?
+ RESULT_SUCCESS : SUCCESS);
+ }
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of "
+ "fast reauths performed - force fullauth");
+ eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ }
+ os_free(decrypted);
+ return eap_aka_response_reauth(data, id, 0, data->nonce_s);
+}
+
+
+static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_aka_data *data = priv;
+ const struct eap_hdr *req;
+ u8 subtype, id;
+ struct wpabuf *res;
+ const u8 *pos;
+ struct eap_sim_attrs attr;
+ size_t len;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData);
+ if (eap_get_config_identity(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured");
+ eap_sm_request_identity(sm);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData,
+ &len);
+ if (pos == NULL || len < 1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ req = wpabuf_head(reqData);
+ id = req->identifier;
+ len = be_to_host16(req->length);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ subtype = *pos++;
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype);
+ pos += 2; /* Reserved */
+
+ if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr,
+ data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
+ 0)) {
+ res = eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ goto done;
+ }
+
+ switch (subtype) {
+ case EAP_AKA_SUBTYPE_IDENTITY:
+ res = eap_aka_process_identity(sm, data, id, reqData, &attr);
+ break;
+ case EAP_AKA_SUBTYPE_CHALLENGE:
+ res = eap_aka_process_challenge(sm, data, id, reqData, &attr);
+ break;
+ case EAP_AKA_SUBTYPE_NOTIFICATION:
+ res = eap_aka_process_notification(sm, data, id, reqData,
+ &attr);
+ break;
+ case EAP_AKA_SUBTYPE_REAUTHENTICATION:
+ res = eap_aka_process_reauthentication(sm, data, id, reqData,
+ &attr);
+ break;
+ case EAP_AKA_SUBTYPE_CLIENT_ERROR:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error");
+ res = eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype);
+ res = eap_aka_client_error(data, id,
+ EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+ break;
+ }
+
+done:
+ if (data->state == FAILURE) {
+ ret->decision = DECISION_FAIL;
+ ret->methodState = METHOD_DONE;
+ } else if (data->state == SUCCESS) {
+ ret->decision = data->use_result_ind ?
+ DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
+ /*
+ * It is possible for the server to reply with AKA
+ * Notification, so we must allow the method to continue and
+ * not only accept EAP-Success at this point.
+ */
+ ret->methodState = data->use_result_ind ?
+ METHOD_DONE : METHOD_MAY_CONT;
+ } else if (data->state == RESULT_FAILURE)
+ ret->methodState = METHOD_CONT;
+ else if (data->state == RESULT_SUCCESS)
+ ret->methodState = METHOD_CONT;
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ return res;
+}
+
+
+static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ return data->pseudonym || data->reauth_id;
+}
+
+
+static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ eap_aka_clear_identities(data, CLEAR_EAP_ID);
+ data->prev_id = -1;
+ wpabuf_free(data->id_msgs);
+ data->id_msgs = NULL;
+ data->use_result_ind = 0;
+ data->kdf_negotiation = 0;
+}
+
+
+static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ eap_aka_state(data, CONTINUE);
+ return priv;
+}
+
+
+static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv,
+ size_t *len)
+{
+ struct eap_aka_data *data = priv;
+
+ if (data->reauth_id) {
+ *len = data->reauth_id_len;
+ return data->reauth_id;
+ }
+
+ if (data->pseudonym) {
+ *len = data->pseudonym_len;
+ return data->pseudonym;
+ }
+
+ return NULL;
+}
+
+
+static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_aka_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_SIM_KEYING_DATA_LEN;
+ os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_aka_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_aka_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_aka_init;
+ eap->deinit = eap_aka_deinit;
+ eap->process = eap_aka_process;
+ eap->isKeyAvailable = eap_aka_isKeyAvailable;
+ eap->getKey = eap_aka_getKey;
+ eap->has_reauth_data = eap_aka_has_reauth_data;
+ eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
+ eap->init_for_reauth = eap_aka_init_for_reauth;
+ eap->get_identity = eap_aka_get_identity;
+ eap->get_emsk = eap_aka_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
+
+
+#ifdef EAP_AKA_PRIME
+int eap_peer_aka_prime_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME,
+ "AKA'");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_aka_prime_init;
+ eap->deinit = eap_aka_deinit;
+ eap->process = eap_aka_process;
+ eap->isKeyAvailable = eap_aka_isKeyAvailable;
+ eap->getKey = eap_aka_getKey;
+ eap->has_reauth_data = eap_aka_has_reauth_data;
+ eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
+ eap->init_for_reauth = eap_aka_init_for_reauth;
+ eap->get_identity = eap_aka_get_identity;
+ eap->get_emsk = eap_aka_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+
+ return ret;
+}
+#endif /* EAP_AKA_PRIME */
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
new file mode 100644
index 0000000..b64b68f
--- /dev/null
+++ b/src/eap_peer/eap_config.h
@@ -0,0 +1,669 @@
+/*
+ * EAP peer configuration data
+ * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_CONFIG_H
+#define EAP_CONFIG_H
+
+/**
+ * struct eap_peer_config - EAP peer configuration/credentials
+ */
+struct eap_peer_config {
+ /**
+ * identity - EAP Identity
+ *
+ * This field is used to set the real user identity or NAI (for
+ * EAP-PSK/PAX/SAKE/GPSK).
+ */
+ u8 *identity;
+
+ /**
+ * identity_len - EAP Identity length
+ */
+ size_t identity_len;
+
+ /**
+ * anonymous_identity - Anonymous EAP Identity
+ *
+ * This field is used for unencrypted use with EAP types that support
+ * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the
+ * real identity (identity field) only to the authentication server.
+ *
+ * If not set, the identity field will be used for both unencrypted and
+ * protected fields.
+ */
+ u8 *anonymous_identity;
+
+ /**
+ * anonymous_identity_len - Length of anonymous_identity
+ */
+ size_t anonymous_identity_len;
+
+ /**
+ * password - Password string for EAP
+ *
+ * This field can include either the plaintext password (default
+ * option) or a NtPasswordHash (16-byte MD4 hash of the unicode
+ * presentation of the password) if flags field has
+ * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can
+ * only be used with authentication mechanism that use this hash as the
+ * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2,
+ * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
+ *
+ * In addition, this field is used to configure a pre-shared key for
+ * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK
+ * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length
+ * PSK.
+ */
+ u8 *password;
+
+ /**
+ * password_len - Length of password field
+ */
+ size_t password_len;
+
+ /**
+ * ca_cert - File path to CA certificate file (PEM/DER)
+ *
+ * This file can have one or more trusted CA certificates. If ca_cert
+ * and ca_path are not included, server certificate will not be
+ * verified. This is insecure and a trusted CA certificate should
+ * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ *
+ * Alternatively, this can be used to only perform matching of the
+ * server certificate (SHA-256 hash of the DER encoded X.509
+ * certificate). In this case, the possible CA certificates in the
+ * server certificate chain are ignored and only the server certificate
+ * is verified. This is configured with the following format:
+ * hash:://server/sha256/cert_hash_in_hex
+ * For example: "hash://server/sha256/
+ * 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a"
+ *
+ * On Windows, trusted CA certificates can be loaded from the system
+ * certificate store by setting this to cert_store://name, e.g.,
+ * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT".
+ * Note that when running wpa_supplicant as an application, the user
+ * certificate store (My user account) is used, whereas computer store
+ * (Computer account) is used when running wpasvc as a service.
+ */
+ u8 *ca_cert;
+
+ /**
+ * ca_path - Directory path for CA certificate files (PEM)
+ *
+ * This path may contain multiple CA certificates in OpenSSL format.
+ * Common use for this is to point to system trusted CA list which is
+ * often installed into directory like /etc/ssl/certs. If configured,
+ * these certificates are added to the list of trusted CAs. ca_cert
+ * may also be included in that case, but it is not required.
+ */
+ u8 *ca_path;
+
+ /**
+ * client_cert - File path to client certificate file (PEM/DER)
+ *
+ * This field is used with EAP method that use TLS authentication.
+ * Usually, this is only configured for EAP-TLS, even though this could
+ * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *client_cert;
+
+ /**
+ * private_key - File path to client private key file (PEM/DER/PFX)
+ *
+ * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+ * commented out. Both the private key and certificate will be read
+ * from the PKCS#12 file in this case. Full path to the file should be
+ * used since working directory may change when wpa_supplicant is run
+ * in the background.
+ *
+ * Windows certificate store can be used by leaving client_cert out and
+ * configuring private_key in one of the following formats:
+ *
+ * cert://substring_to_match
+ *
+ * hash://certificate_thumbprint_in_hex
+ *
+ * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+ *
+ * Note that when running wpa_supplicant as an application, the user
+ * certificate store (My user account) is used, whereas computer store
+ * (Computer account) is used when running wpasvc as a service.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *private_key;
+
+ /**
+ * private_key_passwd - Password for private key file
+ *
+ * If left out, this will be asked through control interface.
+ */
+ u8 *private_key_passwd;
+
+ /**
+ * dh_file - File path to DH/DSA parameters file (in PEM format)
+ *
+ * This is an optional configuration file for setting parameters for an
+ * ephemeral DH key exchange. In most cases, the default RSA
+ * authentication does not use this configuration. However, it is
+ * possible setup RSA to use ephemeral DH key exchange. In addition,
+ * ciphers with DSA keys always use ephemeral DH keys. This can be used
+ * to achieve forward secrecy. If the file is in DSA parameters format,
+ * it will be automatically converted into DH params. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *dh_file;
+
+ /**
+ * subject_match - Constraint for server certificate subject
+ *
+ * This substring is matched against the subject of the authentication
+ * server certificate. If this string is set, the server sertificate is
+ * only accepted if it contains this string in the subject. The subject
+ * string is in following format:
+ *
+ * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com
+ */
+ u8 *subject_match;
+
+ /**
+ * altsubject_match - Constraint for server certificate alt. subject
+ *
+ * Semicolon separated string of entries to be matched against the
+ * alternative subject name of the authentication server certificate.
+ * If this string is set, the server sertificate is only accepted if it
+ * contains one of the entries in an alternative subject name
+ * extension.
+ *
+ * altSubjectName string is in following format: TYPE:VALUE
+ *
+ * Example: EMAIL:server@example.com
+ * Example: DNS:server.example.com;DNS:server2.example.com
+ *
+ * Following types are supported: EMAIL, DNS, URI
+ */
+ u8 *altsubject_match;
+
+ /**
+ * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2)
+ *
+ * This file can have one or more trusted CA certificates. If ca_cert2
+ * and ca_path2 are not included, server certificate will not be
+ * verified. This is insecure and a trusted CA certificate should
+ * always be configured. Full path to the file should be used since
+ * working directory may change when wpa_supplicant is run in the
+ * background.
+ *
+ * This field is like ca_cert, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *ca_cert2;
+
+ /**
+ * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2)
+ *
+ * This path may contain multiple CA certificates in OpenSSL format.
+ * Common use for this is to point to system trusted CA list which is
+ * often installed into directory like /etc/ssl/certs. If configured,
+ * these certificates are added to the list of trusted CAs. ca_cert
+ * may also be included in that case, but it is not required.
+ *
+ * This field is like ca_path, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *ca_path2;
+
+ /**
+ * client_cert2 - File path to client certificate file
+ *
+ * This field is like client_cert, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *client_cert2;
+
+ /**
+ * private_key2 - File path to client private key file
+ *
+ * This field is like private_key, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *private_key2;
+
+ /**
+ * private_key2_passwd - Password for private key file
+ *
+ * This field is like private_key_passwd, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *private_key2_passwd;
+
+ /**
+ * dh_file2 - File path to DH/DSA parameters file (in PEM format)
+ *
+ * This field is like dh_file, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+ * file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ u8 *dh_file2;
+
+ /**
+ * subject_match2 - Constraint for server certificate subject
+ *
+ * This field is like subject_match, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *subject_match2;
+
+ /**
+ * altsubject_match2 - Constraint for server certificate alt. subject
+ *
+ * This field is like altsubject_match, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ u8 *altsubject_match2;
+
+ /**
+ * eap_methods - Allowed EAP methods
+ *
+ * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
+ * allowed EAP methods or %NULL if all methods are accepted.
+ */
+ struct eap_method_type *eap_methods;
+
+ /**
+ * phase1 - Phase 1 (outer authentication) parameters
+ *
+ * String with field-value pairs, e.g., "peapver=0" or
+ * "peapver=1 peaplabel=1".
+ *
+ * 'peapver' can be used to force which PEAP version (0 or 1) is used.
+ *
+ * 'peaplabel=1' can be used to force new label, "client PEAP
+ * encryption", to be used during key derivation when PEAPv1 or newer.
+ *
+ * Most existing PEAPv1 implementation seem to be using the old label,
+ * "client EAP encryption", and wpa_supplicant is now using that as the
+ * default value.
+ *
+ * Some servers, e.g., Radiator, may require peaplabel=1 configuration
+ * to interoperate with PEAPv1; see eap_testing.txt for more details.
+ *
+ * 'peap_outer_success=0' can be used to terminate PEAP authentication
+ * on tunneled EAP-Success. This is required with some RADIUS servers
+ * that implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g.,
+ * Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode).
+ *
+ * include_tls_length=1 can be used to force wpa_supplicant to include
+ * TLS Message Length field in all TLS messages even if they are not
+ * fragmented.
+ *
+ * sim_min_num_chal=3 can be used to configure EAP-SIM to require three
+ * challenges (by default, it accepts 2 or 3).
+ *
+ * result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use
+ * protected result indication.
+ *
+ * fast_provisioning option can be used to enable in-line provisioning
+ * of EAP-FAST credentials (PAC):
+ * 0 = disabled,
+ * 1 = allow unauthenticated provisioning,
+ * 2 = allow authenticated provisioning,
+ * 3 = allow both unauthenticated and authenticated provisioning
+ *
+ * fast_max_pac_list_len=num option can be used to set the maximum
+ * number of PAC entries to store in a PAC list (default: 10).
+ *
+ * fast_pac_format=binary option can be used to select binary format
+ * for storing PAC entries in order to save some space (the default
+ * text format uses about 2.5 times the size of minimal binary format).
+ *
+ * crypto_binding option can be used to control PEAPv0 cryptobinding
+ * behavior:
+ * 0 = do not use cryptobinding (default)
+ * 1 = use cryptobinding if server supports it
+ * 2 = require cryptobinding
+ *
+ * EAP-WSC (WPS) uses following options: pin=Device_Password and
+ * uuid=Device_UUID
+ */
+ char *phase1;
+
+ /**
+ * phase2 - Phase2 (inner authentication with TLS tunnel) parameters
+ *
+ * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
+ * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS.
+ */
+ char *phase2;
+
+ /**
+ * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM
+ *
+ * This field is used to configure PC/SC smartcard interface.
+ * Currently, the only configuration is whether this field is %NULL (do
+ * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC.
+ *
+ * This field is used for EAP-SIM and EAP-AKA.
+ */
+ char *pcsc;
+
+ /**
+ * pin - PIN for USIM, GSM SIM, and smartcards
+ *
+ * This field is used to configure PIN for SIM and smartcards for
+ * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
+ * smartcard is used for private key operations.
+ *
+ * If left out, this will be asked through control interface.
+ */
+ char *pin;
+
+ /**
+ * engine - Enable OpenSSL engine (e.g., for smartcard access)
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ int engine;
+
+ /**
+ * engine_id - Engine ID for OpenSSL engine
+ *
+ * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
+ * engine.
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *engine_id;
+
+ /**
+ * engine2 - Enable OpenSSL engine (e.g., for smartcard) (Phase 2)
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ *
+ * This field is like engine, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ int engine2;
+
+
+ /**
+ * pin2 - PIN for USIM, GSM SIM, and smartcards (Phase 2)
+ *
+ * This field is used to configure PIN for SIM and smartcards for
+ * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
+ * smartcard is used for private key operations.
+ *
+ * This field is like pin2, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ *
+ * If left out, this will be asked through control interface.
+ */
+ char *pin2;
+
+ /**
+ * engine2_id - Engine ID for OpenSSL engine (Phase 2)
+ *
+ * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
+ * engine.
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ *
+ * This field is like engine_id, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ char *engine2_id;
+
+
+ /**
+ * key_id - Key ID for OpenSSL engine
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *key_id;
+
+ /**
+ * cert_id - Cert ID for OpenSSL engine
+ *
+ * This is used if the certificate operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *cert_id;
+
+ /**
+ * ca_cert_id - CA Cert ID for OpenSSL engine
+ *
+ * This is used if the CA certificate for EAP-TLS is on a smartcard.
+ */
+ char *ca_cert_id;
+
+ /**
+ * key2_id - Key ID for OpenSSL engine (phase2)
+ *
+ * This is used if private key operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *key2_id;
+
+ /**
+ * cert2_id - Cert ID for OpenSSL engine (phase2)
+ *
+ * This is used if the certificate operations for EAP-TLS are performed
+ * using a smartcard.
+ */
+ char *cert2_id;
+
+ /**
+ * ca_cert2_id - CA Cert ID for OpenSSL engine (phase2)
+ *
+ * This is used if the CA certificate for EAP-TLS is on a smartcard.
+ */
+ char *ca_cert2_id;
+
+ /**
+ * otp - One-time-password
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when OTP is entered through the control interface.
+ */
+ u8 *otp;
+
+ /**
+ * otp_len - Length of the otp field
+ */
+ size_t otp_len;
+
+ /**
+ * pending_req_identity - Whether there is a pending identity request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_identity;
+
+ /**
+ * pending_req_password - Whether there is a pending password request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_password;
+
+ /**
+ * pending_req_pin - Whether there is a pending PIN request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_pin;
+
+ /**
+ * pending_req_new_password - Pending password update request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_new_password;
+
+ /**
+ * pending_req_passphrase - Pending passphrase request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ int pending_req_passphrase;
+
+ /**
+ * pending_req_otp - Whether there is a pending OTP request
+ *
+ * This field should not be set in configuration step. It is only used
+ * internally when control interface is used to request needed
+ * information.
+ */
+ char *pending_req_otp;
+
+ /**
+ * pending_req_otp_len - Length of the pending OTP request
+ */
+ size_t pending_req_otp_len;
+
+ /**
+ * pac_file - File path or blob name for the PAC entries (EAP-FAST)
+ *
+ * wpa_supplicant will need to be able to create this file and write
+ * updates to it when PAC is being provisioned or refreshed. Full path
+ * to the file should be used since working directory may change when
+ * wpa_supplicant is run in the background.
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ char *pac_file;
+
+ /**
+ * mschapv2_retry - MSCHAPv2 retry in progress
+ *
+ * This field is used internally by EAP-MSCHAPv2 and should not be set
+ * as part of configuration.
+ */
+ int mschapv2_retry;
+
+ /**
+ * new_password - New password for password update
+ *
+ * This field is used during MSCHAPv2 password update. This is normally
+ * requested from the user through the control interface and not set
+ * from configuration.
+ */
+ u8 *new_password;
+
+ /**
+ * new_password_len - Length of new_password field
+ */
+ size_t new_password_len;
+
+ /**
+ * fragment_size - Maximum EAP fragment size in bytes (default 1398)
+ *
+ * This value limits the fragment size for EAP methods that support
+ * fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set
+ * small enough to make the EAP messages fit in MTU of the network
+ * interface used for EAPOL. The default value is suitable for most
+ * cases.
+ */
+ int fragment_size;
+
+#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0)
+ /**
+ * flags - Network configuration flags (bitfield)
+ *
+ * This variable is used for internal flags to describe further details
+ * for the network parameters.
+ * bit 0 = password is represented as a 16-byte NtPasswordHash value
+ * instead of plaintext password
+ */
+ u32 flags;
+};
+
+
+/**
+ * struct wpa_config_blob - Named configuration blob
+ *
+ * This data structure is used to provide storage for binary objects to store
+ * abstract information like certificates and private keys inlined with the
+ * configuration data.
+ */
+struct wpa_config_blob {
+ /**
+ * name - Blob name
+ */
+ char *name;
+
+ /**
+ * data - Pointer to binary data
+ */
+ u8 *data;
+
+ /**
+ * len - Length of binary data
+ */
+ size_t len;
+
+ /**
+ * next - Pointer to next blob in the configuration
+ */
+ struct wpa_config_blob *next;
+};
+
+#endif /* EAP_CONFIG_H */
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
new file mode 100644
index 0000000..5d3e69d
--- /dev/null
+++ b/src/eap_peer/eap_fast.c
@@ -0,0 +1,1712 @@
+/*
+ * EAP peer method: EAP-FAST (RFC 4851)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/tls.h"
+#include "crypto/sha1.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "eap_fast_pac.h"
+
+#ifdef EAP_FAST_DYNAMIC
+#include "eap_fast_pac.c"
+#endif /* EAP_FAST_DYNAMIC */
+
+/* TODO:
+ * - test session resumption and enable it if it interoperates
+ * - password change (pending mschapv2 packet; replay decrypted packet)
+ */
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_fast_data {
+ struct eap_ssl_data ssl;
+
+ int fast_version;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+
+ struct eap_method_type phase2_type;
+ struct eap_method_type *phase2_types;
+ size_t num_phase2_types;
+ int resuming; /* starting a resumed session */
+ struct eap_fast_key_block_provisioning *key_block_p;
+#define EAP_FAST_PROV_UNAUTH 1
+#define EAP_FAST_PROV_AUTH 2
+ int provisioning_allowed; /* Allowed PAC provisioning modes */
+ int provisioning; /* doing PAC provisioning (not the normal auth) */
+ int anon_provisioning; /* doing anonymous (unauthenticated)
+ * provisioning */
+ int session_ticket_used;
+
+ u8 key_data[EAP_FAST_KEY_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+ int success;
+
+ struct eap_fast_pac *pac;
+ struct eap_fast_pac *current_pac;
+ size_t max_pac_list_len;
+ int use_pac_binary_format;
+
+ u8 simck[EAP_FAST_SIMCK_LEN];
+ int simck_idx;
+
+ struct wpabuf *pending_phase2_req;
+};
+
+
+static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
+ const u8 *client_random,
+ const u8 *server_random,
+ u8 *master_secret)
+{
+ struct eap_fast_data *data = ctx;
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback");
+
+ if (client_random == NULL || server_random == NULL ||
+ master_secret == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket failed - fall "
+ "back to full TLS handshake");
+ data->session_ticket_used = 0;
+ if (data->provisioning_allowed) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Try to provision a "
+ "new PAC-Key");
+ data->provisioning = 1;
+ data->current_pac = NULL;
+ }
+ return 0;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket", ticket, len);
+
+ if (data->current_pac == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key available for "
+ "using SessionTicket");
+ data->session_ticket_used = 0;
+ return 0;
+ }
+
+ eap_fast_derive_master_secret(data->current_pac->pac_key,
+ server_random, client_random,
+ master_secret);
+
+ data->session_ticket_used = 1;
+
+ return 1;
+}
+
+
+static int eap_fast_parse_phase1(struct eap_fast_data *data,
+ const char *phase1)
+{
+ const char *pos;
+
+ pos = os_strstr(phase1, "fast_provisioning=");
+ if (pos) {
+ data->provisioning_allowed = atoi(pos + 18);
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC provisioning "
+ "mode: %d", data->provisioning_allowed);
+ }
+
+ pos = os_strstr(phase1, "fast_max_pac_list_len=");
+ if (pos) {
+ data->max_pac_list_len = atoi(pos + 22);
+ if (data->max_pac_list_len == 0)
+ data->max_pac_list_len = 1;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Maximum PAC list length: %lu",
+ (unsigned long) data->max_pac_list_len);
+ }
+
+ pos = os_strstr(phase1, "fast_pac_format=binary");
+ if (pos) {
+ data->use_pac_binary_format = 1;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Using binary format for PAC "
+ "list");
+ }
+
+ return 0;
+}
+
+
+static void * eap_fast_init(struct eap_sm *sm)
+{
+ struct eap_fast_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->fast_version = EAP_FAST_VERSION;
+ data->max_pac_list_len = 10;
+
+ if (config && config->phase1 &&
+ eap_fast_parse_phase1(data, config->phase1) < 0) {
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ if (eap_peer_select_phase2_methods(config, "auth=",
+ &data->phase2_types,
+ &data->num_phase2_types) < 0) {
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ data->phase2_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_type.method = EAP_TYPE_NONE;
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
+ eap_fast_session_ticket_cb,
+ data) < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket "
+ "callback");
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ /*
+ * The local RADIUS server in a Cisco AP does not seem to like empty
+ * fragments before data, so disable that workaround for CBC.
+ * TODO: consider making this configurable
+ */
+ if (tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to enable TLS "
+ "workarounds");
+ }
+
+ if (data->use_pac_binary_format &&
+ eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) {
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ if (!data->use_pac_binary_format &&
+ eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) {
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+ eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
+
+ if (data->pac == NULL && !data->provisioning_allowed) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and "
+ "provisioning disabled");
+ eap_fast_deinit(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ struct eap_fast_pac *pac, *prev;
+
+ if (data == NULL)
+ return;
+ if (data->phase2_priv && data->phase2_method)
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ os_free(data->phase2_types);
+ os_free(data->key_block_p);
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
+
+ pac = data->pac;
+ prev = NULL;
+ while (pac) {
+ prev = pac;
+ pac = pac->next;
+ eap_fast_free_pac(prev);
+ }
+ wpabuf_free(data->pending_phase2_req);
+ os_free(data);
+}
+
+
+static int eap_fast_derive_msk(struct eap_fast_data *data)
+{
+ eap_fast_derive_eap_msk(data->simck, data->key_data);
+ eap_fast_derive_eap_emsk(data->simck, data->emsk);
+ data->success = 1;
+ return 0;
+}
+
+
+static void eap_fast_derive_key_auth(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ u8 *sks;
+
+ /* RFC 4851, Section 5.1:
+ * Extra key material after TLS key_block: session_key_seed[40]
+ */
+
+ sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion",
+ EAP_FAST_SKS_LEN);
+ if (sks == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
+ "session_key_seed");
+ return;
+ }
+
+ /*
+ * RFC 4851, Section 5.2:
+ * S-IMCK[0] = session_key_seed
+ */
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+ sks, EAP_FAST_SKS_LEN);
+ data->simck_idx = 0;
+ os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN);
+ os_free(sks);
+}
+
+
+static void eap_fast_derive_key_provisioning(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ os_free(data->key_block_p);
+ data->key_block_p = (struct eap_fast_key_block_provisioning *)
+ eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
+ "key expansion",
+ sizeof(*data->key_block_p));
+ if (data->key_block_p == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
+ return;
+ }
+ /*
+ * RFC 4851, Section 5.2:
+ * S-IMCK[0] = session_key_seed
+ */
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+ data->key_block_p->session_key_seed,
+ sizeof(data->key_block_p->session_key_seed));
+ data->simck_idx = 0;
+ os_memcpy(data->simck, data->key_block_p->session_key_seed,
+ EAP_FAST_SIMCK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge",
+ data->key_block_p->server_challenge,
+ sizeof(data->key_block_p->server_challenge));
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge",
+ data->key_block_p->client_challenge,
+ sizeof(data->key_block_p->client_challenge));
+}
+
+
+static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data)
+{
+ if (data->anon_provisioning)
+ eap_fast_derive_key_provisioning(sm, data);
+ else
+ eap_fast_derive_key_auth(sm, data);
+}
+
+
+static int eap_fast_init_phase2_method(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ data->phase2_method =
+ eap_peer_get_eap_method(data->phase2_type.vendor,
+ data->phase2_type.method);
+ if (data->phase2_method == NULL)
+ return -1;
+
+ if (data->key_block_p) {
+ sm->auth_challenge = data->key_block_p->server_challenge;
+ sm->peer_challenge = data->key_block_p->client_challenge;
+ }
+ sm->init_phase2 = 1;
+ data->phase2_priv = data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ sm->auth_challenge = NULL;
+ sm->peer_challenge = NULL;
+
+ return data->phase2_priv == NULL ? -1 : 0;
+}
+
+
+static int eap_fast_select_phase2_method(struct eap_fast_data *data, u8 type)
+{
+ size_t i;
+
+ /* TODO: TNC with anonymous provisioning; need to require both
+ * completed MSCHAPv2 and TNC */
+
+ if (data->anon_provisioning && type != EAP_TYPE_MSCHAPV2) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Only EAP-MSCHAPv2 is allowed "
+ "during unauthenticated provisioning; reject phase2"
+ " type %d", type);
+ return -1;
+ }
+
+#ifdef EAP_TNC
+ if (type == EAP_TYPE_TNC) {
+ data->phase2_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_type.method = EAP_TYPE_TNC;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP "
+ "vendor %d method %d for TNC",
+ data->phase2_type.vendor,
+ data->phase2_type.method);
+ return 0;
+ }
+#endif /* EAP_TNC */
+
+ for (i = 0; i < data->num_phase2_types; i++) {
+ if (data->phase2_types[i].vendor != EAP_VENDOR_IETF ||
+ data->phase2_types[i].method != type)
+ continue;
+
+ data->phase2_type.vendor = data->phase2_types[i].vendor;
+ data->phase2_type.method = data->phase2_types[i].method;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP "
+ "vendor %d method %d",
+ data->phase2_type.vendor,
+ data->phase2_type.method);
+ break;
+ }
+
+ if (type != data->phase2_type.method || type == EAP_TYPE_NONE)
+ return -1;
+
+ return 0;
+}
+
+
+static int eap_fast_phase2_request(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr,
+ struct wpabuf **resp)
+{
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_method_ret iret;
+ struct eap_peer_config *config = eap_get_config(sm);
+ struct wpabuf msg;
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos);
+ if (*pos == EAP_TYPE_IDENTITY) {
+ *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+ return 0;
+ }
+
+ if (data->phase2_priv && data->phase2_method &&
+ *pos != data->phase2_type.method) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 EAP sequence - "
+ "deinitialize previous method");
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ data->phase2_method = NULL;
+ data->phase2_priv = NULL;
+ data->phase2_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_type.method = EAP_TYPE_NONE;
+ }
+
+ if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+ data->phase2_type.method == EAP_TYPE_NONE &&
+ eap_fast_select_phase2_method(data, *pos) < 0) {
+ if (eap_peer_tls_phase2_nak(data->phase2_types,
+ data->num_phase2_types,
+ hdr, resp))
+ return -1;
+ return 0;
+ }
+
+ if (data->phase2_priv == NULL &&
+ eap_fast_init_phase2_method(sm, data) < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize "
+ "Phase 2 EAP method %d", *pos);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+
+ os_memset(&iret, 0, sizeof(iret));
+ wpabuf_set(&msg, hdr, len);
+ *resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+ &msg);
+ if (*resp == NULL ||
+ (iret.methodState == METHOD_DONE &&
+ iret.decision == DECISION_FAIL)) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ } else if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC)) {
+ data->phase2_success = 1;
+ }
+
+ if (*resp == NULL && config &&
+ (config->pending_req_identity || config->pending_req_password ||
+ config->pending_req_otp || config->pending_req_new_password)) {
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
+ } else if (*resp == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_fast_tlv_nak(int vendor_id, int tlv_type)
+{
+ struct wpabuf *buf;
+ struct eap_tlv_nak_tlv *nak;
+ buf = wpabuf_alloc(sizeof(*nak));
+ if (buf == NULL)
+ return NULL;
+ nak = wpabuf_put(buf, sizeof(*nak));
+ nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV);
+ nak->length = host_to_be16(6);
+ nak->vendor_id = host_to_be32(vendor_id);
+ nak->nak_type = host_to_be16(tlv_type);
+ return buf;
+}
+
+
+static struct wpabuf * eap_fast_tlv_result(int status, int intermediate)
+{
+ struct wpabuf *buf;
+ struct eap_tlv_intermediate_result_tlv *result;
+ buf = wpabuf_alloc(sizeof(*result));
+ if (buf == NULL)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Add %sResult TLV(status=%d)",
+ intermediate ? "Intermediate " : "", status);
+ result = wpabuf_put(buf, sizeof(*result));
+ result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ (intermediate ?
+ EAP_TLV_INTERMEDIATE_RESULT_TLV :
+ EAP_TLV_RESULT_TLV));
+ result->length = host_to_be16(2);
+ result->status = host_to_be16(status);
+ return buf;
+}
+
+
+static struct wpabuf * eap_fast_tlv_pac_ack(void)
+{
+ struct wpabuf *buf;
+ struct eap_tlv_result_tlv *res;
+ struct eap_tlv_pac_ack_tlv *ack;
+
+ buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack));
+ if (buf == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV (ack)");
+ ack = wpabuf_put(buf, sizeof(*ack));
+ ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV |
+ EAP_TLV_TYPE_MANDATORY);
+ ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr));
+ ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT);
+ ack->pac_len = host_to_be16(2);
+ ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+ return buf;
+}
+
+
+static struct wpabuf * eap_fast_process_eap_payload_tlv(
+ struct eap_sm *sm, struct eap_fast_data *data,
+ struct eap_method_ret *ret, const struct eap_hdr *req,
+ u8 *eap_payload_tlv, size_t eap_payload_tlv_len)
+{
+ struct eap_hdr *hdr;
+ struct wpabuf *resp = NULL;
+
+ if (eap_payload_tlv_len < sizeof(*hdr)) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP "
+ "Payload TLV (len=%lu)",
+ (unsigned long) eap_payload_tlv_len);
+ return NULL;
+ }
+
+ hdr = (struct eap_hdr *) eap_payload_tlv;
+ if (be_to_host16(hdr->length) > eap_payload_tlv_len) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow in "
+ "EAP Payload TLV");
+ return NULL;
+ }
+
+ if (hdr->code != EAP_CODE_REQUEST) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ return NULL;
+ }
+
+ if (eap_fast_phase2_request(sm, data, ret, hdr, &resp)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Phase2 Request processing "
+ "failed");
+ return NULL;
+ }
+
+ return eap_fast_tlv_eap_payload(resp);
+}
+
+
+static int eap_fast_validate_crypto_binding(
+ struct eap_tlv_crypto_binding_tlv *_bind)
+{
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d "
+ "Received Version %d SubType %d",
+ _bind->version, _bind->received_version, _bind->subtype);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+ _bind->nonce, sizeof(_bind->nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+ _bind->compound_mac, sizeof(_bind->compound_mac));
+
+ if (_bind->version != EAP_FAST_VERSION ||
+ _bind->received_version != EAP_FAST_VERSION ||
+ _bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in "
+ "Crypto-Binding TLV: Version %d "
+ "Received Version %d SubType %d",
+ _bind->version, _bind->received_version,
+ _bind->subtype);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void eap_fast_write_crypto_binding(
+ struct eap_tlv_crypto_binding_tlv *rbind,
+ struct eap_tlv_crypto_binding_tlv *_bind, const u8 *cmk)
+{
+ rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ EAP_TLV_CRYPTO_BINDING_TLV);
+ rbind->length = host_to_be16(sizeof(*rbind) -
+ sizeof(struct eap_tlv_hdr));
+ rbind->version = EAP_FAST_VERSION;
+ rbind->received_version = _bind->version;
+ rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE;
+ os_memcpy(rbind->nonce, _bind->nonce, sizeof(_bind->nonce));
+ inc_byte_array(rbind->nonce, sizeof(rbind->nonce));
+ hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) rbind, sizeof(*rbind),
+ rbind->compound_mac);
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d "
+ "Received Version %d SubType %d",
+ rbind->version, rbind->received_version, rbind->subtype);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+ rbind->nonce, sizeof(rbind->nonce));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+ rbind->compound_mac, sizeof(rbind->compound_mac));
+}
+
+
+static int eap_fast_get_phase2_key(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ u8 *isk, size_t isk_len)
+{
+ u8 *key;
+ size_t key_len;
+
+ os_memset(isk, 0, isk_len);
+
+ if (data->phase2_method == NULL || data->phase2_priv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
+ "available");
+ return -1;
+ }
+
+ if (data->phase2_method->isKeyAvailable == NULL ||
+ data->phase2_method->getKey == NULL)
+ return 0;
+
+ if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
+ (key = data->phase2_method->getKey(sm, data->phase2_priv,
+ &key_len)) == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material "
+ "from Phase 2");
+ return -1;
+ }
+
+ if (key_len > isk_len)
+ key_len = isk_len;
+ if (key_len == 32 &&
+ data->phase2_method->vendor == EAP_VENDOR_IETF &&
+ data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
+ /*
+ * EAP-FAST uses reverse order for MS-MPPE keys when deriving
+ * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
+ * ISK for EAP-FAST cryptobinding.
+ */
+ os_memcpy(isk, key + 16, 16);
+ os_memcpy(isk + 16, key, 16);
+ } else
+ os_memcpy(isk, key, key_len);
+ os_free(key);
+
+ return 0;
+}
+
+
+static int eap_fast_get_cmk(struct eap_sm *sm, struct eap_fast_data *data,
+ u8 *cmk)
+{
+ u8 isk[32], imck[60];
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK[%d] for Compound MIC "
+ "calculation", data->simck_idx + 1);
+
+ /*
+ * RFC 4851, Section 5.2:
+ * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys",
+ * MSK[j], 60)
+ * S-IMCK[j] = first 40 octets of IMCK[j]
+ * CMK[j] = last 20 octets of IMCK[j]
+ */
+
+ if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk));
+ sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+ "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck));
+ data->simck_idx++;
+ os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]",
+ data->simck, EAP_FAST_SIMCK_LEN);
+ os_memcpy(cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]",
+ cmk, EAP_FAST_CMK_LEN);
+
+ return 0;
+}
+
+
+static u8 * eap_fast_write_pac_request(u8 *pos, u16 pac_type)
+{
+ struct eap_tlv_hdr *pac;
+ struct eap_tlv_request_action_tlv *act;
+ struct eap_tlv_pac_type_tlv *type;
+
+ act = (struct eap_tlv_request_action_tlv *) pos;
+ act->tlv_type = host_to_be16(EAP_TLV_REQUEST_ACTION_TLV);
+ act->length = host_to_be16(2);
+ act->action = host_to_be16(EAP_TLV_ACTION_PROCESS_TLV);
+
+ pac = (struct eap_tlv_hdr *) (act + 1);
+ pac->tlv_type = host_to_be16(EAP_TLV_PAC_TLV);
+ pac->length = host_to_be16(sizeof(*type));
+
+ type = (struct eap_tlv_pac_type_tlv *) (pac + 1);
+ type->tlv_type = host_to_be16(PAC_TYPE_PAC_TYPE);
+ type->length = host_to_be16(2);
+ type->pac_type = host_to_be16(pac_type);
+
+ return (u8 *) (type + 1);
+}
+
+
+static struct wpabuf * eap_fast_process_crypto_binding(
+ struct eap_sm *sm, struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ struct eap_tlv_crypto_binding_tlv *_bind, size_t bind_len)
+{
+ struct wpabuf *resp;
+ u8 *pos;
+ u8 cmk[EAP_FAST_CMK_LEN], cmac[SHA1_MAC_LEN];
+ int res;
+ size_t len;
+
+ if (eap_fast_validate_crypto_binding(_bind) < 0)
+ return NULL;
+
+ if (eap_fast_get_cmk(sm, data, cmk) < 0)
+ return NULL;
+
+ /* Validate received Compound MAC */
+ os_memcpy(cmac, _bind->compound_mac, sizeof(cmac));
+ os_memset(_bind->compound_mac, 0, sizeof(cmac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound "
+ "MAC calculation", (u8 *) _bind, bind_len);
+ hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) _bind, bind_len,
+ _bind->compound_mac);
+ res = os_memcmp(cmac, _bind->compound_mac, sizeof(cmac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC",
+ cmac, sizeof(cmac));
+ wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC",
+ _bind->compound_mac, sizeof(cmac));
+ if (res != 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match");
+ os_memcpy(_bind->compound_mac, cmac, sizeof(cmac));
+ return NULL;
+ }
+
+ /*
+ * Compound MAC was valid, so authentication succeeded. Reply with
+ * crypto binding to allow server to complete authentication.
+ */
+
+ len = sizeof(struct eap_tlv_crypto_binding_tlv);
+ resp = wpabuf_alloc(len);
+ if (resp == NULL)
+ return NULL;
+
+ if (!data->anon_provisioning && data->phase2_success &&
+ eap_fast_derive_msk(data) < 0) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ data->phase2_success = 0;
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv));
+ eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *)
+ pos, _bind, cmk);
+
+ return resp;
+}
+
+
+static void eap_fast_parse_pac_tlv(struct eap_fast_pac *entry, int type,
+ u8 *pos, size_t len, int *pac_key_found)
+{
+ switch (type & 0x7fff) {
+ case PAC_TYPE_PAC_KEY:
+ wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key", pos, len);
+ if (len != EAP_FAST_PAC_KEY_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid PAC-Key "
+ "length %lu", (unsigned long) len);
+ break;
+ }
+ *pac_key_found = 1;
+ os_memcpy(entry->pac_key, pos, len);
+ break;
+ case PAC_TYPE_PAC_OPAQUE:
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", pos, len);
+ entry->pac_opaque = pos;
+ entry->pac_opaque_len = len;
+ break;
+ case PAC_TYPE_PAC_INFO:
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info", pos, len);
+ entry->pac_info = pos;
+ entry->pac_info_len = len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC type %d",
+ type);
+ break;
+ }
+}
+
+
+static int eap_fast_process_pac_tlv(struct eap_fast_pac *entry,
+ u8 *pac, size_t pac_len)
+{
+ struct pac_tlv_hdr *hdr;
+ u8 *pos;
+ size_t left, len;
+ int type, pac_key_found = 0;
+
+ pos = pac;
+ left = pac_len;
+
+ while (left > sizeof(*hdr)) {
+ hdr = (struct pac_tlv_hdr *) pos;
+ type = be_to_host16(hdr->type);
+ len = be_to_host16(hdr->len);
+ pos += sizeof(*hdr);
+ left -= sizeof(*hdr);
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun "
+ "(type=%d len=%lu left=%lu)",
+ type, (unsigned long) len,
+ (unsigned long) left);
+ return -1;
+ }
+
+ eap_fast_parse_pac_tlv(entry, type, pos, len, &pac_key_found);
+
+ pos += len;
+ left -= len;
+ }
+
+ if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include "
+ "all the required fields");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_fast_parse_pac_info(struct eap_fast_pac *entry, int type,
+ u8 *pos, size_t len)
+{
+ u16 pac_type;
+ u32 lifetime;
+ struct os_time now;
+
+ switch (type & 0x7fff) {
+ case PAC_TYPE_CRED_LIFETIME:
+ if (len != 4) {
+ wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info - "
+ "Invalid CRED_LIFETIME length - ignored",
+ pos, len);
+ return 0;
+ }
+
+ /*
+ * This is not currently saved separately in PAC files since
+ * the server can automatically initiate PAC update when
+ * needed. Anyway, the information is available from PAC-Info
+ * dump if it is needed for something in the future.
+ */
+ lifetime = WPA_GET_BE32(pos);
+ os_get_time(&now);
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - CRED_LIFETIME %d "
+ "(%d days)",
+ lifetime, (lifetime - (u32) now.sec) / 86400);
+ break;
+ case PAC_TYPE_A_ID:
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID",
+ pos, len);
+ entry->a_id = pos;
+ entry->a_id_len = len;
+ break;
+ case PAC_TYPE_I_ID:
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - I-ID",
+ pos, len);
+ entry->i_id = pos;
+ entry->i_id_len = len;
+ break;
+ case PAC_TYPE_A_ID_INFO:
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID-Info",
+ pos, len);
+ entry->a_id_info = pos;
+ entry->a_id_info_len = len;
+ break;
+ case PAC_TYPE_PAC_TYPE:
+ /* RFC 5422, Section 4.2.6 - PAC-Type TLV */
+ if (len != 2) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC-Type "
+ "length %lu (expected 2)",
+ (unsigned long) len);
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-FAST: PAC-Info - PAC-Type",
+ pos, len);
+ return -1;
+ }
+ pac_type = WPA_GET_BE16(pos);
+ if (pac_type != PAC_TYPE_TUNNEL_PAC &&
+ pac_type != PAC_TYPE_USER_AUTHORIZATION &&
+ pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Unsupported PAC Type "
+ "%d", pac_type);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - PAC-Type %d",
+ pac_type);
+ entry->pac_type = pac_type;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC-Info "
+ "type %d", type);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int eap_fast_process_pac_info(struct eap_fast_pac *entry)
+{
+ struct pac_tlv_hdr *hdr;
+ u8 *pos;
+ size_t left, len;
+ int type;
+
+ /* RFC 5422, Section 4.2.4 */
+
+ /* PAC-Type defaults to Tunnel PAC (Type 1) */
+ entry->pac_type = PAC_TYPE_TUNNEL_PAC;
+
+ pos = entry->pac_info;
+ left = entry->pac_info_len;
+ while (left > sizeof(*hdr)) {
+ hdr = (struct pac_tlv_hdr *) pos;
+ type = be_to_host16(hdr->type);
+ len = be_to_host16(hdr->len);
+ pos += sizeof(*hdr);
+ left -= sizeof(*hdr);
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun "
+ "(type=%d len=%lu left=%lu)",
+ type, (unsigned long) len,
+ (unsigned long) left);
+ return -1;
+ }
+
+ if (eap_fast_parse_pac_info(entry, type, pos, len) < 0)
+ return -1;
+
+ pos += len;
+ left -= len;
+ }
+
+ if (entry->a_id == NULL || entry->a_id_info == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include "
+ "all the required fields");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ u8 *pac, size_t pac_len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ struct eap_fast_pac entry;
+
+ os_memset(&entry, 0, sizeof(entry));
+ if (eap_fast_process_pac_tlv(&entry, pac, pac_len) ||
+ eap_fast_process_pac_info(&entry))
+ return NULL;
+
+ eap_fast_add_pac(&data->pac, &data->current_pac, &entry);
+ eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
+ if (data->use_pac_binary_format)
+ eap_fast_save_pac_bin(sm, data->pac, config->pac_file);
+ else
+ eap_fast_save_pac(sm, data->pac, config->pac_file);
+
+ if (data->provisioning) {
+ if (data->anon_provisioning) {
+ /*
+ * Unauthenticated provisioning does not provide keying
+ * material and must end with an EAP-Failure.
+ * Authentication will be done separately after this.
+ */
+ data->success = 0;
+ ret->decision = DECISION_FAIL;
+ } else {
+ /*
+ * Server may or may not allow authenticated
+ * provisioning also for key generation.
+ */
+ ret->decision = DECISION_COND_SUCC;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+ "- Provisioning completed successfully");
+ } else {
+ /*
+ * This is PAC refreshing, i.e., normal authentication that is
+ * expected to be completed with an EAP-Success.
+ */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+ "- PAC refreshing completed successfully");
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+ ret->methodState = METHOD_DONE;
+ return eap_fast_tlv_pac_ack();
+}
+
+
+static int eap_fast_parse_decrypted(struct wpabuf *decrypted,
+ struct eap_fast_tlv_parse *tlv,
+ struct wpabuf **resp)
+{
+ int mandatory, tlv_type, len, res;
+ u8 *pos, *end;
+
+ os_memset(tlv, 0, sizeof(*tlv));
+
+ /* Parse TLVs from the decrypted Phase 2 data */
+ pos = wpabuf_mhead(decrypted);
+ end = pos + wpabuf_len(decrypted);
+ while (pos + 4 < end) {
+ mandatory = pos[0] & 0x80;
+ tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+ pos += 2;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (pos + len > end) {
+ wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
+ "TLV type %d length %d%s",
+ tlv_type, len, mandatory ? " (mandatory)" : "");
+
+ res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
+ if (res == -2)
+ break;
+ if (res < 0) {
+ if (mandatory) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown "
+ "mandatory TLV type %d", tlv_type);
+ *resp = eap_fast_tlv_nak(0, tlv_type);
+ break;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: ignored "
+ "unknown optional TLV type %d",
+ tlv_type);
+ }
+ }
+
+ pos += len;
+ }
+
+ return 0;
+}
+
+
+static int eap_fast_encrypt_response(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct wpabuf *resp,
+ u8 identifier, struct wpabuf **out_data)
+{
+ if (resp == NULL)
+ return 0;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data",
+ resp);
+ if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
+ data->fast_version, identifier,
+ resp, out_data)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 "
+ "frame");
+ }
+ wpabuf_free(resp);
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_fast_pac_request(void)
+{
+ struct wpabuf *tmp;
+ u8 *pos, *pos2;
+
+ tmp = wpabuf_alloc(sizeof(struct eap_tlv_hdr) +
+ sizeof(struct eap_tlv_request_action_tlv) +
+ sizeof(struct eap_tlv_pac_type_tlv));
+ if (tmp == NULL)
+ return NULL;
+
+ pos = wpabuf_put(tmp, 0);
+ pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC);
+ wpabuf_put(tmp, pos2 - pos);
+ return tmp;
+}
+
+
+static int eap_fast_process_decrypted(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_hdr *req,
+ struct wpabuf *decrypted,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *resp = NULL, *tmp;
+ struct eap_fast_tlv_parse tlv;
+ int failed = 0;
+
+ if (eap_fast_parse_decrypted(decrypted, &tlv, &resp) < 0)
+ return 0;
+ if (resp)
+ return eap_fast_encrypt_response(sm, data, resp,
+ req->identifier, out_data);
+
+ if (tlv.result == EAP_TLV_RESULT_FAILURE) {
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+ return eap_fast_encrypt_response(sm, data, resp,
+ req->identifier, out_data);
+ }
+
+ if (tlv.iresult == EAP_TLV_RESULT_FAILURE) {
+ resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1);
+ return eap_fast_encrypt_response(sm, data, resp,
+ req->identifier, out_data);
+ }
+
+ if (tlv.crypto_binding) {
+ tmp = eap_fast_process_crypto_binding(sm, data, ret,
+ tlv.crypto_binding,
+ tlv.crypto_binding_len);
+ if (tmp == NULL)
+ failed = 1;
+ else
+ resp = wpabuf_concat(resp, tmp);
+ }
+
+ if (tlv.iresult == EAP_TLV_RESULT_SUCCESS) {
+ tmp = eap_fast_tlv_result(failed ? EAP_TLV_RESULT_FAILURE :
+ EAP_TLV_RESULT_SUCCESS, 1);
+ resp = wpabuf_concat(resp, tmp);
+ }
+
+ if (tlv.eap_payload_tlv) {
+ tmp = eap_fast_process_eap_payload_tlv(
+ sm, data, ret, req, tlv.eap_payload_tlv,
+ tlv.eap_payload_tlv_len);
+ resp = wpabuf_concat(resp, tmp);
+ }
+
+ if (tlv.pac && tlv.result != EAP_TLV_RESULT_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV "
+ "acknowledging success");
+ failed = 1;
+ } else if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) {
+ tmp = eap_fast_process_pac(sm, data, ret, tlv.pac,
+ tlv.pac_len);
+ resp = wpabuf_concat(resp, tmp);
+ }
+
+ if (data->current_pac == NULL && data->provisioning &&
+ !data->anon_provisioning && !tlv.pac &&
+ (tlv.iresult == EAP_TLV_RESULT_SUCCESS ||
+ tlv.result == EAP_TLV_RESULT_SUCCESS)) {
+ /*
+ * Need to request Tunnel PAC when using authenticated
+ * provisioning.
+ */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC");
+ tmp = eap_fast_pac_request();
+ resp = wpabuf_concat(resp, tmp);
+ }
+
+ if (tlv.result == EAP_TLV_RESULT_SUCCESS && !failed) {
+ tmp = eap_fast_tlv_result(EAP_TLV_RESULT_SUCCESS, 0);
+ resp = wpabuf_concat(tmp, resp);
+ } else if (failed) {
+ tmp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+ resp = wpabuf_concat(tmp, resp);
+ }
+
+ if (resp && tlv.result == EAP_TLV_RESULT_SUCCESS && !failed &&
+ tlv.crypto_binding && data->phase2_success) {
+ if (data->anon_provisioning) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated "
+ "provisioning completed successfully.");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
+ "completed successfully.");
+ if (data->provisioning)
+ ret->methodState = METHOD_MAY_CONT;
+ else
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+ }
+
+ if (resp == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send "
+ "empty response packet");
+ resp = wpabuf_alloc(1);
+ }
+
+ return eap_fast_encrypt_response(sm, data, resp, req->identifier,
+ out_data);
+}
+
+
+static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_hdr *req,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *in_decrypted;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for"
+ " Phase 2", (unsigned long) wpabuf_len(in_data));
+
+ if (data->pending_phase2_req) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Pending Phase 2 request - "
+ "skip decryption and use old data");
+ /* Clear TLS reassembly state. */
+ eap_peer_tls_reset_input(&data->ssl);
+
+ in_decrypted = data->pending_phase2_req;
+ data->pending_phase2_req = NULL;
+ goto continue_req;
+ }
+
+ if (wpabuf_len(in_data) == 0) {
+ /* Received TLS ACK - requesting more fragments */
+ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
+ data->fast_version,
+ req->identifier, NULL, out_data);
+ }
+
+ res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+ if (res)
+ return res;
+
+continue_req:
+ wpa_hexdump_buf(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)",
+ in_decrypted);
+
+ if (wpabuf_len(in_decrypted) < 4) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
+ "TLV frame (len=%lu)",
+ (unsigned long) wpabuf_len(in_decrypted));
+ wpabuf_free(in_decrypted);
+ return -1;
+ }
+
+ res = eap_fast_process_decrypted(sm, data, ret, req,
+ in_decrypted, out_data);
+
+ wpabuf_free(in_decrypted);
+
+ return res;
+}
+
+
+static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len)
+{
+ const u8 *a_id;
+ struct pac_tlv_hdr *hdr;
+
+ /*
+ * Parse authority identity (A-ID) from the EAP-FAST/Start. This
+ * supports both raw A-ID and one inside an A-ID TLV.
+ */
+ a_id = buf;
+ *id_len = len;
+ if (len > sizeof(*hdr)) {
+ int tlen;
+ hdr = (struct pac_tlv_hdr *) buf;
+ tlen = be_to_host16(hdr->len);
+ if (be_to_host16(hdr->type) == PAC_TYPE_A_ID &&
+ sizeof(*hdr) + tlen <= len) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV "
+ "(Start)");
+ a_id = (u8 *) (hdr + 1);
+ *id_len = tlen;
+ }
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, *id_len);
+
+ return a_id;
+}
+
+
+static void eap_fast_select_pac(struct eap_fast_data *data,
+ const u8 *a_id, size_t a_id_len)
+{
+ data->current_pac = eap_fast_get_pac(data->pac, a_id, a_id_len,
+ PAC_TYPE_TUNNEL_PAC);
+ if (data->current_pac == NULL) {
+ /*
+ * Tunnel PAC was not available for this A-ID. Try to use
+ * Machine Authentication PAC, if one is available.
+ */
+ data->current_pac = eap_fast_get_pac(
+ data->pac, a_id, a_id_len,
+ PAC_TYPE_MACHINE_AUTHENTICATION);
+ }
+
+ if (data->current_pac) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this A-ID "
+ "(PAC-Type %d)", data->current_pac->pac_type);
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info",
+ data->current_pac->a_id_info,
+ data->current_pac->a_id_info_len);
+ }
+}
+
+
+static int eap_fast_use_pac_opaque(struct eap_sm *sm,
+ struct eap_fast_data *data,
+ struct eap_fast_pac *pac)
+{
+ u8 *tlv;
+ size_t tlv_len, olen;
+ struct eap_tlv_hdr *ehdr;
+
+ olen = pac->pac_opaque_len;
+ tlv_len = sizeof(*ehdr) + olen;
+ tlv = os_malloc(tlv_len);
+ if (tlv) {
+ ehdr = (struct eap_tlv_hdr *) tlv;
+ ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE);
+ ehdr->length = host_to_be16(olen);
+ os_memcpy(ehdr + 1, pac->pac_opaque, olen);
+ }
+ if (tlv == NULL ||
+ tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
+ TLS_EXT_PAC_OPAQUE,
+ tlv, tlv_len) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to add PAC-Opaque TLS "
+ "extension");
+ os_free(tlv);
+ return -1;
+ }
+ os_free(tlv);
+
+ return 0;
+}
+
+
+static int eap_fast_clear_pac_opaque_ext(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
+ TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to remove PAC-Opaque "
+ "TLS extension");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm,
+ struct eap_fast_data *data)
+{
+ u8 ciphers[5];
+ int count = 0;
+
+ if (data->provisioning_allowed & EAP_FAST_PROV_UNAUTH) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling unauthenticated "
+ "provisioning TLS cipher suites");
+ ciphers[count++] = TLS_CIPHER_ANON_DH_AES128_SHA;
+ }
+
+ if (data->provisioning_allowed & EAP_FAST_PROV_AUTH) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling authenticated "
+ "provisioning TLS cipher suites");
+ ciphers[count++] = TLS_CIPHER_RSA_DHE_AES128_SHA;
+ ciphers[count++] = TLS_CIPHER_AES128_SHA;
+ ciphers[count++] = TLS_CIPHER_RC4_SHA;
+ }
+
+ ciphers[count++] = TLS_CIPHER_NONE;
+
+ if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn,
+ ciphers)) {
+ wpa_printf(MSG_INFO, "EAP-FAST: Could not configure TLS "
+ "cipher suites for provisioning");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_fast_process_start(struct eap_sm *sm,
+ struct eap_fast_data *data, u8 flags,
+ const u8 *pos, size_t left)
+{
+ const u8 *a_id;
+ size_t a_id_len;
+
+ /* EAP-FAST Version negotiation (section 3.1) */
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own ver=%d)",
+ flags & EAP_TLS_VERSION_MASK, data->fast_version);
+ if ((flags & EAP_TLS_VERSION_MASK) < data->fast_version)
+ data->fast_version = flags & EAP_TLS_VERSION_MASK;
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d",
+ data->fast_version);
+
+ a_id = eap_fast_get_a_id(pos, left, &a_id_len);
+ eap_fast_select_pac(data, a_id, a_id_len);
+
+ if (data->resuming && data->current_pac) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume session - "
+ "do not add PAC-Opaque to TLS ClientHello");
+ if (eap_fast_clear_pac_opaque_ext(sm, data) < 0)
+ return -1;
+ } else if (data->current_pac) {
+ /*
+ * PAC found for the A-ID and we are not resuming an old
+ * session, so add PAC-Opaque extension to ClientHello.
+ */
+ if (eap_fast_use_pac_opaque(sm, data, data->current_pac) < 0)
+ return -1;
+ } else {
+ /* No PAC found, so we must provision one. */
+ if (!data->provisioning_allowed) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found and "
+ "provisioning disabled");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - "
+ "starting provisioning");
+ if (eap_fast_set_provisioning_ciphers(sm, data) < 0 ||
+ eap_fast_clear_pac_opaque_ext(sm, data) < 0)
+ return -1;
+ data->provisioning = 1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ const struct eap_hdr *req;
+ size_t left;
+ int res;
+ u8 flags, id;
+ struct wpabuf *resp;
+ const u8 *pos;
+ struct eap_fast_data *data = priv;
+
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret,
+ reqData, &left, &flags);
+ if (pos == NULL)
+ return NULL;
+
+ req = wpabuf_head(reqData);
+ id = req->identifier;
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ if (eap_fast_process_start(sm, data, flags, pos, left) < 0)
+ return NULL;
+
+ left = 0; /* A-ID is not used in further packet processing */
+ }
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ /* Process tunneled (encrypted) phase 2 data. */
+ struct wpabuf msg;
+ wpabuf_set(&msg, pos, left);
+ res = eap_fast_decrypt(sm, data, ret, req, &msg, &resp);
+ if (res < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ /*
+ * Ack possible Alert that may have caused failure in
+ * decryption.
+ */
+ res = 1;
+ }
+ } else {
+ /* Continue processing TLS handshake (phase 1). */
+ res = eap_peer_tls_process_helper(sm, &data->ssl,
+ EAP_TYPE_FAST,
+ data->fast_version, id, pos,
+ left, &resp);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ char cipher[80];
+ wpa_printf(MSG_DEBUG,
+ "EAP-FAST: TLS done, proceed to Phase 2");
+ if (data->provisioning &&
+ (!(data->provisioning_allowed &
+ EAP_FAST_PROV_AUTH) ||
+ tls_get_cipher(sm->ssl_ctx, data->ssl.conn,
+ cipher, sizeof(cipher)) < 0 ||
+ os_strstr(cipher, "ADH-") ||
+ os_strstr(cipher, "anon"))) {
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Using "
+ "anonymous (unauthenticated) "
+ "provisioning");
+ data->anon_provisioning = 1;
+ } else
+ data->anon_provisioning = 0;
+ data->resuming = 0;
+ eap_fast_derive_keys(sm, data);
+ }
+
+ if (res == 2) {
+ struct wpabuf msg;
+ /*
+ * Application data included in the handshake message.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = resp;
+ resp = NULL;
+ wpabuf_set(&msg, pos, left);
+ res = eap_fast_decrypt(sm, data, ret, req, &msg,
+ &resp);
+ }
+ }
+
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, EAP_TYPE_FAST,
+ data->fast_version);
+ }
+
+ return resp;
+}
+
+
+#if 0 /* FIX */
+static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ os_free(data->key_block_p);
+ data->key_block_p = NULL;
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = NULL;
+}
+
+
+static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->init_for_reauth)
+ data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+ data->phase2_success = 0;
+ data->resuming = 1;
+ data->provisioning = 0;
+ data->anon_provisioning = 0;
+ data->simck_idx = 0;
+ return priv;
+}
+#endif
+
+
+static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_fast_data *data = priv;
+ int len, ret;
+
+ len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+ if (data->phase2_method) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "EAP-FAST Phase2 method=%s\n",
+ data->phase2_method->name);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+ }
+ return len;
+}
+
+
+static Boolean eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_fast_data *data = priv;
+ return data->success;
+}
+
+
+static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_fast_data *data = priv;
+ u8 *key;
+
+ if (!data->success)
+ return NULL;
+
+ key = os_malloc(EAP_FAST_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_FAST_KEY_LEN;
+ os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_fast_data *data = priv;
+ u8 *key;
+
+ if (!data->success)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_fast_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_fast_init;
+ eap->deinit = eap_fast_deinit;
+ eap->process = eap_fast_process;
+ eap->isKeyAvailable = eap_fast_isKeyAvailable;
+ eap->getKey = eap_fast_getKey;
+ eap->get_status = eap_fast_get_status;
+#if 0
+ eap->has_reauth_data = eap_fast_has_reauth_data;
+ eap->deinit_for_reauth = eap_fast_deinit_for_reauth;
+ eap->init_for_reauth = eap_fast_init_for_reauth;
+#endif
+ eap->get_emsk = eap_fast_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
new file mode 100644
index 0000000..4037288
--- /dev/null
+++ b/src/eap_peer/eap_fast_pac.c
@@ -0,0 +1,923 @@
+/*
+ * EAP peer method: EAP-FAST PAC file processing
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_config.h"
+#include "eap_i.h"
+#include "eap_fast_pac.h"
+
+/* TODO: encrypt PAC-Key in the PAC file */
+
+
+/* Text data format */
+static const char *pac_file_hdr =
+ "wpa_supplicant EAP-FAST PAC file - version 1";
+
+/*
+ * Binary data format
+ * 4-octet magic value: 6A E4 92 0C
+ * 2-octet version (big endian)
+ * <version specific data>
+ *
+ * version=0:
+ * Sequence of PAC entries:
+ * 2-octet PAC-Type (big endian)
+ * 32-octet PAC-Key
+ * 2-octet PAC-Opaque length (big endian)
+ * <variable len> PAC-Opaque data (length bytes)
+ * 2-octet PAC-Info length (big endian)
+ * <variable len> PAC-Info data (length bytes)
+ */
+
+#define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c
+#define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0
+
+
+/**
+ * eap_fast_free_pac - Free PAC data
+ * @pac: Pointer to the PAC entry
+ *
+ * Note that the PAC entry must not be in a list since this function does not
+ * remove the list links.
+ */
+void eap_fast_free_pac(struct eap_fast_pac *pac)
+{
+ os_free(pac->pac_opaque);
+ os_free(pac->pac_info);
+ os_free(pac->a_id);
+ os_free(pac->i_id);
+ os_free(pac->a_id_info);
+ os_free(pac);
+}
+
+
+/**
+ * eap_fast_get_pac - Get a PAC entry based on A-ID
+ * @pac_root: Pointer to root of the PAC list
+ * @a_id: A-ID to search for
+ * @a_id_len: Length of A-ID
+ * @pac_type: PAC-Type to search for
+ * Returns: Pointer to the PAC entry, or %NULL if A-ID not found
+ */
+struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
+ const u8 *a_id, size_t a_id_len,
+ u16 pac_type)
+{
+ struct eap_fast_pac *pac = pac_root;
+
+ while (pac) {
+ if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
+ os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
+ return pac;
+ }
+ pac = pac->next;
+ }
+ return NULL;
+}
+
+
+static void eap_fast_remove_pac(struct eap_fast_pac **pac_root,
+ struct eap_fast_pac **pac_current,
+ const u8 *a_id, size_t a_id_len, u16 pac_type)
+{
+ struct eap_fast_pac *pac, *prev;
+
+ pac = *pac_root;
+ prev = NULL;
+
+ while (pac) {
+ if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
+ os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
+ if (prev == NULL)
+ *pac_root = pac->next;
+ else
+ prev->next = pac->next;
+ if (*pac_current == pac)
+ *pac_current = NULL;
+ eap_fast_free_pac(pac);
+ break;
+ }
+ prev = pac;
+ pac = pac->next;
+ }
+}
+
+
+static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
+ const u8 *src, size_t src_len)
+{
+ if (src) {
+ *dst = os_malloc(src_len);
+ if (*dst == NULL)
+ return -1;
+ os_memcpy(*dst, src, src_len);
+ *dst_len = src_len;
+ }
+ return 0;
+}
+
+
+/**
+ * eap_fast_add_pac - Add a copy of a PAC entry to a list
+ * @pac_root: Pointer to PAC list root pointer
+ * @pac_current: Pointer to the current PAC pointer
+ * @entry: New entry to clone and add to the list
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function makes a clone of the given PAC entry and adds this copied
+ * entry to the list (pac_root). If an old entry for the same A-ID is found,
+ * it will be removed from the PAC list and in this case, pac_current entry
+ * is set to %NULL if it was the removed entry.
+ */
+int eap_fast_add_pac(struct eap_fast_pac **pac_root,
+ struct eap_fast_pac **pac_current,
+ struct eap_fast_pac *entry)
+{
+ struct eap_fast_pac *pac;
+
+ if (entry == NULL || entry->a_id == NULL)
+ return -1;
+
+ /* Remove a possible old entry for the matching A-ID. */
+ eap_fast_remove_pac(pac_root, pac_current,
+ entry->a_id, entry->a_id_len, entry->pac_type);
+
+ /* Allocate a new entry and add it to the list of PACs. */
+ pac = os_zalloc(sizeof(*pac));
+ if (pac == NULL)
+ return -1;
+
+ pac->pac_type = entry->pac_type;
+ os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
+ if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
+ entry->pac_opaque, entry->pac_opaque_len) < 0 ||
+ eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
+ entry->pac_info, entry->pac_info_len) < 0 ||
+ eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
+ entry->a_id, entry->a_id_len) < 0 ||
+ eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
+ entry->i_id, entry->i_id_len) < 0 ||
+ eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
+ entry->a_id_info, entry->a_id_info_len) < 0) {
+ eap_fast_free_pac(pac);
+ return -1;
+ }
+
+ pac->next = *pac_root;
+ *pac_root = pac;
+
+ return 0;
+}
+
+
+struct eap_fast_read_ctx {
+ FILE *f;
+ const char *pos;
+ const char *end;
+ int line;
+ char *buf;
+ size_t buf_len;
+};
+
+static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
+{
+ char *pos;
+
+ rc->line++;
+ if (rc->f) {
+ if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
+ return -1;
+ } else {
+ const char *l_end;
+ size_t len;
+ if (rc->pos >= rc->end)
+ return -1;
+ l_end = rc->pos;
+ while (l_end < rc->end && *l_end != '\n')
+ l_end++;
+ len = l_end - rc->pos;
+ if (len >= rc->buf_len)
+ len = rc->buf_len - 1;
+ os_memcpy(rc->buf, rc->pos, len);
+ rc->buf[len] = '\0';
+ rc->pos = l_end + 1;
+ }
+
+ rc->buf[rc->buf_len - 1] = '\0';
+ pos = rc->buf;
+ while (*pos != '\0') {
+ if (*pos == '\n' || *pos == '\r') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+
+ pos = os_strchr(rc->buf, '=');
+ if (pos)
+ *pos++ = '\0';
+ *value = pos;
+
+ return 0;
+}
+
+
+static u8 * eap_fast_parse_hex(const char *value, size_t *len)
+{
+ int hlen;
+ u8 *buf;
+
+ if (value == NULL)
+ return NULL;
+ hlen = os_strlen(value);
+ if (hlen & 1)
+ return NULL;
+ *len = hlen / 2;
+ buf = os_malloc(*len);
+ if (buf == NULL)
+ return NULL;
+ if (hexstr2bin(value, buf, *len)) {
+ os_free(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+
+static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
+ struct eap_fast_read_ctx *rc)
+{
+ os_memset(rc, 0, sizeof(*rc));
+
+ rc->buf_len = 2048;
+ rc->buf = os_malloc(rc->buf_len);
+ if (rc->buf == NULL)
+ return -1;
+
+ if (os_strncmp(pac_file, "blob://", 7) == 0) {
+ const struct wpa_config_blob *blob;
+ blob = eap_get_config_blob(sm, pac_file + 7);
+ if (blob == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
+ "assume no PAC entries have been "
+ "provisioned", pac_file + 7);
+ os_free(rc->buf);
+ return -1;
+ }
+ rc->pos = (char *) blob->data;
+ rc->end = (char *) blob->data + blob->len;
+ } else {
+ rc->f = fopen(pac_file, "rb");
+ if (rc->f == NULL) {
+ wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
+ "assume no PAC entries have been "
+ "provisioned", pac_file);
+ os_free(rc->buf);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
+{
+ os_free(rc->buf);
+ if (rc->f)
+ fclose(rc->f);
+}
+
+
+static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
+{
+ if (*pac)
+ return "START line without END";
+
+ *pac = os_zalloc(sizeof(struct eap_fast_pac));
+ if (*pac == NULL)
+ return "No memory for PAC entry";
+ (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
+ struct eap_fast_pac **pac)
+{
+ if (*pac == NULL)
+ return "END line without START";
+ if (*pac_root) {
+ struct eap_fast_pac *end = *pac_root;
+ while (end->next)
+ end = end->next;
+ end->next = *pac;
+ } else
+ *pac_root = *pac;
+
+ *pac = NULL;
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
+ char *pos)
+{
+ pac->pac_type = atoi(pos);
+ if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
+ pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
+ pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION)
+ return "Unrecognized PAC-Type";
+
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos)
+{
+ u8 *key;
+ size_t key_len;
+
+ key = eap_fast_parse_hex(pos, &key_len);
+ if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
+ os_free(key);
+ return "Invalid PAC-Key";
+ }
+
+ os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
+ os_free(key);
+
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
+ char *pos)
+{
+ os_free(pac->pac_opaque);
+ pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
+ if (pac->pac_opaque == NULL)
+ return "Invalid PAC-Opaque";
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
+{
+ os_free(pac->a_id);
+ pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
+ if (pac->a_id == NULL)
+ return "Invalid A-ID";
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
+{
+ os_free(pac->i_id);
+ pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
+ if (pac->i_id == NULL)
+ return "Invalid I-ID";
+ return NULL;
+}
+
+
+static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
+ char *pos)
+{
+ os_free(pac->a_id_info);
+ pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
+ if (pac->a_id_info == NULL)
+ return "Invalid A-ID-Info";
+ return NULL;
+}
+
+
+/**
+ * eap_fast_load_pac - Load PAC entries (text format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Pointer to root of the PAC list (to be filled)
+ * @pac_file: Name of the PAC file/blob to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+ const char *pac_file)
+{
+ struct eap_fast_read_ctx rc;
+ struct eap_fast_pac *pac = NULL;
+ int count = 0;
+ char *pos;
+ const char *err = NULL;
+
+ if (pac_file == NULL)
+ return -1;
+
+ if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
+ return 0;
+
+ if (eap_fast_read_line(&rc, &pos) < 0 ||
+ os_strcmp(pac_file_hdr, rc.buf) != 0)
+ err = "Unrecognized header line";
+
+ while (!err && eap_fast_read_line(&rc, &pos) == 0) {
+ if (os_strcmp(rc.buf, "START") == 0)
+ err = eap_fast_parse_start(&pac);
+ else if (os_strcmp(rc.buf, "END") == 0) {
+ err = eap_fast_parse_end(pac_root, &pac);
+ count++;
+ } else if (!pac)
+ err = "Unexpected line outside START/END block";
+ else if (os_strcmp(rc.buf, "PAC-Type") == 0)
+ err = eap_fast_parse_pac_type(pac, pos);
+ else if (os_strcmp(rc.buf, "PAC-Key") == 0)
+ err = eap_fast_parse_pac_key(pac, pos);
+ else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
+ err = eap_fast_parse_pac_opaque(pac, pos);
+ else if (os_strcmp(rc.buf, "A-ID") == 0)
+ err = eap_fast_parse_a_id(pac, pos);
+ else if (os_strcmp(rc.buf, "I-ID") == 0)
+ err = eap_fast_parse_i_id(pac, pos);
+ else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
+ err = eap_fast_parse_a_id_info(pac, pos);
+ }
+
+ if (pac) {
+ err = "PAC block not terminated with END";
+ eap_fast_free_pac(pac);
+ }
+
+ eap_fast_deinit_pac_data(&rc);
+
+ if (err) {
+ wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'",
+ err, pac_file, rc.line);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
+ count, pac_file);
+
+ return 0;
+}
+
+
+static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
+ const char *field, const u8 *data,
+ size_t len, int txt)
+{
+ size_t i, need;
+ int ret;
+ char *end;
+
+ if (data == NULL || buf == NULL || *buf == NULL ||
+ pos == NULL || *pos == NULL || *pos < *buf)
+ return;
+
+ need = os_strlen(field) + len * 2 + 30;
+ if (txt)
+ need += os_strlen(field) + len + 20;
+
+ if (*pos - *buf + need > *buf_len) {
+ char *nbuf = os_realloc(*buf, *buf_len + need);
+ if (nbuf == NULL) {
+ os_free(*buf);
+ *buf = NULL;
+ return;
+ }
+ *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/src/eap_peer/eap_fast_pac.h b/src/eap_peer/eap_fast_pac.h
new file mode 100644
index 0000000..9483f96
--- /dev/null
+++ b/src/eap_peer/eap_fast_pac.h
@@ -0,0 +1,56 @@
+/*
+ * EAP peer method: EAP-FAST PAC file processing
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_FAST_PAC_H
+#define EAP_FAST_PAC_H
+
+#include "eap_common/eap_fast_common.h"
+
+struct eap_fast_pac {
+ struct eap_fast_pac *next;
+
+ u8 pac_key[EAP_FAST_PAC_KEY_LEN];
+ u8 *pac_opaque;
+ size_t pac_opaque_len;
+ u8 *pac_info;
+ size_t pac_info_len;
+ u8 *a_id;
+ size_t a_id_len;
+ u8 *i_id;
+ size_t i_id_len;
+ u8 *a_id_info;
+ size_t a_id_info_len;
+ u16 pac_type;
+};
+
+
+void eap_fast_free_pac(struct eap_fast_pac *pac);
+struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
+ const u8 *a_id, size_t a_id_len,
+ u16 pac_type);
+int eap_fast_add_pac(struct eap_fast_pac **pac_root,
+ struct eap_fast_pac **pac_current,
+ struct eap_fast_pac *entry);
+int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+ const char *pac_file);
+int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+ const char *pac_file);
+size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
+ size_t max_len);
+int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+ const char *pac_file);
+int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+ const char *pac_file);
+
+#endif /* EAP_FAST_PAC_H */
diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c
new file mode 100644
index 0000000..5037c60
--- /dev/null
+++ b/src/eap_peer/eap_gpsk.c
@@ -0,0 +1,738 @@
+/*
+ * EAP peer method: EAP-GPSK (RFC 5433)
+ * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "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;
+ int session_id_set;
+ u8 *id_peer;
+ size_t id_peer_len;
+ u8 *id_server;
+ size_t id_server_len;
+ int vendor; /* CSuite/Specifier */
+ int specifier; /* CSuite/Specifier */
+ u8 *psk;
+ size_t psk_len;
+};
+
+
+static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
+ u8 identifier,
+ const u8 *csuite_list,
+ size_t csuite_list_len);
+static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
+ u8 identifier);
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_gpsk_state_txt(int state)
+{
+ switch (state) {
+ case GPSK_1:
+ return "GPSK-1";
+ case GPSK_3:
+ return "GPSK-3";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "?";
+ }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
+ eap_gpsk_state_txt(data->state),
+ eap_gpsk_state_txt(state));
+ data->state = state;
+}
+
+
+static void eap_gpsk_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_gpsk_init(struct eap_sm *sm)
+{
+ struct eap_gpsk_data *data;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = GPSK_1;
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ data->id_peer = os_malloc(identity_len);
+ if (data->id_peer == NULL) {
+ eap_gpsk_deinit(sm, data);
+ return NULL;
+ }
+ os_memcpy(data->id_peer, identity, identity_len);
+ data->id_peer_len = identity_len;
+ }
+
+ data->psk = os_malloc(password_len);
+ if (data->psk == NULL) {
+ eap_gpsk_deinit(sm, data);
+ return NULL;
+ }
+ os_memcpy(data->psk, password, password_len);
+ data->psk_len = password_len;
+
+ return data;
+}
+
+
+static void eap_gpsk_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_gpsk_data *data = priv;
+ os_free(data->id_server);
+ os_free(data->id_peer);
+ os_free(data->psk);
+ os_free(data);
+}
+
+
+static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data,
+ const u8 *pos, const u8 *end)
+{
+ u16 alen;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
+ return NULL;
+ }
+ alen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < alen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow");
+ return NULL;
+ }
+ os_free(data->id_server);
+ data->id_server = os_malloc(alen);
+ if (data->id_server == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server");
+ return NULL;
+ }
+ os_memcpy(data->id_server, pos, alen);
+ data->id_server_len = alen;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server",
+ data->id_server, data->id_server_len);
+ pos += alen;
+
+ return pos;
+}
+
+
+static const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data,
+ const u8 *pos, const u8 *end)
+{
+ if (pos == NULL)
+ return NULL;
+
+ if (end - pos < EAP_GPSK_RAND_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow");
+ return NULL;
+ }
+ os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server",
+ data->rand_server, EAP_GPSK_RAND_LEN);
+ pos += EAP_GPSK_RAND_LEN;
+
+ return pos;
+}
+
+
+static int eap_gpsk_select_csuite(struct eap_sm *sm,
+ struct eap_gpsk_data *data,
+ const u8 *csuite_list,
+ size_t csuite_list_len)
+{
+ struct eap_gpsk_csuite *csuite;
+ int i, count;
+
+ count = csuite_list_len / sizeof(struct eap_gpsk_csuite);
+ data->vendor = EAP_GPSK_VENDOR_IETF;
+ data->specifier = EAP_GPSK_CIPHER_RESERVED;
+ csuite = (struct eap_gpsk_csuite *) csuite_list;
+ for (i = 0; i < count; i++) {
+ int vendor, specifier;
+ vendor = WPA_GET_BE32(csuite->vendor);
+ specifier = WPA_GET_BE16(csuite->specifier);
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d",
+ i, vendor, specifier);
+ if (data->vendor == EAP_GPSK_VENDOR_IETF &&
+ data->specifier == EAP_GPSK_CIPHER_RESERVED &&
+ eap_gpsk_supported_ciphersuite(vendor, specifier)) {
+ data->vendor = vendor;
+ data->specifier = specifier;
+ }
+ csuite++;
+ }
+ if (data->vendor == EAP_GPSK_VENDOR_IETF &&
+ data->specifier == EAP_GPSK_CIPHER_RESERVED) {
+ wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported "
+ "ciphersuite found");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d",
+ data->vendor, data->specifier);
+
+ return 0;
+}
+
+
+static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
+ struct eap_gpsk_data *data,
+ const u8 **list,
+ size_t *list_len,
+ const u8 *pos, const u8 *end)
+{
+ if (pos == NULL)
+ return NULL;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
+ return NULL;
+ }
+ *list_len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < (int) *list_len) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
+ return NULL;
+ }
+ if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu",
+ (unsigned long) *list_len);
+ return NULL;
+ }
+ *list = pos;
+ pos += *list_len;
+
+ if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0)
+ return NULL;
+
+ return pos;
+}
+
+
+static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
+ struct eap_gpsk_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload,
+ size_t payload_len)
+{
+ size_t csuite_list_len;
+ const u8 *csuite_list, *pos, *end;
+ struct wpabuf *resp;
+
+ if (data->state != GPSK_1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1");
+
+ end = payload + payload_len;
+
+ pos = eap_gpsk_process_id_server(data, payload, end);
+ pos = eap_gpsk_process_rand_server(data, pos, end);
+ pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list,
+ &csuite_list_len, pos, end);
+ if (pos == NULL) {
+ eap_gpsk_state(data, FAILURE);
+ return NULL;
+ }
+
+ resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData),
+ csuite_list, csuite_list_len);
+ if (resp == NULL)
+ return NULL;
+
+ eap_gpsk_state(data, GPSK_3);
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
+ u8 identifier,
+ const u8 *csuite_list,
+ size_t csuite_list_len)
+{
+ struct wpabuf *resp;
+ size_t len, miclen;
+ u8 *rpos, *start;
+ struct eap_gpsk_csuite *csuite;
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2");
+
+ miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+ len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len +
+ 2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len +
+ sizeof(struct eap_gpsk_csuite) + 2 + miclen;
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
+ EAP_CODE_RESPONSE, identifier);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2);
+ start = wpabuf_put(resp, 0);
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
+ data->id_peer, data->id_peer_len);
+ wpabuf_put_be16(resp, data->id_peer_len);
+ wpabuf_put_data(resp, data->id_peer, data->id_peer_len);
+
+ wpabuf_put_be16(resp, data->id_server_len);
+ wpabuf_put_data(resp, data->id_server, data->id_server_len);
+
+ if (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;
+ }
+
+ /* No PD_Payload_1 */
+ wpabuf_put_be16(resp, 0);
+
+ rpos = wpabuf_put(resp, miclen);
+ if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+ data->specifier, start, rpos - start, rpos) <
+ 0) {
+ eap_gpsk_state(data, FAILURE);
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ return resp;
+}
+
+
+static const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data,
+ const u8 *pos, const u8 *end)
+{
+ if (end - pos < EAP_GPSK_RAND_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "RAND_Peer");
+ return NULL;
+ }
+ if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and "
+ "GPSK-3 did not match");
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2",
+ data->rand_peer, EAP_GPSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3",
+ pos, EAP_GPSK_RAND_LEN);
+ return NULL;
+ }
+ pos += EAP_GPSK_RAND_LEN;
+
+ if (end - pos < EAP_GPSK_RAND_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "RAND_Server");
+ return NULL;
+ }
+ if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
+ "GPSK-3 did not match");
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
+ data->rand_server, EAP_GPSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3",
+ pos, EAP_GPSK_RAND_LEN);
+ return NULL;
+ }
+ pos += EAP_GPSK_RAND_LEN;
+
+ return pos;
+}
+
+
+static const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data,
+ const u8 *pos, const u8 *end)
+{
+ size_t len;
+
+ if (pos == NULL)
+ return NULL;
+
+ if (end - pos < (int) 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "length(ID_Server)");
+ return NULL;
+ }
+
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+
+ if (end - pos < (int) len) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "ID_Server");
+ return NULL;
+ }
+
+ if (len != data->id_server_len ||
+ os_memcmp(pos, data->id_server, len) != 0) {
+ wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with "
+ "the one used in GPSK-1");
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1",
+ data->id_server, data->id_server_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3",
+ pos, len);
+ return NULL;
+ }
+
+ pos += len;
+
+ return pos;
+}
+
+
+static const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data,
+ const u8 *pos, const u8 *end)
+{
+ int vendor, specifier;
+ const struct eap_gpsk_csuite *csuite;
+
+ if (pos == NULL)
+ return NULL;
+
+ if (end - pos < (int) sizeof(*csuite)) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "CSuite_Sel");
+ return NULL;
+ }
+ csuite = (const struct eap_gpsk_csuite *) pos;
+ vendor = WPA_GET_BE32(csuite->vendor);
+ specifier = WPA_GET_BE16(csuite->specifier);
+ pos += sizeof(*csuite);
+ if (vendor != data->vendor || specifier != data->specifier) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not "
+ "match with the one sent in GPSK-2 (%d:%d)",
+ vendor, specifier, data->vendor, data->specifier);
+ return NULL;
+ }
+
+ return pos;
+}
+
+
+static const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data,
+ const u8 *pos, const u8 *end)
+{
+ u16 alen;
+
+ if (pos == NULL)
+ return NULL;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "PD_Payload_2 length");
+ return NULL;
+ }
+ alen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < alen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+ "%d-octet PD_Payload_2", alen);
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen);
+ pos += alen;
+
+ return pos;
+}
+
+
+static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data,
+ const u8 *payload,
+ const u8 *pos, const u8 *end)
+{
+ size_t miclen;
+ u8 mic[EAP_GPSK_MAX_MIC_LEN];
+
+ if (pos == NULL)
+ return NULL;
+
+ miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+ if (end - pos < (int) miclen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
+ "(left=%lu miclen=%lu)",
+ (unsigned long) (end - pos),
+ (unsigned long) miclen);
+ return NULL;
+ }
+ if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+ data->specifier, payload, pos - payload, mic)
+ < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
+ return NULL;
+ }
+ if (os_memcmp(mic, pos, miclen) != 0) {
+ wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3");
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
+ return NULL;
+ }
+ pos += miclen;
+
+ return pos;
+}
+
+
+static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm,
+ struct eap_gpsk_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload,
+ size_t payload_len)
+{
+ struct wpabuf *resp;
+ const u8 *pos, *end;
+
+ if (data->state != GPSK_3) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3");
+
+ end = payload + payload_len;
+
+ pos = eap_gpsk_validate_rand(data, payload, end);
+ pos = eap_gpsk_validate_id_server(data, pos, end);
+ pos = eap_gpsk_validate_csuite(data, pos, end);
+ pos = eap_gpsk_validate_pd_payload_2(data, pos, end);
+ pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end);
+
+ if (pos == NULL) {
+ eap_gpsk_state(data, FAILURE);
+ return NULL;
+ }
+ if (pos != end) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
+ "data in the end of GPSK-2",
+ (unsigned long) (end - pos));
+ }
+
+ resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+
+ eap_gpsk_state(data, SUCCESS);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
+ u8 identifier)
+{
+ struct wpabuf *resp;
+ u8 *rpos, *start;
+ size_t mlen;
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4");
+
+ mlen = eap_gpsk_mic_len(data->vendor, data->specifier);
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen,
+ EAP_CODE_RESPONSE, identifier);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4);
+ start = wpabuf_put(resp, 0);
+
+ /* No PD_Payload_3 */
+ wpabuf_put_be16(resp, 0);
+
+ rpos = wpabuf_put(resp, mlen);
+ if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+ data->specifier, start, rpos - start, rpos) <
+ 0) {
+ eap_gpsk_state(data, FAILURE);
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ return resp;
+}
+
+
+static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_gpsk_data *data = priv;
+ struct wpabuf *resp;
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len);
+ if (pos == NULL || len < 1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+
+ switch (*pos) {
+ case EAP_GPSK_OPCODE_GPSK_1:
+ resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData,
+ pos + 1, len - 1);
+ break;
+ case EAP_GPSK_OPCODE_GPSK_3:
+ resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData,
+ pos + 1, len - 1);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with "
+ "unknown opcode %d", *pos);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_gpsk_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_gpsk_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->msk, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+
+ return key;
+}
+
+
+static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_gpsk_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
+int eap_peer_gpsk_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_gpsk_init;
+ eap->deinit = eap_gpsk_deinit;
+ eap->process = eap_gpsk_process;
+ eap->isKeyAvailable = eap_gpsk_isKeyAvailable;
+ eap->getKey = eap_gpsk_getKey;
+ eap->get_emsk = eap_gpsk_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_gtc.c b/src/eap_peer/eap_gtc.c
new file mode 100644
index 0000000..b2b554b
--- /dev/null
+++ b/src/eap_peer/eap_gtc.c
@@ -0,0 +1,151 @@
+/*
+ * EAP peer method: EAP-GTC (RFC 3748)
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_gtc_data {
+ int prefix;
+};
+
+
+static void * eap_gtc_init(struct eap_sm *sm)
+{
+ struct eap_gtc_data *data;
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ if (sm->m && sm->m->vendor == EAP_VENDOR_IETF &&
+ sm->m->method == EAP_TYPE_FAST) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix "
+ "with challenge/response");
+ data->prefix = 1;
+ }
+ return data;
+}
+
+
+static void eap_gtc_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_gtc_data *data = priv;
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_gtc_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_gtc_data *data = priv;
+ struct wpabuf *resp;
+ const u8 *pos, *password, *identity;
+ size_t password_len, identity_len, len, plen;
+ int otp;
+ u8 id;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, reqData, &len);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ id = eap_get_id(reqData);
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", pos, len);
+ if (data->prefix &&
+ (len < 10 || os_memcmp(pos, "CHALLENGE=", 10) != 0)) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with "
+ "expected prefix");
+
+ /* Send an empty response in order to allow tunneled
+ * acknowledgement of the failure. This will also cover the
+ * error case which seems to use EAP-MSCHAPv2 like error
+ * reporting with EAP-GTC inside EAP-FAST tunnel. */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC,
+ 0, EAP_CODE_RESPONSE, id);
+ return resp;
+ }
+
+ password = eap_get_config_otp(sm, &password_len);
+ if (password)
+ otp = 1;
+ else {
+ password = eap_get_config_password(sm, &password_len);
+ otp = 0;
+ }
+
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-GTC: Password not configured");
+ eap_sm_request_otp(sm, (const char *) pos, len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+
+ ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = FALSE;
+
+ plen = password_len;
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity == NULL)
+ return NULL;
+ if (data->prefix)
+ plen += 9 + identity_len + 1;
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, plen,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+ if (data->prefix) {
+ wpabuf_put_data(resp, "RESPONSE=", 9);
+ wpabuf_put_data(resp, identity, identity_len);
+ wpabuf_put_u8(resp, '\0');
+ }
+ wpabuf_put_data(resp, password, password_len);
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response",
+ wpabuf_head_u8(resp) + sizeof(struct eap_hdr) +
+ 1, plen);
+
+ if (otp) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password");
+ eap_clear_config_otp(sm);
+ }
+
+ return resp;
+}
+
+
+int eap_peer_gtc_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_gtc_init;
+ eap->deinit = eap_gtc_deinit;
+ eap->process = eap_gtc_process;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
new file mode 100644
index 0000000..afca611
--- /dev/null
+++ b/src/eap_peer/eap_i.h
@@ -0,0 +1,356 @@
+/*
+ * EAP peer state machines internal structures (RFC 4137)
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_I_H
+#define EAP_I_H
+
+#include "wpabuf.h"
+#include "eap_peer/eap.h"
+#include "eap_common/eap_common.h"
+
+/* RFC 4137 - EAP Peer state machine */
+
+typedef enum {
+ DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC
+} EapDecision;
+
+typedef enum {
+ METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE
+} EapMethodState;
+
+/**
+ * struct eap_method_ret - EAP return values from struct eap_method::process()
+ *
+ * These structure contains OUT variables for the interface between peer state
+ * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as
+ * the return value of struct eap_method::process() so it is not included in
+ * this structure.
+ */
+struct eap_method_ret {
+ /**
+ * ignore - Whether method decided to drop the current packed (OUT)
+ */
+ Boolean ignore;
+
+ /**
+ * methodState - Method-specific state (IN/OUT)
+ */
+ EapMethodState methodState;
+
+ /**
+ * decision - Authentication decision (OUT)
+ */
+ EapDecision decision;
+
+ /**
+ * allowNotifications - Whether method allows notifications (OUT)
+ */
+ Boolean allowNotifications;
+};
+
+
+/**
+ * struct eap_method - EAP method interface
+ * This structure defines the EAP method interface. Each method will need to
+ * register its own EAP type, EAP name, and set of function pointers for method
+ * specific operations. This interface is based on section 4.4 of RFC 4137.
+ */
+struct eap_method {
+ /**
+ * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+ */
+ int vendor;
+
+ /**
+ * method - EAP type number (EAP_TYPE_*)
+ */
+ EapType method;
+
+ /**
+ * name - Name of the method (e.g., "TLS")
+ */
+ const char *name;
+
+ /**
+ * init - Initialize an EAP method
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to allocated private data, or %NULL on failure
+ *
+ * This function is used to initialize the EAP method explicitly
+ * instead of using METHOD_INIT state as specific in RFC 4137. The
+ * method is expected to initialize it method-specific state and return
+ * a pointer that will be used as the priv argument to other calls.
+ */
+ void * (*init)(struct eap_sm *sm);
+
+ /**
+ * deinit - Deinitialize an EAP method
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ *
+ * Deinitialize the EAP method and free any allocated private data.
+ */
+ void (*deinit)(struct eap_sm *sm, void *priv);
+
+ /**
+ * process - Process an EAP request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * Returns: Pointer to allocated EAP response packet (eapRespData)
+ *
+ * This function is a combination of m.check(), m.process(), and
+ * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other
+ * words, this function validates the incoming request, processes it,
+ * and build a response packet. m.check() and m.process() return values
+ * are returned through struct eap_method_ret *ret variable. Caller is
+ * responsible for freeing the returned EAP response packet.
+ */
+ struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData);
+
+ /**
+ * isKeyAvailable - Find out whether EAP method has keying material
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * Returns: %TRUE if key material (eapKeyData) is available
+ */
+ Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv);
+
+ /**
+ * getKey - Get EAP method specific keying material (eapKeyData)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Pointer to variable to store key length (eapKeyDataLen)
+ * Returns: Keying material (eapKeyData) or %NULL if not available
+ *
+ * This function can be used to get the keying material from the EAP
+ * method. The key may already be stored in the method-specific private
+ * data or this function may derive the key.
+ */
+ u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+
+ /**
+ * get_status - Get EAP method status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf
+ *
+ * Query EAP method for status information. This function fills in a
+ * text area with current status information from the EAP method. If
+ * the buffer (buf) is not large enough, status information will be
+ * truncated to fit the buffer.
+ */
+ int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose);
+
+ /**
+ * has_reauth_data - Whether method is ready for fast reauthentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * Returns: %TRUE or %FALSE based on whether fast reauthentication is
+ * possible
+ *
+ * This function is an optional handler that only EAP methods
+ * supporting fast re-authentication need to implement.
+ */
+ Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv);
+
+ /**
+ * deinit_for_reauth - Release data that is not needed for fast re-auth
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ *
+ * This function is an optional handler that only EAP methods
+ * supporting fast re-authentication need to implement. This is called
+ * when authentication has been completed and EAP state machine is
+ * requesting that enough state information is maintained for fast
+ * re-authentication
+ */
+ void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
+
+ /**
+ * init_for_reauth - Prepare for start of fast re-authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ *
+ * This function is an optional handler that only EAP methods
+ * supporting fast re-authentication need to implement. This is called
+ * when EAP authentication is started and EAP state machine is
+ * requesting fast re-authentication to be used.
+ */
+ void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
+
+ /**
+ * get_identity - Get method specific identity for re-authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Length of the returned identity
+ * Returns: Pointer to the method specific identity or %NULL if default
+ * identity is to be used
+ *
+ * This function is an optional handler that only EAP methods
+ * that use method specific identity need to implement.
+ */
+ const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
+
+ /**
+ * free - Free EAP method data
+ * @method: Pointer to the method data registered with
+ * eap_peer_method_register().
+ *
+ * This function will be called when the EAP method is being
+ * unregistered. If the EAP method allocated resources during
+ * registration (e.g., allocated struct eap_method), they should be
+ * freed in this function. No other method functions will be called
+ * after this call. If this function is not defined (i.e., function
+ * pointer is %NULL), a default handler is used to release the method
+ * data with free(method). This is suitable for most cases.
+ */
+ void (*free)(struct eap_method *method);
+
+#define EAP_PEER_METHOD_INTERFACE_VERSION 1
+ /**
+ * version - Version of the EAP peer method interface
+ *
+ * The EAP peer method implementation should set this variable to
+ * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the
+ * EAP method is using supported API version when using dynamically
+ * loadable EAP methods.
+ */
+ int version;
+
+ /**
+ * next - Pointer to the next EAP method
+ *
+ * This variable is used internally in the EAP method registration code
+ * to create a linked list of registered EAP methods.
+ */
+ struct eap_method *next;
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ /**
+ * dl_handle - Handle for the dynamic library
+ *
+ * This variable is used internally in the EAP method registration code
+ * to store a handle for the dynamic library. If the method is linked
+ * in statically, this is %NULL.
+ */
+ void *dl_handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+ /**
+ * get_emsk - Get EAP method specific keying extended material (EMSK)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Pointer to a variable to store EMSK length
+ * Returns: EMSK or %NULL if not available
+ *
+ * This function can be used to get the extended keying material from
+ * the EAP method. The key may already be stored in the method-specific
+ * private data or this function may derive the key.
+ */
+ u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
+};
+
+
+/**
+ * struct eap_sm - EAP state machine data
+ */
+struct eap_sm {
+ enum {
+ EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED,
+ EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD,
+ EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS,
+ EAP_FAILURE
+ } EAP_state;
+ /* Long-term local variables */
+ EapType selectedMethod;
+ EapMethodState methodState;
+ int lastId;
+ struct wpabuf *lastRespData;
+ EapDecision decision;
+ /* Short-term local variables */
+ Boolean rxReq;
+ Boolean rxSuccess;
+ Boolean rxFailure;
+ int reqId;
+ EapType reqMethod;
+ int reqVendor;
+ u32 reqVendorMethod;
+ Boolean ignore;
+ /* Constants */
+ int ClientTimeout;
+
+ /* Miscellaneous variables */
+ Boolean allowNotifications; /* peer state machine <-> methods */
+ struct wpabuf *eapRespData; /* peer to lower layer */
+ Boolean eapKeyAvailable; /* peer to lower layer */
+ u8 *eapKeyData; /* peer to lower layer */
+ size_t eapKeyDataLen; /* peer to lower layer */
+ const struct eap_method *m; /* selected EAP method */
+ /* not defined in RFC 4137 */
+ Boolean changed;
+ void *eapol_ctx;
+ struct eapol_callbacks *eapol_cb;
+ void *eap_method_priv;
+ int init_phase2;
+ int fast_reauth;
+
+ Boolean rxResp /* LEAP only */;
+ Boolean leap_done;
+ Boolean peap_done;
+ u8 req_md5[16]; /* MD5() of the current EAP packet */
+ u8 last_md5[16]; /* MD5() of the previously received EAP packet; used
+ * in duplicate request detection. */
+
+ void *msg_ctx;
+ void *scard_ctx;
+ void *ssl_ctx;
+
+ unsigned int workaround;
+
+ /* Optional challenges generated in Phase 1 (EAP-FAST) */
+ u8 *peer_challenge, *auth_challenge;
+
+ int num_rounds;
+ int force_disabled;
+
+ struct wps_context *wps;
+
+ int prev_failure;
+};
+
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash);
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len);
+void eap_clear_config_otp(struct eap_sm *sm);
+const char * eap_get_config_phase1(struct eap_sm *sm);
+const char * eap_get_config_phase2(struct eap_sm *sm);
+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/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c
new file mode 100644
index 0000000..bb49a66
--- /dev/null
+++ b/src/eap_peer/eap_ikev2.c
@@ -0,0 +1,506 @@
+/*
+ * EAP-IKEv2 peer (RFC 5106)
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/eap_ikev2_common.h"
+#include "ikev2.h"
+
+
+struct eap_ikev2_data {
+ struct ikev2_responder_data ikev2;
+ enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state;
+ struct wpabuf *in_buf;
+ struct wpabuf *out_buf;
+ size_t out_used;
+ size_t fragment_size;
+ int keys_ready;
+ u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN];
+ int keymat_ok;
+};
+
+
+static const char * eap_ikev2_state_txt(int state)
+{
+ switch (state) {
+ case WAIT_START:
+ return "WAIT_START";
+ case PROC_MSG:
+ return "PROC_MSG";
+ case WAIT_FRAG_ACK:
+ return "WAIT_FRAG_ACK";
+ case DONE:
+ return "DONE";
+ case FAIL:
+ return "FAIL";
+ default:
+ return "?";
+ }
+}
+
+
+static void eap_ikev2_state(struct eap_ikev2_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s",
+ eap_ikev2_state_txt(data->state),
+ eap_ikev2_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_ikev2_init(struct eap_sm *sm)
+{
+ struct eap_ikev2_data *data;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity == NULL) {
+ wpa_printf(MSG_INFO, "EAP-IKEV2: No identity available");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = WAIT_START;
+ data->fragment_size = IKEV2_FRAGMENT_SIZE;
+ data->ikev2.state = SA_INIT;
+ data->ikev2.peer_auth = PEER_AUTH_SECRET;
+ data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
+ if (data->ikev2.key_pad == NULL)
+ goto failed;
+ data->ikev2.key_pad_len = 21;
+ data->ikev2.IDr = os_malloc(identity_len);
+ if (data->ikev2.IDr == NULL)
+ goto failed;
+ os_memcpy(data->ikev2.IDr, identity, identity_len);
+ data->ikev2.IDr_len = identity_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (password) {
+ data->ikev2.shared_secret = os_malloc(password_len);
+ if (data->ikev2.shared_secret == NULL)
+ goto failed;
+ os_memcpy(data->ikev2.shared_secret, password, password_len);
+ data->ikev2.shared_secret_len = password_len;
+ }
+
+ return data;
+
+failed:
+ ikev2_responder_deinit(&data->ikev2);
+ os_free(data);
+ return NULL;
+}
+
+
+static void eap_ikev2_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_ikev2_data *data = priv;
+ wpabuf_free(data->in_buf);
+ wpabuf_free(data->out_buf);
+ ikev2_responder_deinit(&data->ikev2);
+ os_free(data);
+}
+
+
+static int eap_ikev2_peer_keymat(struct eap_ikev2_data *data)
+{
+ if (eap_ikev2_derive_keymat(
+ data->ikev2.proposal.prf, &data->ikev2.keys,
+ data->ikev2.i_nonce, data->ikev2.i_nonce_len,
+ data->ikev2.r_nonce, data->ikev2.r_nonce_len,
+ data->keymat) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to "
+ "derive key material");
+ return -1;
+ }
+ data->keymat_ok = 1;
+ return 0;
+}
+
+
+static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data,
+ struct eap_method_ret *ret, u8 id)
+{
+ struct wpabuf *resp;
+ u8 flags;
+ size_t send_len, plen, icv_len = 0;
+
+ ret->ignore = FALSE;
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Response");
+ ret->allowNotifications = TRUE;
+
+ flags = 0;
+ send_len = wpabuf_len(data->out_buf) - data->out_used;
+ if (1 + send_len > data->fragment_size) {
+ send_len = data->fragment_size - 1;
+ flags |= IKEV2_FLAGS_MORE_FRAGMENTS;
+ if (data->out_used == 0) {
+ flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
+ send_len -= 4;
+ }
+ }
+#ifdef CCNS_PL
+ /* Some issues figuring out the length of the message if Message Length
+ * field not included?! */
+ if (!(flags & IKEV2_FLAGS_LENGTH_INCLUDED))
+ flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
+#endif /* CCNS_PL */
+
+ plen = 1 + send_len;
+ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+ plen += 4;
+ if (data->keys_ready) {
+ const struct ikev2_integ_alg *integ;
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum "
+ "Data");
+ flags |= IKEV2_FLAGS_ICV_INCLUDED;
+ integ = ikev2_get_integ(data->ikev2.proposal.integ);
+ if (integ == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
+ "transform / cannot generate ICV");
+ return NULL;
+ }
+ icv_len = integ->hash_len;
+
+ plen += icv_len;
+ }
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, flags); /* Flags */
+ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+ wpabuf_put_be32(resp, wpabuf_len(data->out_buf));
+
+ wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
+ send_len);
+ data->out_used += send_len;
+
+ if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+ const u8 *msg = wpabuf_head(resp);
+ size_t len = wpabuf_len(resp);
+ ikev2_integ_hash(data->ikev2.proposal.integ,
+ data->ikev2.keys.SK_ar,
+ data->ikev2.keys.SK_integ_len,
+ msg, len, wpabuf_put(resp, icv_len));
+ }
+
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+
+ if (data->out_used == wpabuf_len(data->out_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+ "(message sent completely)",
+ (unsigned long) send_len);
+ wpabuf_free(data->out_buf);
+ data->out_buf = NULL;
+ data->out_used = 0;
+ switch (data->ikev2.state) {
+ case SA_AUTH:
+ /* SA_INIT was sent out, so message have to be
+ * integrity protected from now on. */
+ data->keys_ready = 1;
+ break;
+ case IKEV2_DONE:
+ ret->methodState = METHOD_DONE;
+ if (data->state == FAIL)
+ break;
+ ret->decision = DECISION_COND_SUCC;
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication "
+ "completed successfully");
+ if (eap_ikev2_peer_keymat(data))
+ break;
+ eap_ikev2_state(data, DONE);
+ break;
+ case IKEV2_FAILED:
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication "
+ "failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ break;
+ default:
+ break;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+ "(%lu more to send)", (unsigned long) send_len,
+ (unsigned long) wpabuf_len(data->out_buf) -
+ data->out_used);
+ eap_ikev2_state(data, WAIT_FRAG_ACK);
+ }
+
+ return resp;
+}
+
+
+static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
+ const struct wpabuf *reqData,
+ u8 flags, const u8 *pos, const u8 **end)
+{
+ if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+ int icv_len = eap_ikev2_validate_icv(
+ data->ikev2.proposal.integ, &data->ikev2.keys, 1,
+ reqData, pos, *end);
+ if (icv_len < 0)
+ return -1;
+ /* Hide Integrity Checksum Data from further processing */
+ *end -= icv_len;
+ } else if (data->keys_ready) {
+ wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
+ "included integrity checksum");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_ikev2_process_cont(struct eap_ikev2_data *data,
+ const u8 *buf, size_t len)
+{
+ /* Process continuation of a pending message */
+ if (len > wpabuf_tailroom(data->in_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
+ eap_ikev2_state(data, FAIL);
+ return -1;
+ }
+
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting "
+ "for %lu bytes more", (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data,
+ struct eap_method_ret *ret,
+ u8 id, u8 flags,
+ u32 message_length,
+ const u8 *buf, size_t len)
+{
+ /* Process a fragment that is not the last one of the message */
+ if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in "
+ "a fragmented packet");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->in_buf == NULL) {
+ /* First fragment of the message */
+ data->in_buf = wpabuf_alloc(message_length);
+ if (data->in_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
+ "message");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first "
+ "fragment, waiting for %lu bytes more",
+ (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+ }
+
+ return eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE);
+}
+
+
+static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_ikev2_data *data = priv;
+ const u8 *start, *pos, *end;
+ size_t len;
+ u8 flags, id;
+ u32 message_length = 0;
+ struct wpabuf tmpbuf;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, reqData, &len);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ id = eap_get_id(reqData);
+
+ start = pos;
+ end = start + len;
+
+ if (len == 0)
+ flags = 0; /* fragment ack */
+ else
+ flags = *pos++;
+
+ if (eap_ikev2_process_icv(data, reqData, flags, pos, &end) < 0) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ message_length = WPA_GET_BE32(pos);
+ pos += 4;
+
+ if (message_length < (u32) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message "
+ "Length (%d; %ld remaining in this msg)",
+ message_length, (long) (end - pos));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
+ "Message Length %u", flags, message_length);
+
+ if (data->state == WAIT_FRAG_ACK) {
+#ifdef CCNS_PL
+ if (len > 1) /* Empty Flags field included in ACK */
+#else /* CCNS_PL */
+ if (len != 0)
+#endif /* CCNS_PL */
+ {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
+ "in WAIT_FRAG_ACK state");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
+ eap_ikev2_state(data, PROC_MSG);
+ return eap_ikev2_build_msg(data, ret, id);
+ }
+
+ if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
+ return eap_ikev2_process_fragment(data, ret, id, flags,
+ message_length, pos,
+ end - pos);
+ }
+
+ if (data->in_buf == NULL) {
+ /* Wrap unfragmented messages as wpabuf without extra copy */
+ wpabuf_set(&tmpbuf, pos, end - pos);
+ data->in_buf = &tmpbuf;
+ }
+
+ if (ikev2_responder_process(&data->ikev2, data->in_buf) < 0) {
+ if (data->in_buf == &tmpbuf)
+ data->in_buf = NULL;
+ eap_ikev2_state(data, FAIL);
+ return NULL;
+ }
+
+ if (data->in_buf != &tmpbuf)
+ wpabuf_free(data->in_buf);
+ data->in_buf = NULL;
+
+ if (data->out_buf == NULL) {
+ data->out_buf = ikev2_responder_build(&data->ikev2);
+ if (data->out_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to generate "
+ "IKEv2 message");
+ return NULL;
+ }
+ data->out_used = 0;
+ }
+
+ eap_ikev2_state(data, PROC_MSG);
+ return eap_ikev2_build_msg(data, ret, id);
+}
+
+
+static Boolean eap_ikev2_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_ikev2_data *data = priv;
+ return data->state == DONE && data->keymat_ok;
+}
+
+
+static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ikev2_data *data = priv;
+ u8 *key;
+
+ if (data->state != DONE || !data->keymat_ok)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key) {
+ os_memcpy(key, data->keymat, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+ }
+
+ return key;
+}
+
+
+static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ikev2_data *data = priv;
+ u8 *key;
+
+ if (data->state != DONE || !data->keymat_ok)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key) {
+ os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+ }
+
+ return key;
+}
+
+
+int eap_peer_ikev2_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
+ "IKEV2");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_ikev2_init;
+ eap->deinit = eap_ikev2_deinit;
+ eap->process = eap_ikev2_process;
+ eap->isKeyAvailable = eap_ikev2_isKeyAvailable;
+ eap->getKey = eap_ikev2_getKey;
+ eap->get_emsk = eap_ikev2_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c
new file mode 100644
index 0000000..6a8efcd
--- /dev/null
+++ b/src/eap_peer/eap_leap.c
@@ -0,0 +1,416 @@
+/*
+ * EAP peer method: LEAP
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/ms_funcs.h"
+#include "crypto/crypto.h"
+#include "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/src/eap_peer/eap_md5.c b/src/eap_peer/eap_md5.c
new file mode 100644
index 0000000..0edbae8
--- /dev/null
+++ b/src/eap_peer/eap_md5.c
@@ -0,0 +1,120 @@
+/*
+ * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994)
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/chap.h"
+
+
+static void * eap_md5_init(struct eap_sm *sm)
+{
+ /* No need for private data. However, must return non-NULL to indicate
+ * success. */
+ return (void *) 1;
+}
+
+
+static void eap_md5_deinit(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct wpabuf *resp;
+ const u8 *pos, *challenge, *password;
+ u8 *rpos, id;
+ size_t len, challenge_len, password_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Password not configured");
+ eap_sm_request_password(sm);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, reqData, &len);
+ if (pos == NULL || len == 0) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame (pos=%p len=%lu)",
+ pos, (unsigned long) len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ /*
+ * CHAP Challenge:
+ * Value-Size (1 octet) | Value(Challenge) | Name(optional)
+ */
+ challenge_len = *pos++;
+ if (challenge_len == 0 || challenge_len > len - 1) {
+ wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge "
+ "(challenge_len=%lu len=%lu)",
+ (unsigned long) challenge_len, (unsigned long) len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ ret->ignore = FALSE;
+ challenge = pos;
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge",
+ challenge, challenge_len);
+
+ wpa_printf(MSG_DEBUG, "EAP-MD5: Generating Challenge Response");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = TRUE;
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHAP_MD5_LEN,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+
+ /*
+ * CHAP Response:
+ * Value-Size (1 octet) | Value(Response) | Name(optional)
+ */
+ wpabuf_put_u8(resp, CHAP_MD5_LEN);
+
+ id = eap_get_id(resp);
+ rpos = wpabuf_put(resp, CHAP_MD5_LEN);
+ chap_md5(id, password, password_len, challenge, challenge_len, rpos);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN);
+
+ return resp;
+}
+
+
+int eap_peer_md5_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_md5_init;
+ eap->deinit = eap_md5_deinit;
+ eap->process = eap_md5_process;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c
new file mode 100644
index 0000000..3b0af05
--- /dev/null
+++ b/src/eap_peer/eap_methods.c
@@ -0,0 +1,373 @@
+/*
+ * EAP peer: Method registration
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+#include <dlfcn.h>
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_methods.h"
+
+
+static struct eap_method *eap_methods = NULL;
+
+
+/**
+ * eap_peer_get_eap_method - Get EAP method based on type number
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @method: EAP type number
+ * Returns: Pointer to EAP method or %NULL if not found
+ */
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method)
+{
+ struct eap_method *m;
+ for (m = eap_methods; m; m = m->next) {
+ if (m->vendor == vendor && m->method == method)
+ return m;
+ }
+ return NULL;
+}
+
+
+/**
+ * eap_peer_get_type - Get EAP type for the given EAP method name
+ * @name: EAP method name, e.g., TLS
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers based on the list of
+ * EAP methods included in the build.
+ */
+EapType eap_peer_get_type(const char *name, int *vendor)
+{
+ struct eap_method *m;
+ for (m = eap_methods; m; m = m->next) {
+ if (os_strcmp(m->name, name) == 0) {
+ *vendor = m->vendor;
+ return m->method;
+ }
+ }
+ *vendor = EAP_VENDOR_IETF;
+ return EAP_TYPE_NONE;
+}
+
+
+/**
+ * eap_get_name - Get EAP method name for the given EAP type
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @type: EAP method type
+ * Returns: EAP method name, e.g., TLS, or %NULL if not found
+ *
+ * This function maps EAP type numbers into EAP type names based on the list of
+ * EAP methods included in the build.
+ */
+const char * eap_get_name(int vendor, EapType type)
+{
+ struct eap_method *m;
+ for (m = eap_methods; m; m = m->next) {
+ if (m->vendor == vendor && m->method == type)
+ return m->name;
+ }
+ return NULL;
+}
+
+
+/**
+ * eap_get_names - Get space separated list of names for supported EAP methods
+ * @buf: Buffer for names
+ * @buflen: Buffer length
+ * Returns: Number of characters written into buf (not including nul
+ * termination)
+ */
+size_t eap_get_names(char *buf, size_t buflen)
+{
+ char *pos, *end;
+ struct eap_method *m;
+ int ret;
+
+ if (buflen == 0)
+ return 0;
+
+ pos = buf;
+ end = pos + buflen;
+
+ for (m = eap_methods; m; m = m->next) {
+ ret = os_snprintf(pos, end - pos, "%s%s",
+ m == eap_methods ? "" : " ", m->name);
+ if (ret < 0 || ret >= end - pos)
+ break;
+ pos += ret;
+ }
+ buf[buflen - 1] = '\0';
+
+ return pos - buf;
+}
+
+
+/**
+ * eap_get_names_as_string_array - Get supported EAP methods as string array
+ * @num: Buffer for returning the number of items in array, not including %NULL
+ * terminator. This parameter can be %NULL if the length is not needed.
+ * Returns: A %NULL-terminated array of strings, or %NULL on error.
+ *
+ * This function returns the list of names for all supported EAP methods as an
+ * array of strings. The caller must free the returned array items and the
+ * array.
+ */
+char ** eap_get_names_as_string_array(size_t *num)
+{
+ struct eap_method *m;
+ size_t array_len = 0;
+ char **array;
+ int i = 0, j;
+
+ for (m = eap_methods; m; m = m->next)
+ array_len++;
+
+ array = os_zalloc(sizeof(char *) * (array_len + 1));
+ if (array == NULL)
+ return NULL;
+
+ for (m = eap_methods; m; m = m->next) {
+ array[i++] = os_strdup(m->name);
+ if (array[i - 1] == NULL) {
+ for (j = 0; j < i; j++)
+ os_free(array[j]);
+ os_free(array);
+ return NULL;
+ }
+ }
+ array[i] = NULL;
+
+ if (num)
+ *num = array_len;
+
+ return array;
+}
+
+
+/**
+ * eap_peer_get_methods - Get a list of enabled EAP peer methods
+ * @count: Set to number of available methods
+ * Returns: List of enabled EAP peer methods
+ */
+const struct eap_method * eap_peer_get_methods(size_t *count)
+{
+ int c = 0;
+ struct eap_method *m;
+
+ for (m = eap_methods; m; m = m->next)
+ c++;
+
+ *count = c;
+ return eap_methods;
+}
+
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+/**
+ * eap_peer_method_load - Load a dynamic EAP method library (shared object)
+ * @so: File path for the shared object file to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_method_load(const char *so)
+{
+ void *handle;
+ int (*dyn_init)(void);
+ int ret;
+
+ handle = dlopen(so, RTLD_LAZY);
+ if (handle == NULL) {
+ wpa_printf(MSG_ERROR, "EAP: Failed to open dynamic EAP method "
+ "'%s': %s", so, dlerror());
+ return -1;
+ }
+
+ dyn_init = dlsym(handle, "eap_peer_method_dynamic_init");
+ if (dyn_init == NULL) {
+ dlclose(handle);
+ wpa_printf(MSG_ERROR, "EAP: Invalid EAP method '%s' - no "
+ "eap_peer_method_dynamic_init()", so);
+ return -1;
+ }
+
+ ret = dyn_init();
+ if (ret) {
+ dlclose(handle);
+ wpa_printf(MSG_ERROR, "EAP: Failed to add EAP method '%s' - "
+ "ret %d", so, ret);
+ return ret;
+ }
+
+ /* Store the handle for this shared object. It will be freed with
+ * dlclose() when the EAP method is unregistered. */
+ eap_methods->dl_handle = handle;
+
+ wpa_printf(MSG_DEBUG, "EAP: Loaded dynamic EAP method: '%s'", so);
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_method_unload - Unload a dynamic EAP method library (shared object)
+ * @method: Pointer to the dynamically loaded EAP method
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to unload EAP methods that have been previously
+ * loaded with eap_peer_method_load(). Before unloading the method, all
+ * references to the method must be removed to make sure that no dereferences
+ * of freed memory will occur after unloading.
+ */
+int eap_peer_method_unload(struct eap_method *method)
+{
+ struct eap_method *m, *prev;
+ void *handle;
+
+ m = eap_methods;
+ prev = NULL;
+ while (m) {
+ if (m == method)
+ break;
+ prev = m;
+ m = m->next;
+ }
+
+ if (m == NULL || m->dl_handle == NULL)
+ return -1;
+
+ if (prev)
+ prev->next = m->next;
+ else
+ eap_methods = m->next;
+
+ handle = m->dl_handle;
+
+ if (m->free)
+ m->free(m);
+ else
+ eap_peer_method_free(m);
+
+ dlclose(handle);
+
+ return 0;
+}
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+
+/**
+ * eap_peer_method_alloc - Allocate EAP peer method structure
+ * @version: Version of the EAP peer method interface (set to
+ * EAP_PEER_METHOD_INTERFACE_VERSION)
+ * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+ * @method: EAP type number (EAP_TYPE_*)
+ * @name: Name of the method (e.g., "TLS")
+ * Returns: Allocated EAP method structure or %NULL on failure
+ *
+ * The returned structure should be freed with eap_peer_method_free() when it
+ * is not needed anymore.
+ */
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+ EapType method, const char *name)
+{
+ struct eap_method *eap;
+ eap = os_zalloc(sizeof(*eap));
+ if (eap == NULL)
+ return NULL;
+ eap->version = version;
+ eap->vendor = vendor;
+ eap->method = method;
+ eap->name = name;
+ return eap;
+}
+
+
+/**
+ * eap_peer_method_free - Free EAP peer method structure
+ * @method: Method structure allocated with eap_peer_method_alloc()
+ */
+void eap_peer_method_free(struct eap_method *method)
+{
+ os_free(method);
+}
+
+
+/**
+ * eap_peer_method_register - Register an EAP peer method
+ * @method: EAP method to register
+ * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method
+ * has already been registered
+ *
+ * Each EAP peer method needs to call this function to register itself as a
+ * supported EAP method.
+ */
+int eap_peer_method_register(struct eap_method *method)
+{
+ struct eap_method *m, *last = NULL;
+
+ if (method == NULL || method->name == NULL ||
+ method->version != EAP_PEER_METHOD_INTERFACE_VERSION)
+ return -1;
+
+ for (m = eap_methods; m; m = m->next) {
+ if ((m->vendor == method->vendor &&
+ m->method == method->method) ||
+ os_strcmp(m->name, method->name) == 0)
+ return -2;
+ last = m;
+ }
+
+ if (last)
+ last->next = method;
+ else
+ eap_methods = method;
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_unregister_methods - Unregister EAP peer methods
+ *
+ * This function is called at program termination to unregister all EAP peer
+ * methods.
+ */
+void eap_peer_unregister_methods(void)
+{
+ struct eap_method *m;
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ void *handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+ while (eap_methods) {
+ m = eap_methods;
+ eap_methods = eap_methods->next;
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ handle = m->dl_handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+ if (m->free)
+ m->free(m);
+ else
+ eap_peer_method_free(m);
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+ if (handle)
+ dlclose(handle);
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+ }
+}
diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h
new file mode 100644
index 0000000..4330b57
--- /dev/null
+++ b/src/eap_peer/eap_methods.h
@@ -0,0 +1,114 @@
+/*
+ * EAP peer: Method registration
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_METHODS_H
+#define EAP_METHODS_H
+
+#include "eap_common/eap_defs.h"
+
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method);
+const struct eap_method * eap_peer_get_methods(size_t *count);
+
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+ EapType method, const char *name);
+void eap_peer_method_free(struct eap_method *method);
+int eap_peer_method_register(struct eap_method *method);
+
+
+#ifdef IEEE8021X_EAPOL
+
+EapType eap_peer_get_type(const char *name, int *vendor);
+const char * eap_get_name(int vendor, EapType type);
+size_t eap_get_names(char *buf, size_t buflen);
+char ** eap_get_names_as_string_array(size_t *num);
+void eap_peer_unregister_methods(void);
+
+#else /* IEEE8021X_EAPOL */
+
+static inline EapType eap_peer_get_type(const char *name, int *vendor)
+{
+ *vendor = EAP_VENDOR_IETF;
+ return EAP_TYPE_NONE;
+}
+
+static inline const char * eap_get_name(int vendor, EapType type)
+{
+ return NULL;
+}
+
+static inline size_t eap_get_names(char *buf, size_t buflen)
+{
+ return 0;
+}
+
+static inline int eap_peer_register_methods(void)
+{
+ return 0;
+}
+
+static inline void eap_peer_unregister_methods(void)
+{
+}
+
+static inline char ** eap_get_names_as_string_array(size_t *num)
+{
+ return NULL;
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+
+int eap_peer_method_load(const char *so);
+int eap_peer_method_unload(struct eap_method *method);
+
+#else /* CONFIG_DYNAMIC_EAP_METHODS */
+
+static inline int eap_peer_method_load(const char *so)
+{
+ return 0;
+}
+
+static inline int eap_peer_method_unload(struct eap_method *method)
+{
+ return 0;
+}
+
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+/* EAP peer method registration calls for statically linked in methods */
+int eap_peer_md5_register(void);
+int eap_peer_tls_register(void);
+int eap_peer_mschapv2_register(void);
+int eap_peer_peap_register(void);
+int eap_peer_ttls_register(void);
+int eap_peer_gtc_register(void);
+int eap_peer_otp_register(void);
+int eap_peer_sim_register(void);
+int eap_peer_leap_register(void);
+int eap_peer_psk_register(void);
+int eap_peer_aka_register(void);
+int eap_peer_aka_prime_register(void);
+int eap_peer_fast_register(void);
+int eap_peer_pax_register(void);
+int eap_peer_sake_register(void);
+int eap_peer_gpsk_register(void);
+int eap_peer_wsc_register(void);
+int eap_peer_ikev2_register(void);
+int eap_peer_vendor_test_register(void);
+int eap_peer_tnc_register(void);
+int eap_peer_pwd_register(void);
+
+#endif /* EAP_METHODS_H */
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
new file mode 100644
index 0000000..321e9f7
--- /dev/null
+++ b/src/eap_peer/eap_mschapv2.c
@@ -0,0 +1,883 @@
+/*
+ * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26).
+ * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP
+ * Extensions Protocol, Version 2, for mutual authentication and key
+ * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in
+ * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in
+ * RFC 3079.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/ms_funcs.h"
+#include "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_PASSWORD_NTHASH) {
+ config->password = os_malloc(16);
+ config->password_len = 16;
+ if (config->password) {
+ nt_password_hash(config->new_password,
+ config->new_password_len,
+ config->password);
+ }
+ os_free(config->new_password);
+ } else {
+ config->password = config->new_password;
+ config->password_len = config->new_password_len;
+ }
+ config->new_password = NULL;
+ config->new_password_len = 0;
+ }
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in th erequest
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_mschapv2_hdr *req,
+ size_t req_len, u8 id)
+{
+ struct wpabuf *resp;
+ const u8 *pos;
+ size_t len;
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
+ len = req_len - sizeof(*req);
+ pos = (const u8 *) (req + 1);
+ if (!data->auth_response_valid ||
+ mschapv2_verify_auth_response(data->auth_response, pos, len)) {
+ wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
+ "response in success request");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
+ len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
+ while (len > 0 && *pos == ' ') {
+ pos++;
+ len--;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
+ pos, len);
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
+
+ /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
+ * message. */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
+ "buffer for success response");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->allowNotifications = FALSE;
+ data->success = 1;
+
+ if (data->prev_error == ERROR_PASSWD_EXPIRED)
+ eap_mschapv2_password_changed(sm, data);
+
+ return resp;
+}
+
+
+static int eap_mschapv2_failure_txt(struct eap_sm *sm,
+ struct eap_mschapv2_data *data, char *txt)
+{
+ char *pos, *msg = "";
+ int retry = 1;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ /* For example:
+ * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
+ */
+
+ pos = txt;
+
+ if (pos && os_strncmp(pos, "E=", 2) == 0) {
+ pos += 2;
+ data->prev_error = atoi(pos);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
+ data->prev_error);
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && os_strncmp(pos, "R=", 2) == 0) {
+ pos += 2;
+ retry = atoi(pos);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
+ retry == 1 ? "" : "not ");
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && os_strncmp(pos, "C=", 2) == 0) {
+ int hex_len;
+ pos += 2;
+ hex_len = os_strchr(pos, ' ') - (char *) pos;
+ if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
+ if (hexstr2bin(pos, data->passwd_change_challenge,
+ PASSWD_CHANGE_CHAL_LEN)) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
+ "failure challenge");
+ } else {
+ wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
+ "challenge",
+ data->passwd_change_challenge,
+ PASSWD_CHANGE_CHAL_LEN);
+ data->passwd_change_challenge_valid = 1;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
+ "challenge len %d", hex_len);
+ }
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
+ "was not present in failure message");
+ }
+
+ if (pos && os_strncmp(pos, "V=", 2) == 0) {
+ pos += 2;
+ data->passwd_change_version = atoi(pos);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
+ "protocol version %d", data->passwd_change_version);
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ if (pos && os_strncmp(pos, "M=", 2) == 0) {
+ pos += 2;
+ msg = pos;
+ }
+ wpa_msg(sm->msg_ctx, MSG_WARNING,
+ "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
+ "%d)",
+ msg, retry == 1 ? "" : "not ", data->prev_error);
+ if (data->prev_error == ERROR_PASSWD_EXPIRED &&
+ data->passwd_change_version == 3 && config) {
+ if (config->new_password == NULL) {
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ "EAP-MSCHAPV2: Password expired - password "
+ "change required");
+ eap_sm_request_new_password(sm);
+ }
+ } else if (retry == 1 && config) {
+ /* TODO: could prevent the current password from being used
+ * again at least for some period of time */
+ if (!config->mschapv2_retry)
+ eap_sm_request_identity(sm);
+ eap_sm_request_password(sm);
+ config->mschapv2_retry = 1;
+ } else if (config) {
+ /* TODO: prevent retries using same username/password */
+ config->mschapv2_retry = 0;
+ }
+
+ return retry == 1;
+}
+
+
+static struct wpabuf * eap_mschapv2_change_password(
+ struct eap_sm *sm, struct eap_mschapv2_data *data,
+ struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
+{
+ struct wpabuf *resp;
+ int ms_len;
+ const u8 *username, *password, *new_password;
+ size_t username_len, password_len, new_password_len;
+ struct eap_mschapv2_hdr *ms;
+ struct ms_change_password *cp;
+ u8 password_hash[16], password_hash_hash[16];
+ int pwhash;
+
+ username = eap_get_config_identity(sm, &username_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ new_password = eap_get_config_new_password(sm, &new_password_len);
+ if (username == NULL || password == NULL || new_password == NULL)
+ return NULL;
+
+ username = mschapv2_remove_domain(username, &username_len);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = TRUE;
+
+ ms_len = sizeof(*ms) + sizeof(*cp);
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ ms = wpabuf_put(resp, sizeof(*ms));
+ ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
+ ms->mschapv2_id = req->mschapv2_id + 1;
+ WPA_PUT_BE16(ms->ms_length, ms_len);
+ cp = wpabuf_put(resp, sizeof(*cp));
+
+ /* Encrypted-Password */
+ if (pwhash) {
+ if (encrypt_pw_block_with_password_hash(
+ new_password, new_password_len,
+ password, cp->encr_password))
+ goto fail;
+ } else {
+ if (new_password_encrypted_with_old_nt_password_hash(
+ new_password, new_password_len,
+ password, password_len, cp->encr_password))
+ goto fail;
+ }
+
+ /* Encrypted-Hash */
+ if (pwhash) {
+ u8 new_password_hash[16];
+ nt_password_hash(new_password, new_password_len,
+ new_password_hash);
+ nt_password_hash_encrypted_with_block(password,
+ new_password_hash,
+ cp->encr_hash);
+ } else {
+ old_nt_password_hash_encrypted_with_new_nt_password_hash(
+ new_password, new_password_len,
+ password, password_len, cp->encr_hash);
+ }
+
+ /* Peer-Challenge */
+ if (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 = os_malloc(len + 1);
+ if (buf) {
+ os_memcpy(buf, msdata, len);
+ buf[len] = '\0';
+ retry = eap_mschapv2_failure_txt(sm, data, buf);
+ os_free(buf);
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+
+ if (data->prev_error == ERROR_PASSWD_EXPIRED &&
+ data->passwd_change_version == 3) {
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config && config->new_password)
+ return eap_mschapv2_change_password(sm, data, ret, req,
+ id);
+ if (config && config->pending_req_new_password)
+ return NULL;
+ } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+ /* TODO: could try to retry authentication, e.g, after having
+ * changed the username/password. In this case, EAP MS-CHAP-v2
+ * Failure Response would not be sent here. */
+ return NULL;
+ }
+
+ /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
+ * message. */
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
+
+ return resp;
+}
+
+
+static int eap_mschapv2_check_config(struct eap_sm *sm)
+{
+ size_t len;
+
+ if (eap_get_config_identity(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
+ eap_sm_request_identity(sm);
+ return -1;
+ }
+
+ if (eap_get_config_password(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
+ eap_sm_request_password(sm);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
+ const struct eap_mschapv2_hdr *ms)
+{
+ size_t ms_len = WPA_GET_BE16(ms->ms_length);
+
+ if (ms_len == len)
+ return 0;
+
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
+ "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
+ if (sm->workaround) {
+ /* Some authentication servers use invalid ms_len,
+ * ignore it for interoperability. */
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
+ " invalid ms_len %lu (len %lu)",
+ (unsigned long) ms_len,
+ (unsigned long) len);
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
+ const struct wpabuf *reqData)
+{
+ /*
+ * Store a copy of the challenge message, so that it can be processed
+ * again in case retry is allowed after a possible failure.
+ */
+ wpabuf_free(data->prev_challenge);
+ data->prev_challenge = wpabuf_dup(reqData);
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_mschapv2_data *data = priv;
+ struct eap_peer_config *config = eap_get_config(sm);
+ const struct eap_mschapv2_hdr *ms;
+ int using_prev_challenge = 0;
+ const u8 *pos;
+ size_t len;
+ u8 id;
+
+ if (eap_mschapv2_check_config(sm)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (config->mschapv2_retry && data->prev_challenge &&
+ data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
+ "with the previous challenge");
+
+ reqData = data->prev_challenge;
+ using_prev_challenge = 1;
+ config->mschapv2_retry = 0;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
+ &len);
+ if (pos == NULL || len < sizeof(*ms) + 1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ms = (const struct eap_mschapv2_hdr *) pos;
+ if (eap_mschapv2_check_mslen(sm, len, ms)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ id = eap_get_id(reqData);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
+ id, ms->mschapv2_id);
+
+ switch (ms->op_code) {
+ case MSCHAPV2_OP_CHALLENGE:
+ if (!using_prev_challenge)
+ eap_mschapv2_copy_challenge(data, reqData);
+ return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
+ case MSCHAPV2_OP_SUCCESS:
+ return eap_mschapv2_success(sm, data, ret, ms, len, id);
+ case MSCHAPV2_OP_FAILURE:
+ return eap_mschapv2_failure(sm, data, ret, ms, len, id);
+ default:
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
+ ms->op_code);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+}
+
+
+static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_mschapv2_data *data = priv;
+ return data->success && data->master_key_valid;
+}
+
+
+static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_mschapv2_data *data = priv;
+ u8 *key;
+ int key_len;
+
+ if (!data->master_key_valid || !data->success)
+ return NULL;
+
+ key_len = 2 * MSCHAPV2_KEY_LEN;
+
+ key = os_malloc(key_len);
+ if (key == NULL)
+ return NULL;
+
+ /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
+ * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
+ get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0);
+ get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
+ MSCHAPV2_KEY_LEN, 0, 0);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
+ key, key_len);
+
+ *len = key_len;
+ return key;
+}
+
+
+/**
+ * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to register EAP-MSCHAPv2 peer method into the EAP
+ * method list.
+ */
+int eap_peer_mschapv2_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
+ "MSCHAPV2");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_mschapv2_init;
+ eap->deinit = eap_mschapv2_deinit;
+ eap->process = eap_mschapv2_process;
+ eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
+ eap->getKey = eap_mschapv2_getKey;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_otp.c b/src/eap_peer/eap_otp.c
new file mode 100644
index 0000000..556c22f
--- /dev/null
+++ b/src/eap_peer/eap_otp.c
@@ -0,0 +1,107 @@
+/*
+ * EAP peer method: EAP-OTP (RFC 3748)
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+static void * eap_otp_init(struct eap_sm *sm)
+{
+ /* No need for private data. However, must return non-NULL to indicate
+ * success. */
+ return (void *) 1;
+}
+
+
+static void eap_otp_deinit(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static struct wpabuf * eap_otp_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct wpabuf *resp;
+ const u8 *pos, *password;
+ size_t password_len, len;
+ int otp;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_OTP, reqData, &len);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message",
+ pos, len);
+
+ password = eap_get_config_otp(sm, &password_len);
+ if (password)
+ otp = 1;
+ else {
+ password = eap_get_config_password(sm, &password_len);
+ otp = 0;
+ }
+
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-OTP: Password not configured");
+ eap_sm_request_otp(sm, (const char *) pos, len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ ret->allowNotifications = FALSE;
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_OTP, password_len,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+ wpabuf_put_data(resp, password, password_len);
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response",
+ password, password_len);
+
+ if (otp) {
+ wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password");
+ eap_clear_config_otp(sm);
+ }
+
+ return resp;
+}
+
+
+int eap_peer_otp_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_OTP, "OTP");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_otp_init;
+ eap->deinit = eap_otp_deinit;
+ eap->process = eap_otp_process;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c
new file mode 100644
index 0000000..d42a7f8
--- /dev/null
+++ b/src/eap_peer/eap_pax.c
@@ -0,0 +1,531 @@
+/*
+ * EAP peer method: EAP-PAX (RFC 4746)
+ * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "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/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
new file mode 100644
index 0000000..2b72084
--- /dev/null
+++ b/src/eap_peer/eap_peap.c
@@ -0,0 +1,1288 @@
+/*
+ * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_peap_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "tncc.h"
+
+
+/* Maximum supported PEAP version
+ * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
+ * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
+ * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt
+ */
+#define EAP_PEAP_VERSION 1
+
+
+static void eap_peap_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_peap_data {
+ struct eap_ssl_data ssl;
+
+ int peap_version, force_peap_version, force_new_label;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+ int phase2_eap_success;
+ int phase2_eap_started;
+
+ struct eap_method_type phase2_type;
+ struct eap_method_type *phase2_types;
+ size_t num_phase2_types;
+
+ int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner
+ * EAP-Success
+ * 1 = reply with tunneled EAP-Success to inner
+ * EAP-Success and expect AS to send outer
+ * (unencrypted) EAP-Success after this
+ * 2 = reply with PEAP/TLS ACK to inner
+ * EAP-Success and expect AS to send outer
+ * (unencrypted) EAP-Success after this */
+ int resuming; /* starting a resumed session */
+ int reauth; /* reauthentication */
+ u8 *key_data;
+
+ struct wpabuf *pending_phase2_req;
+ enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
+ int crypto_binding_used;
+ u8 binding_nonce[32];
+ u8 ipmk[40];
+ u8 cmk[20];
+ int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP)
+ * is enabled. */
+};
+
+
+static int eap_peap_parse_phase1(struct eap_peap_data *data,
+ const char *phase1)
+{
+ const char *pos;
+
+ pos = os_strstr(phase1, "peapver=");
+ if (pos) {
+ data->force_peap_version = atoi(pos + 8);
+ data->peap_version = data->force_peap_version;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d",
+ data->force_peap_version);
+ }
+
+ if (os_strstr(phase1, "peaplabel=1")) {
+ data->force_new_label = 1;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key "
+ "derivation");
+ }
+
+ if (os_strstr(phase1, "peap_outer_success=0")) {
+ data->peap_outer_success = 0;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on "
+ "tunneled EAP-Success");
+ } else if (os_strstr(phase1, "peap_outer_success=1")) {
+ data->peap_outer_success = 1;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success "
+ "after receiving tunneled EAP-Success");
+ } else if (os_strstr(phase1, "peap_outer_success=2")) {
+ data->peap_outer_success = 2;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after "
+ "receiving tunneled EAP-Success");
+ }
+
+ if (os_strstr(phase1, "crypto_binding=0")) {
+ data->crypto_binding = NO_BINDING;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding");
+ } else if (os_strstr(phase1, "crypto_binding=1")) {
+ data->crypto_binding = OPTIONAL_BINDING;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding");
+ } else if (os_strstr(phase1, "crypto_binding=2")) {
+ data->crypto_binding = REQUIRE_BINDING;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding");
+ }
+
+#ifdef EAP_TNC
+ if (os_strstr(phase1, "tnc=soh2")) {
+ data->soh = 2;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
+ } else if (os_strstr(phase1, "tnc=soh1")) {
+ data->soh = 1;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled");
+ } else if (os_strstr(phase1, "tnc=soh")) {
+ data->soh = 2;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
+ }
+#endif /* EAP_TNC */
+
+ return 0;
+}
+
+
+static void * eap_peap_init(struct eap_sm *sm)
+{
+ struct eap_peap_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ sm->peap_done = FALSE;
+ data->peap_version = EAP_PEAP_VERSION;
+ data->force_peap_version = -1;
+ data->peap_outer_success = 2;
+ data->crypto_binding = OPTIONAL_BINDING;
+
+ if (config && config->phase1 &&
+ eap_peap_parse_phase1(data, config->phase1) < 0) {
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+
+ if (eap_peer_select_phase2_methods(config, "auth=",
+ &data->phase2_types,
+ &data->num_phase2_types) < 0) {
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+
+ data->phase2_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_type.method = EAP_TYPE_NONE;
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
+ eap_peap_deinit(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_peap_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ if (data == NULL)
+ return;
+ if (data->phase2_priv && data->phase2_method)
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ os_free(data->phase2_types);
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
+ os_free(data->key_data);
+ wpabuf_free(data->pending_phase2_req);
+ os_free(data);
+}
+
+
+/**
+ * eap_tlv_build_nak - Build EAP-TLV NAK message
+ * @id: EAP identifier for the header
+ * @nak_type: TLV type (EAP_TLV_*)
+ * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure
+ *
+ * This funtion builds an EAP-TLV NAK message. The caller is responsible for
+ * freeing the returned buffer.
+ */
+static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type)
+{
+ struct wpabuf *msg;
+
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10,
+ EAP_CODE_RESPONSE, id);
+ if (msg == NULL)
+ return NULL;
+
+ wpabuf_put_u8(msg, 0x80); /* Mandatory */
+ wpabuf_put_u8(msg, EAP_TLV_NAK_TLV);
+ wpabuf_put_be16(msg, 6); /* Length */
+ wpabuf_put_be32(msg, 0); /* Vendor-Id */
+ wpabuf_put_be16(msg, nak_type); /* NAK-Type */
+
+ return msg;
+}
+
+
+static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data,
+ u8 *isk, size_t isk_len)
+{
+ u8 *key;
+ size_t key_len;
+
+ os_memset(isk, 0, isk_len);
+ if (data->phase2_method == NULL || data->phase2_priv == NULL ||
+ data->phase2_method->isKeyAvailable == NULL ||
+ data->phase2_method->getKey == NULL)
+ return 0;
+
+ if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
+ (key = data->phase2_method->getKey(sm, data->phase2_priv,
+ &key_len)) == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material "
+ "from Phase 2");
+ return -1;
+ }
+
+ if (key_len > isk_len)
+ key_len = isk_len;
+ os_memcpy(isk, key, key_len);
+ os_free(key);
+
+ return 0;
+}
+
+
+static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
+{
+ u8 *tk;
+ u8 isk[32], imck[60];
+
+ /*
+ * Tunnel key (TK) is the first 60 octets of the key generated by
+ * phase 1 of PEAP (based on TLS).
+ */
+ tk = data->key_data;
+ if (tk == NULL)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
+
+ if (data->reauth &&
+ tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+ /* Fast-connect: IPMK|CMK = TK */
+ os_memcpy(data->ipmk, tk, 40);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK",
+ data->ipmk, 40);
+ os_memcpy(data->cmk, tk + 40, 20);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK",
+ data->cmk, 20);
+ return 0;
+ }
+
+ if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
+
+ /*
+ * IPMK Seed = "Inner Methods Compound Keys" | ISK
+ * TempKey = First 40 octets of TK
+ * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)
+ * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space
+ * in the end of the label just before ISK; is that just a typo?)
+ */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
+ peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck));
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
+ imck, sizeof(imck));
+
+ os_memcpy(data->ipmk, imck, 40);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
+ os_memcpy(data->cmk, imck + 40, 20);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
+
+ return 0;
+}
+
+
+static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ struct wpabuf *buf)
+{
+ u8 *mac;
+ u8 eap_type = EAP_TYPE_PEAP;
+ const u8 *addr[2];
+ size_t len[2];
+ u16 tlv_type;
+
+ /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
+ addr[0] = wpabuf_put(buf, 0);
+ len[0] = 60;
+ addr[1] = &eap_type;
+ len[1] = 1;
+
+ tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
+ if (data->peap_version >= 2)
+ tlv_type |= EAP_TLV_TYPE_MANDATORY;
+ wpabuf_put_be16(buf, tlv_type);
+ wpabuf_put_be16(buf, 56);
+
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ wpabuf_put_u8(buf, data->peap_version); /* Version */
+ wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */
+ wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */
+ wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
+ mac = wpabuf_put(buf, 20); /* Compound_MAC */
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
+ addr[0], len[0]);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
+ addr[1], len[1]);
+ hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN);
+ data->crypto_binding_used = 1;
+
+ return 0;
+}
+
+
+/**
+ * eap_tlv_build_result - Build EAP-TLV Result message
+ * @id: EAP identifier for the header
+ * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE)
+ * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure
+ *
+ * This funtion builds an EAP-TLV Result message. The caller is responsible for
+ * freeing the returned buffer.
+ */
+static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ int crypto_tlv_used,
+ int id, u16 status)
+{
+ struct wpabuf *msg;
+ size_t len;
+
+ if (data->crypto_binding == NO_BINDING)
+ crypto_tlv_used = 0;
+
+ len = 6;
+ if (crypto_tlv_used)
+ len += 60; /* Cryptobinding TLV */
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len,
+ EAP_CODE_RESPONSE, id);
+ if (msg == NULL)
+ return NULL;
+
+ wpabuf_put_u8(msg, 0x80); /* Mandatory */
+ wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV);
+ wpabuf_put_be16(msg, 2); /* Length */
+ wpabuf_put_be16(msg, status); /* Status */
+
+ if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ const u8 *crypto_tlv,
+ size_t crypto_tlv_len)
+{
+ u8 buf[61], mac[SHA1_MAC_LEN];
+ const u8 *pos;
+
+ if (eap_peap_derive_cmk(sm, data) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK");
+ return -1;
+ }
+
+ if (crypto_tlv_len != 4 + 56) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "
+ "length %d", (int) crypto_tlv_len);
+ return -1;
+ }
+
+ pos = crypto_tlv;
+ pos += 4; /* TLV header */
+ if (pos[1] != data->peap_version) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "
+ "mismatch (was %d; expected %d)",
+ pos[1], data->peap_version);
+ return -1;
+ }
+
+ if (pos[3] != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "
+ "SubType %d", pos[3]);
+ return -1;
+ }
+ pos += 4;
+ os_memcpy(data->binding_nonce, pos, 32);
+ pos += 32; /* Nonce */
+
+ /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
+ os_memcpy(buf, crypto_tlv, 60);
+ os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */
+ buf[60] = EAP_TYPE_PEAP;
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data",
+ buf, sizeof(buf));
+ hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
+
+ if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "
+ "cryptobinding TLV");
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC",
+ pos, SHA1_MAC_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC",
+ mac, SHA1_MAC_LEN);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");
+
+ return 0;
+}
+
+
+/**
+ * eap_tlv_process - Process a received EAP-TLV message and generate a response
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: EAP-TLV request to be processed. The caller must have validated that
+ * the buffer is large enough to contain full request (hdr->length bytes) and
+ * that the EAP type is EAP_TYPE_TLV.
+ * @resp: Buffer to return a pointer to the allocated response message. This
+ * field should be initialized to %NULL before the call. The value will be
+ * updated if a response message is generated. The caller is responsible for
+ * freeing the allocated message.
+ * @force_failure: Force negotiation to fail
+ * Returns: 0 on success, -1 on failure
+ */
+static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *req, struct wpabuf **resp,
+ int force_failure)
+{
+ size_t left, tlv_len;
+ const u8 *pos;
+ const u8 *result_tlv = NULL, *crypto_tlv = NULL;
+ size_t result_tlv_len = 0, crypto_tlv_len = 0;
+ int tlv_type, mandatory;
+
+ /* Parse TLVs */
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left);
+ if (pos == NULL)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);
+ while (left >= 4) {
+ mandatory = !!(pos[0] & 0x80);
+ tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+ pos += 2;
+ tlv_len = WPA_GET_BE16(pos);
+ pos += 2;
+ left -= 4;
+ if (tlv_len > left) {
+ wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "
+ "(tlv_len=%lu left=%lu)",
+ (unsigned long) tlv_len,
+ (unsigned long) left);
+ return -1;
+ }
+ switch (tlv_type) {
+ case EAP_TLV_RESULT_TLV:
+ result_tlv = pos;
+ result_tlv_len = tlv_len;
+ break;
+ case EAP_TLV_CRYPTO_BINDING_TLV:
+ crypto_tlv = pos;
+ crypto_tlv_len = tlv_len;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
+ "%d%s", tlv_type,
+ mandatory ? " (mandatory)" : "");
+ if (mandatory) {
+ /* NAK TLV and ignore all TLVs in this packet.
+ */
+ *resp = eap_tlv_build_nak(eap_get_id(req),
+ tlv_type);
+ return *resp == NULL ? -1 : 0;
+ }
+ /* Ignore this TLV, but process other TLVs */
+ break;
+ }
+
+ pos += tlv_len;
+ left -= tlv_len;
+ }
+ if (left) {
+ wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "
+ "Request (left=%lu)", (unsigned long) left);
+ return -1;
+ }
+
+ /* Process supported TLVs */
+ if (crypto_tlv && data->crypto_binding != NO_BINDING) {
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",
+ crypto_tlv, crypto_tlv_len);
+ if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,
+ crypto_tlv_len + 4) < 0) {
+ if (result_tlv == NULL)
+ return -1;
+ force_failure = 1;
+ crypto_tlv = NULL; /* do not include Cryptobinding TLV
+ * in response, if the received
+ * cryptobinding was invalid. */
+ }
+ } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
+ return -1;
+ }
+
+ if (result_tlv) {
+ int status, resp_status;
+ wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
+ result_tlv, result_tlv_len);
+ if (result_tlv_len < 2) {
+ wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "
+ "(len=%lu)",
+ (unsigned long) result_tlv_len);
+ return -1;
+ }
+ status = WPA_GET_BE16(result_tlv);
+ if (status == EAP_TLV_RESULT_SUCCESS) {
+ wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
+ "- EAP-TLV/Phase2 Completed");
+ if (force_failure) {
+ wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure"
+ " - force failed Phase 2");
+ resp_status = EAP_TLV_RESULT_FAILURE;
+ ret->decision = DECISION_FAIL;
+ } else {
+ resp_status = EAP_TLV_RESULT_SUCCESS;
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+ } else if (status == EAP_TLV_RESULT_FAILURE) {
+ wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure");
+ resp_status = EAP_TLV_RESULT_FAILURE;
+ ret->decision = DECISION_FAIL;
+ } else {
+ wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
+ "Status %d", status);
+ resp_status = EAP_TLV_RESULT_FAILURE;
+ ret->decision = DECISION_FAIL;
+ }
+ ret->methodState = METHOD_DONE;
+
+ *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL,
+ eap_get_id(req), resp_status);
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf)
+{
+ struct wpabuf *e;
+ struct eap_tlv_hdr *tlv;
+
+ if (buf == NULL)
+ return NULL;
+
+ /* Encapsulate EAP packet in EAP-Payload TLV */
+ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");
+ e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
+ if (e == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "
+ "for TLV encapsulation");
+ wpabuf_free(buf);
+ return NULL;
+ }
+ tlv = wpabuf_put(e, sizeof(*tlv));
+ tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+ EAP_TLV_EAP_PAYLOAD_TLV);
+ tlv->length = host_to_be16(wpabuf_len(buf));
+ wpabuf_put_buf(e, buf);
+ wpabuf_free(buf);
+ return e;
+}
+
+
+static int eap_peap_phase2_request(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf *req,
+ struct wpabuf **resp)
+{
+ struct eap_hdr *hdr = wpabuf_mhead(req);
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_method_ret iret;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos);
+ switch (*pos) {
+ case EAP_TYPE_IDENTITY:
+ *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+ break;
+ case EAP_TYPE_TLV:
+ os_memset(&iret, 0, sizeof(iret));
+ if (eap_tlv_process(sm, data, &iret, req, resp,
+ data->phase2_eap_started &&
+ !data->phase2_eap_success)) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+ if (iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) {
+ ret->methodState = iret.methodState;
+ ret->decision = iret.decision;
+ data->phase2_success = 1;
+ }
+ break;
+ case EAP_TYPE_EXPANDED:
+#ifdef EAP_TNC
+ if (data->soh) {
+ const u8 *epos;
+ size_t eleft;
+
+ epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21,
+ req, &eleft);
+ if (epos) {
+ struct wpabuf *buf;
+ wpa_printf(MSG_DEBUG,
+ "EAP-PEAP: SoH EAP Extensions");
+ buf = tncc_process_soh_request(data->soh,
+ epos, eleft);
+ if (buf) {
+ *resp = eap_msg_alloc(
+ EAP_VENDOR_MICROSOFT, 0x21,
+ wpabuf_len(buf),
+ EAP_CODE_RESPONSE,
+ hdr->identifier);
+ if (*resp == NULL) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+ wpabuf_put_buf(*resp, buf);
+ wpabuf_free(buf);
+ break;
+ }
+ }
+ }
+#endif /* EAP_TNC */
+ /* fall through */
+ default:
+ if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+ data->phase2_type.method == EAP_TYPE_NONE) {
+ size_t i;
+ for (i = 0; i < data->num_phase2_types; i++) {
+ if (data->phase2_types[i].vendor !=
+ EAP_VENDOR_IETF ||
+ data->phase2_types[i].method != *pos)
+ continue;
+
+ data->phase2_type.vendor =
+ data->phase2_types[i].vendor;
+ data->phase2_type.method =
+ data->phase2_types[i].method;
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "
+ "Phase 2 EAP vendor %d method %d",
+ data->phase2_type.vendor,
+ data->phase2_type.method);
+ break;
+ }
+ }
+ if (*pos != data->phase2_type.method ||
+ *pos == EAP_TYPE_NONE) {
+ if (eap_peer_tls_phase2_nak(data->phase2_types,
+ data->num_phase2_types,
+ hdr, resp))
+ return -1;
+ return 0;
+ }
+
+ if (data->phase2_priv == NULL) {
+ data->phase2_method = eap_peer_get_eap_method(
+ data->phase2_type.vendor,
+ data->phase2_type.method);
+ if (data->phase2_method) {
+ sm->init_phase2 = 1;
+ data->phase2_priv =
+ data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ }
+ }
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize "
+ "Phase 2 EAP method %d", *pos);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+ data->phase2_eap_started = 1;
+ os_memset(&iret, 0, sizeof(iret));
+ *resp = data->phase2_method->process(sm, data->phase2_priv,
+ &iret, req);
+ if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC)) {
+ data->phase2_eap_success = 1;
+ data->phase2_success = 1;
+ }
+ break;
+ }
+
+ if (*resp == NULL &&
+ (config->pending_req_identity || config->pending_req_password ||
+ config->pending_req_otp || config->pending_req_new_password)) {
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
+ }
+
+ return 0;
+}
+
+
+static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data,
+ struct eap_method_ret *ret,
+ const struct eap_hdr *req,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *in_decrypted = NULL;
+ int res, skip_change = 0;
+ struct eap_hdr *hdr, *rhdr;
+ struct wpabuf *resp = NULL;
+ size_t len;
+
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
+ " Phase 2", (unsigned long) wpabuf_len(in_data));
+
+ if (data->pending_phase2_req) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "
+ "skip decryption and use old data");
+ /* Clear TLS reassembly state. */
+ eap_peer_tls_reset_input(&data->ssl);
+ in_decrypted = data->pending_phase2_req;
+ data->pending_phase2_req = NULL;
+ skip_change = 1;
+ goto continue_req;
+ }
+
+ if (wpabuf_len(in_data) == 0 && sm->workaround &&
+ data->phase2_success) {
+ /*
+ * Cisco ACS seems to be using TLS ACK to terminate
+ * EAP-PEAPv0/GTC. Try to reply with TLS ACK.
+ */
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but "
+ "expected data - acknowledge with TLS ACK since "
+ "Phase 2 has been completed");
+ ret->decision = DECISION_COND_SUCC;
+ ret->methodState = METHOD_DONE;
+ return 1;
+ } else if (wpabuf_len(in_data) == 0) {
+ /* Received TLS ACK - requesting more fragments */
+ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
+ data->peap_version,
+ req->identifier, NULL, out_data);
+ }
+
+ res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+ if (res)
+ return res;
+
+continue_req:
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
+ in_decrypted);
+
+ hdr = wpabuf_mhead(in_decrypted);
+ if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST &&
+ be_to_host16(hdr->length) == 5 &&
+ eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) {
+ /* At least FreeRADIUS seems to send full EAP header with
+ * EAP Request Identity */
+ skip_change = 1;
+ }
+ if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST &&
+ eap_get_type(in_decrypted) == EAP_TYPE_TLV) {
+ skip_change = 1;
+ }
+
+ if (data->peap_version == 0 && !skip_change) {
+ struct eap_hdr *nhdr;
+ struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) +
+ wpabuf_len(in_decrypted));
+ if (nmsg == NULL) {
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ nhdr = wpabuf_put(nmsg, sizeof(*nhdr));
+ wpabuf_put_buf(nmsg, in_decrypted);
+ nhdr->code = req->code;
+ nhdr->identifier = req->identifier;
+ nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
+ wpabuf_len(in_decrypted));
+
+ wpabuf_free(in_decrypted);
+ in_decrypted = nmsg;
+ }
+
+ if (data->peap_version >= 2) {
+ struct eap_tlv_hdr *tlv;
+ struct wpabuf *nmsg;
+
+ if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
+ "EAP TLV");
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ tlv = wpabuf_mhead(in_decrypted);
+ if ((be_to_host16(tlv->tlv_type) & 0x3fff) !=
+ EAP_TLV_EAP_PAYLOAD_TLV) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ if (sizeof(*tlv) + be_to_host16(tlv->length) >
+ wpabuf_len(in_decrypted)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
+ "length");
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ hdr = (struct eap_hdr *) (tlv + 1);
+ if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
+ "EAP packet in EAP TLV");
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+
+ nmsg = wpabuf_alloc(be_to_host16(hdr->length));
+ if (nmsg == NULL) {
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+
+ wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
+ wpabuf_free(in_decrypted);
+ in_decrypted = nmsg;
+ }
+
+ hdr = wpabuf_mhead(in_decrypted);
+ if (wpabuf_len(in_decrypted) < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
+ "EAP frame (len=%lu)",
+ (unsigned long) wpabuf_len(in_decrypted));
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ len = be_to_host16(hdr->length);
+ if (len > wpabuf_len(in_decrypted)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
+ "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+ (unsigned long) wpabuf_len(in_decrypted),
+ (unsigned long) len);
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ if (len < wpabuf_len(in_decrypted)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "
+ "shorter length than full decrypted data "
+ "(%lu < %lu)",
+ (unsigned long) len,
+ (unsigned long) wpabuf_len(in_decrypted));
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
+ "identifier=%d length=%lu", hdr->code, hdr->identifier,
+ (unsigned long) len);
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (eap_peap_phase2_request(sm, data, ret, in_decrypted,
+ &resp)) {
+ wpabuf_free(in_decrypted);
+ wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request "
+ "processing failed");
+ return 0;
+ }
+ break;
+ case EAP_CODE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
+ if (data->peap_version == 1) {
+ /* EAP-Success within TLS tunnel is used to indicate
+ * shutdown of the TLS channel. The authentication has
+ * been completed. */
+ if (data->phase2_eap_started &&
+ !data->phase2_eap_success) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 "
+ "Success used to indicate success, "
+ "but Phase 2 EAP was not yet "
+ "completed successfully");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ wpabuf_free(in_decrypted);
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "
+ "EAP-Success within TLS tunnel - "
+ "authentication completed");
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->methodState = METHOD_DONE;
+ data->phase2_success = 1;
+ if (data->peap_outer_success == 2) {
+ wpabuf_free(in_decrypted);
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "
+ "to finish authentication");
+ return 1;
+ } else if (data->peap_outer_success == 1) {
+ /* Reply with EAP-Success within the TLS
+ * channel to complete the authentication. */
+ resp = wpabuf_alloc(sizeof(struct eap_hdr));
+ if (resp) {
+ rhdr = wpabuf_put(resp, sizeof(*rhdr));
+ rhdr->code = EAP_CODE_SUCCESS;
+ rhdr->identifier = hdr->identifier;
+ rhdr->length =
+ host_to_be16(sizeof(*rhdr));
+ }
+ } else {
+ /* No EAP-Success expected for Phase 1 (outer,
+ * unencrypted auth), so force EAP state
+ * machine to SUCCESS state. */
+ sm->peap_done = TRUE;
+ }
+ } else {
+ /* FIX: ? */
+ }
+ break;
+ case EAP_CODE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
+ ret->decision = DECISION_FAIL;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->allowNotifications = FALSE;
+ /* Reply with EAP-Failure within the TLS channel to complete
+ * failure reporting. */
+ resp = wpabuf_alloc(sizeof(struct eap_hdr));
+ if (resp) {
+ rhdr = wpabuf_put(resp, sizeof(*rhdr));
+ rhdr->code = EAP_CODE_FAILURE;
+ rhdr->identifier = hdr->identifier;
+ rhdr->length = host_to_be16(sizeof(*rhdr));
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ break;
+ }
+
+ wpabuf_free(in_decrypted);
+
+ if (resp) {
+ int skip_change2 = 0;
+ struct wpabuf *rmsg, buf;
+
+ wpa_hexdump_buf_key(MSG_DEBUG,
+ "EAP-PEAP: Encrypting Phase 2 data", resp);
+ /* PEAP version changes */
+ if (data->peap_version >= 2) {
+ resp = eap_peapv2_tlv_eap_payload(resp);
+ if (resp == NULL)
+ return -1;
+ }
+ if (wpabuf_len(resp) >= 5 &&
+ wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&
+ eap_get_type(resp) == EAP_TYPE_TLV)
+ skip_change2 = 1;
+ rmsg = resp;
+ if (data->peap_version == 0 && !skip_change2) {
+ wpabuf_set(&buf, wpabuf_head_u8(resp) +
+ sizeof(struct eap_hdr),
+ wpabuf_len(resp) - sizeof(struct eap_hdr));
+ rmsg = &buf;
+ }
+
+ if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
+ data->peap_version, req->identifier,
+ rmsg, out_data)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "
+ "a Phase 2 frame");
+ }
+ wpabuf_free(resp);
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ const struct eap_hdr *req;
+ size_t left;
+ int res;
+ u8 flags, id;
+ struct wpabuf *resp;
+ const u8 *pos;
+ struct eap_peap_data *data = priv;
+
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,
+ reqData, &left, &flags);
+ if (pos == NULL)
+ return NULL;
+ req = wpabuf_head(reqData);
+ id = req->identifier;
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "
+ "ver=%d)", flags & EAP_TLS_VERSION_MASK,
+ data->peap_version);
+ if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version)
+ data->peap_version = flags & EAP_TLS_VERSION_MASK;
+ if (data->force_peap_version >= 0 &&
+ data->force_peap_version != data->peap_version) {
+ wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "
+ "forced PEAP version %d",
+ data->force_peap_version);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",
+ data->peap_version);
+ left = 0; /* make sure that this frame is empty, even though it
+ * should always be, anyway */
+ }
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ struct wpabuf msg;
+ wpabuf_set(&msg, pos, left);
+ res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);
+ } else {
+ res = eap_peer_tls_process_helper(sm, &data->ssl,
+ EAP_TYPE_PEAP,
+ data->peap_version, id, pos,
+ left, &resp);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ char *label;
+ wpa_printf(MSG_DEBUG,
+ "EAP-PEAP: TLS done, proceed to Phase 2");
+ os_free(data->key_data);
+ /* draft-josefsson-ppext-eap-tls-eap-05.txt
+ * specifies that PEAPv1 would use "client PEAP
+ * encryption" as the label. However, most existing
+ * PEAPv1 implementations seem to be using the old
+ * label, "client EAP encryption", instead. Use the old
+ * label by default, but allow it to be configured with
+ * phase1 parameter peaplabel=1. */
+ if (data->peap_version > 1 || data->force_new_label)
+ label = "client PEAP encryption";
+ else
+ label = "client EAP encryption";
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "
+ "key derivation", label);
+ data->key_data =
+ eap_peer_tls_derive_key(sm, &data->ssl, label,
+ EAP_TLS_KEY_LEN);
+ if (data->key_data) {
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-PEAP: Derived key",
+ data->key_data,
+ EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "
+ "derive key");
+ }
+
+ if (sm->workaround && data->resuming) {
+ /*
+ * At least few RADIUS servers (Aegis v1.1.6;
+ * but not v1.1.4; and Cisco ACS) seem to be
+ * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco
+ * ACS) session resumption with outer
+ * EAP-Success. This does not seem to follow
+ * draft-josefsson-pppext-eap-tls-eap-05.txt
+ * section 4.2, so only allow this if EAP
+ * workarounds are enabled.
+ */
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - "
+ "allow outer EAP-Success to "
+ "terminate PEAP resumption");
+ ret->decision = DECISION_COND_SUCC;
+ data->phase2_success = 1;
+ }
+
+ data->resuming = 0;
+ }
+
+ if (res == 2) {
+ struct wpabuf msg;
+ /*
+ * Application data included in the handshake message.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = resp;
+ resp = NULL;
+ wpabuf_set(&msg, pos, left);
+ res = eap_peap_decrypt(sm, data, ret, req, &msg,
+ &resp);
+ }
+ }
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP,
+ data->peap_version);
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ data->phase2_success;
+}
+
+
+static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = NULL;
+ data->crypto_binding_used = 0;
+}
+
+
+static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ os_free(data->key_data);
+ data->key_data = NULL;
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->init_for_reauth)
+ data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+ data->phase2_success = 0;
+ data->phase2_eap_success = 0;
+ data->phase2_eap_started = 0;
+ data->resuming = 1;
+ data->reauth = 1;
+ sm->peap_done = FALSE;
+ return priv;
+}
+
+
+static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_peap_data *data = priv;
+ int len, ret;
+
+ len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+ if (data->phase2_method) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "EAP-PEAPv%d Phase2 method=%s\n",
+ data->peap_version,
+ data->phase2_method->name);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+ }
+ return len;
+}
+
+
+static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_peap_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL || !data->phase2_success)
+ return NULL;
+
+ key = os_malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+
+ if (data->crypto_binding_used) {
+ u8 csk[128];
+ /*
+ * Note: It looks like Microsoft implementation requires null
+ * termination for this label while the one used for deriving
+ * IPMK|CMK did not use null termination.
+ */
+ peap_prfplus(data->peap_version, data->ipmk, 40,
+ "Session Key Generating Function",
+ (u8 *) "\00", 1, csk, sizeof(csk));
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
+ os_memcpy(key, csk, EAP_TLS_KEY_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
+ key, EAP_TLS_KEY_LEN);
+ } else
+ os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
+int eap_peer_peap_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_peap_init;
+ eap->deinit = eap_peap_deinit;
+ eap->process = eap_peap_process;
+ eap->isKeyAvailable = eap_peap_isKeyAvailable;
+ eap->getKey = eap_peap_getKey;
+ eap->get_status = eap_peap_get_status;
+ eap->has_reauth_data = eap_peap_has_reauth_data;
+ eap->deinit_for_reauth = eap_peap_deinit_for_reauth;
+ eap->init_for_reauth = eap_peap_init_for_reauth;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c
new file mode 100644
index 0000000..592ef13
--- /dev/null
+++ b/src/eap_peer/eap_psk.c
@@ -0,0 +1,483 @@
+/*
+ * EAP peer method: EAP-PSK (RFC 4764)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * Note: EAP-PSK is an EAP authentication method and as such, completely
+ * different from WPA-PSK. This file is not needed for WPA-PSK functionality.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "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 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN];
+ u8 *id_s, *id_p;
+ size_t id_s_len, id_p_len;
+ u8 msk[EAP_MSK_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+};
+
+
+static void * eap_psk_init(struct eap_sm *sm)
+{
+ struct eap_psk_data *data;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (!password || password_len != 16) {
+ wpa_printf(MSG_INFO, "EAP-PSK: 16-octet pre-shared key not "
+ "configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ if (eap_psk_key_setup(password, data->ak, data->kdk)) {
+ os_free(data);
+ return NULL;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN);
+ data->state = PSK_INIT;
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ data->id_p = os_malloc(identity_len);
+ if (data->id_p)
+ os_memcpy(data->id_p, identity, identity_len);
+ data->id_p_len = identity_len;
+ }
+ if (data->id_p == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity");
+ os_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_psk_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_psk_data *data = priv;
+ os_free(data->id_s);
+ os_free(data->id_p);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ const struct eap_psk_hdr_1 *hdr1;
+ struct eap_psk_hdr_2 *hdr2;
+ struct wpabuf *resp;
+ u8 *buf, *pos;
+ size_t buflen, len;
+ const u8 *cpos;
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state");
+
+ cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len);
+ hdr1 = (const struct eap_psk_hdr_1 *) cpos;
+ if (cpos == NULL || len < sizeof(*hdr1)) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message "
+ "length (%lu; expected %lu or more)",
+ (unsigned long) len,
+ (unsigned long) sizeof(*hdr1));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags);
+ if (EAP_PSK_FLAGS_GET_T(hdr1->flags) != 0) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)",
+ EAP_PSK_FLAGS_GET_T(hdr1->flags));
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s,
+ EAP_PSK_RAND_LEN);
+ os_free(data->id_s);
+ data->id_s_len = len - sizeof(*hdr1);
+ data->id_s = os_malloc(data->id_s_len);
+ if (data->id_s == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for "
+ "ID_S (len=%lu)", (unsigned long) data->id_s_len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
+ data->id_s, data->id_s_len);
+
+ if (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_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_psk_data *data = priv;
+ u8 *key;
+
+ if (data->state != PSK_DONE)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_psk_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_psk_init;
+ eap->deinit = eap_psk_deinit;
+ eap->process = eap_psk_process;
+ eap->isKeyAvailable = eap_psk_isKeyAvailable;
+ eap->getKey = eap_psk_getKey;
+ eap->get_emsk = eap_psk_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
new file mode 100644
index 0000000..e4705b7
--- /dev/null
+++ b/src/eap_peer/eap_pwd.c
@@ -0,0 +1,744 @@
+/*
+ * EAP peer method: EAP-pwd (RFC 5931)
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.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;
+
+ 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_INFO, "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->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 struct wpabuf *
+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;
+ struct wpabuf *resp;
+
+ if (data->state != PWD_ID_Req) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (payload_len < sizeof(struct eap_pwd_id)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ 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;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-PWD (peer): server said 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");
+ return NULL;
+ }
+ 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");
+ return NULL;
+ }
+
+ /* 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");
+ return NULL;
+ }
+
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): computed %d bit PWE...",
+ BN_num_bits(data->grp->prime));
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ 1 + sizeof(struct eap_pwd_id) + data->id_peer_len,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, EAP_PWD_OPCODE_ID_EXCH);
+ wpabuf_put_be16(resp, data->group_num);
+ wpabuf_put_u8(resp, EAP_PWD_DEFAULT_RAND_FUNC);
+ wpabuf_put_u8(resp, EAP_PWD_DEFAULT_PRF);
+ wpabuf_put_data(resp, id->token, sizeof(id->token));
+ wpabuf_put_u8(resp, EAP_PWD_PREP_NONE);
+ wpabuf_put_data(resp, data->id_peer, data->id_peer_len);
+
+ eap_pwd_state(data, PWD_Commit_Req);
+
+ return resp;
+}
+
+
+static struct wpabuf *
+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)
+{
+ struct wpabuf *resp = NULL;
+ 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);
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ sizeof(struct eap_pwd_hdr) +
+ BN_num_bytes(data->grp->order) +
+ (2 * BN_num_bytes(data->grp->prime)),
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ goto fin;
+
+ wpabuf_put_u8(resp, EAP_PWD_OPCODE_COMMIT_EXCH);
+
+ /* we send the element as (x,y) follwed by the scalar */
+ wpabuf_put_data(resp, element, (2 * BN_num_bytes(data->grp->prime)));
+ wpabuf_put_data(resp, 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 (resp == NULL)
+ eap_pwd_state(data, FAILURE);
+ else
+ eap_pwd_state(data, PWD_Confirm_Req);
+
+ return resp;
+}
+
+
+static struct wpabuf *
+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)
+{
+ struct wpabuf *resp = NULL;
+ BIGNUM *x = NULL, *y = NULL;
+ HMAC_CTX ctx;
+ u32 cs;
+ u16 grp;
+ u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+
+ /*
+ * 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): debug allocation "
+ "fail");
+ goto fin;
+ }
+
+ /*
+ * server's commit is H(k | server_element | server_scalar |
+ * peer_element | peer_scalar | ciphersuite)
+ */
+ H_Init(&ctx);
+
+ /*
+ * 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));
+ BN_bn2bin(data->k, cruft);
+ H_Update(&ctx, 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));
+ BN_bn2bin(x, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(y, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(data->server_scalar, cruft);
+ H_Update(&ctx, 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));
+ BN_bn2bin(x, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(y, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* my scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(data->my_scalar, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+ /* the ciphersuite */
+ H_Update(&ctx, (u8 *) &cs, sizeof(u32));
+
+ /* random function fin */
+ H_Final(&ctx, conf);
+
+ ptr = (u8 *) payload;
+ if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
+ 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)
+ */
+ H_Init(&ctx);
+
+ /* k */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(data->k, cruft);
+ H_Update(&ctx, 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));
+ BN_bn2bin(x, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(y, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* my scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(data->my_scalar, cruft);
+ H_Update(&ctx, 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));
+ BN_bn2bin(x, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(y, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(data->server_scalar, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+ /* the ciphersuite */
+ H_Update(&ctx, (u8 *) &cs, sizeof(u32));
+
+ /* all done */
+ H_Final(&ctx, conf);
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ goto fin;
+
+ wpabuf_put_u8(resp, EAP_PWD_OPCODE_CONFIRM_EXCH);
+ wpabuf_put_data(resp, conf, SHA256_DIGEST_LENGTH);
+
+ 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;
+ }
+
+fin:
+ os_free(cruft);
+ BN_free(x);
+ BN_free(y);
+ ret->methodState = METHOD_DONE;
+ if (resp == NULL) {
+ ret->decision = DECISION_FAIL;
+ eap_pwd_state(data, FAILURE);
+ } else {
+ ret->decision = DECISION_UNCOND_SUCC;
+ eap_pwd_state(data, SUCCESS);
+ }
+
+ return resp;
+}
+
+
+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;
+ size_t len;
+ u8 exch;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len);
+ if ((pos == NULL) || (len < 1)) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_INFO, "EAP-pwd: Received frame: opcode %d", *pos);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+
+ exch = *pos & 0x3f;
+ switch (exch) {
+ case EAP_PWD_OPCODE_ID_EXCH:
+ resp = eap_pwd_perform_id_exchange(sm, data, ret, reqData,
+ pos + 1, len - 1);
+ break;
+ case EAP_PWD_OPCODE_COMMIT_EXCH:
+ resp = eap_pwd_perform_commit_exchange(sm, data, ret, reqData,
+ pos + 1, len - 1);
+ break;
+ case EAP_PWD_OPCODE_CONFIRM_EXCH:
+ resp = eap_pwd_perform_confirm_exchange(sm, data, ret, reqData,
+ pos + 1, len - 1);
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown "
+ "opcode %d", exch);
+ break;
+ }
+
+ 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/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
new file mode 100644
index 0000000..1474b7f
--- /dev/null
+++ b/src/eap_peer/eap_sake.c
@@ -0,0 +1,500 @@
+/*
+ * EAP peer method: EAP-SAKE (RFC 4763)
+ * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "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_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sake_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
+int eap_peer_sake_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_sake_init;
+ eap->deinit = eap_sake_deinit;
+ eap->process = eap_sake_process;
+ eap->isKeyAvailable = eap_sake_isKeyAvailable;
+ eap->getKey = eap_sake_getKey;
+ eap->get_emsk = eap_sake_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
new file mode 100644
index 0000000..6677063
--- /dev/null
+++ b/src/eap_peer/eap_sim.c
@@ -0,0 +1,1101 @@
+/*
+ * EAP peer method: EAP-SIM (RFC 4186)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "pcsc_funcs.h"
+#include "crypto/milenage.h"
+#include "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;
+ }
+
+ eap_sim_state(data, CONTINUE);
+
+ return data;
+}
+
+
+static void eap_sim_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ if (data) {
+ os_free(data->ver_list);
+ os_free(data->pseudonym);
+ os_free(data->reauth_id);
+ os_free(data->last_eap_identity);
+ os_free(data);
+ }
+}
+
+
+static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data)
+{
+ struct eap_peer_config *conf;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm");
+
+ conf = eap_get_config(sm);
+ if (conf == NULL)
+ return -1;
+ if (conf->pcsc) {
+ if (scard_gsm_auth(sm->scard_ctx, data->rand[0],
+ data->sres[0], data->kc[0]) ||
+ scard_gsm_auth(sm->scard_ctx, data->rand[1],
+ data->sres[1], data->kc[1]) ||
+ (data->num_chal > 2 &&
+ scard_gsm_auth(sm->scard_ctx, data->rand[2],
+ data->sres[2], data->kc[2]))) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM "
+ "authentication could not be completed");
+ return -1;
+ }
+ return 0;
+ }
+
+#ifdef CONFIG_SIM_SIMULATOR
+ if (conf->password) {
+ u8 opc[16], k[16];
+ const char *pos;
+ size_t i;
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Use internal GSM-Milenage "
+ "implementation for authentication");
+ if (conf->password_len < 65) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: invalid GSM-Milenage "
+ "password");
+ return -1;
+ }
+ pos = (const char *) conf->password;
+ if (hexstr2bin(pos, k, 16))
+ return -1;
+ pos += 32;
+ if (*pos != ':')
+ return -1;
+ pos++;
+
+ if (hexstr2bin(pos, opc, 16))
+ return -1;
+
+ for (i = 0; i < data->num_chal; i++) {
+ if (gsm_milenage(opc, k, data->rand[i],
+ data->sres[i], data->kc[i])) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: "
+ "GSM-Milenage authentication "
+ "could not be completed");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND",
+ data->rand[i], GSM_RAND_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES",
+ data->sres[i], EAP_SIM_SRES_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc",
+ data->kc[i], EAP_SIM_KC_LEN);
+ }
+ return 0;
+ }
+#endif /* CONFIG_SIM_SIMULATOR */
+
+#ifdef CONFIG_SIM_HARDCODED
+ /* These hardcoded Kc and SRES values are used for testing. RAND to
+ * KC/SREC mapping is very bogus as far as real authentication is
+ * concerned, but it is quite useful for cases where the AS is rotating
+ * the order of pre-configured values. */
+ {
+ size_t i;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Use hardcoded Kc and SRES "
+ "values for testing");
+
+ for (i = 0; i < data->num_chal; i++) {
+ if (data->rand[i][0] == 0xaa) {
+ os_memcpy(data->kc[i],
+ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7",
+ EAP_SIM_KC_LEN);
+ os_memcpy(data->sres[i], "\xd1\xd2\xd3\xd4",
+ EAP_SIM_SRES_LEN);
+ } else if (data->rand[i][0] == 0xbb) {
+ os_memcpy(data->kc[i],
+ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7",
+ EAP_SIM_KC_LEN);
+ os_memcpy(data->sres[i], "\xe1\xe2\xe3\xe4",
+ EAP_SIM_SRES_LEN);
+ } else {
+ os_memcpy(data->kc[i],
+ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7",
+ EAP_SIM_KC_LEN);
+ os_memcpy(data->sres[i], "\xf1\xf2\xf3\xf4",
+ EAP_SIM_SRES_LEN);
+ }
+ }
+ }
+
+ return 0;
+
+#else /* CONFIG_SIM_HARDCODED */
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: No GSM authentication algorithm "
+ "enabled");
+ return -1;
+
+#endif /* CONFIG_SIM_HARDCODED */
+}
+
+
+static int eap_sim_supported_ver(int version)
+{
+ return version == EAP_SIM_VERSION;
+}
+
+
+#define CLEAR_PSEUDONYM 0x01
+#define CLEAR_REAUTH_ID 0x02
+#define CLEAR_EAP_ID 0x04
+
+static void eap_sim_clear_identities(struct eap_sim_data *data, int id)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old%s%s%s",
+ id & CLEAR_PSEUDONYM ? " pseudonym" : "",
+ id & CLEAR_REAUTH_ID ? " reauth_id" : "",
+ id & CLEAR_EAP_ID ? " eap_id" : "");
+ if (id & CLEAR_PSEUDONYM) {
+ os_free(data->pseudonym);
+ data->pseudonym = NULL;
+ data->pseudonym_len = 0;
+ }
+ if (id & CLEAR_REAUTH_ID) {
+ os_free(data->reauth_id);
+ data->reauth_id = NULL;
+ data->reauth_id_len = 0;
+ }
+ if (id & CLEAR_EAP_ID) {
+ os_free(data->last_eap_identity);
+ data->last_eap_identity = NULL;
+ data->last_eap_identity_len = 0;
+ }
+}
+
+
+static int eap_sim_learn_ids(struct eap_sim_data *data,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->next_pseudonym) {
+ os_free(data->pseudonym);
+ data->pseudonym = os_malloc(attr->next_pseudonym_len);
+ if (data->pseudonym == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
+ "next pseudonym");
+ return -1;
+ }
+ os_memcpy(data->pseudonym, attr->next_pseudonym,
+ attr->next_pseudonym_len);
+ data->pseudonym_len = attr->next_pseudonym_len;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-SIM: (encr) AT_NEXT_PSEUDONYM",
+ data->pseudonym,
+ data->pseudonym_len);
+ }
+
+ if (attr->next_reauth_id) {
+ os_free(data->reauth_id);
+ data->reauth_id = os_malloc(attr->next_reauth_id_len);
+ if (data->reauth_id == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
+ "next reauth_id");
+ return -1;
+ }
+ os_memcpy(data->reauth_id, attr->next_reauth_id,
+ attr->next_reauth_id_len);
+ data->reauth_id_len = attr->next_reauth_id_len;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-SIM: (encr) AT_NEXT_REAUTH_ID",
+ data->reauth_id,
+ data->reauth_id_len);
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id,
+ int err)
+{
+ struct eap_sim_msg *msg;
+
+ eap_sim_state(data, FAILURE);
+ data->num_id_req = 0;
+ data->num_notification = 0;
+
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
+ EAP_SIM_SUBTYPE_CLIENT_ERROR);
+ eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_sim_response_start(struct eap_sm *sm,
+ struct eap_sim_data *data, u8 id,
+ enum eap_sim_id_req id_req)
+{
+ const u8 *identity = NULL;
+ size_t identity_len = 0;
+ struct eap_sim_msg *msg;
+
+ data->reauth = 0;
+ if (id_req == ANY_ID && data->reauth_id) {
+ identity = data->reauth_id;
+ identity_len = data->reauth_id_len;
+ data->reauth = 1;
+ } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
+ data->pseudonym) {
+ identity = data->pseudonym;
+ identity_len = data->pseudonym_len;
+ eap_sim_clear_identities(data, CLEAR_REAUTH_ID);
+ } else if (id_req != NO_ID_REQ) {
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ eap_sim_clear_identities(data, CLEAR_PSEUDONYM |
+ CLEAR_REAUTH_ID);
+ }
+ }
+ if (id_req != NO_ID_REQ)
+ eap_sim_clear_identities(data, CLEAR_EAP_ID);
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START);
+ if (!data->reauth) {
+ wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT",
+ data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+ eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0,
+ data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+ wpa_printf(MSG_DEBUG, " AT_SELECTED_VERSION %d",
+ data->selected_version);
+ eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION,
+ data->selected_version, NULL, 0);
+ }
+
+ if (identity) {
+ wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY",
+ identity, identity_len);
+ eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+ identity, identity_len);
+ }
+
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data,
+ u8 id)
+{
+ struct eap_sim_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
+ EAP_SIM_SUBTYPE_CHALLENGE);
+ if (data->use_result_ind) {
+ wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+ }
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, data->k_aut, (u8 *) data->sres,
+ data->num_chal * EAP_SIM_SRES_LEN);
+}
+
+
+static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data,
+ u8 id, int counter_too_small)
+{
+ struct eap_sim_msg *msg;
+ unsigned int counter;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)",
+ id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
+ EAP_SIM_SUBTYPE_REAUTHENTICATION);
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+ if (counter_too_small) {
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL");
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
+ counter = data->counter_too_small;
+ } else
+ counter = data->counter;
+
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+ "AT_ENCR_DATA");
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+ if (data->use_result_ind) {
+ wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+ }
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, data->k_aut, data->nonce_s,
+ EAP_SIM_NONCE_S_LEN);
+}
+
+
+static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data,
+ u8 id, u16 notification)
+{
+ struct eap_sim_msg *msg;
+ u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
+
+ wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", id);
+ msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION);
+ if (k_aut && data->reauth) {
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+ EAP_SIM_AT_ENCR_DATA);
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+ NULL, 0);
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+ EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+ "AT_ENCR_DATA");
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+ }
+ if (k_aut) {
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ }
+ return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0);
+}
+
+
+static struct wpabuf * eap_sim_process_start(struct eap_sm *sm,
+ struct eap_sim_data *data, u8 id,
+ struct eap_sim_attrs *attr)
+{
+ int selected_version = -1, id_error;
+ size_t i;
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start");
+ if (attr->version_list == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in "
+ "SIM/Start");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNSUPPORTED_VERSION);
+ }
+
+ os_free(data->ver_list);
+ data->ver_list = os_malloc(attr->version_list_len);
+ if (data->ver_list == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate "
+ "memory for version list");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+ os_memcpy(data->ver_list, attr->version_list, attr->version_list_len);
+ data->ver_list_len = attr->version_list_len;
+ pos = data->ver_list;
+ for (i = 0; i < data->ver_list_len / 2; i++) {
+ int ver = pos[0] * 256 + pos[1];
+ pos += 2;
+ if (eap_sim_supported_ver(ver)) {
+ selected_version = ver;
+ break;
+ }
+ }
+ if (selected_version < 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported "
+ "version");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNSUPPORTED_VERSION);
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d",
+ selected_version);
+ data->selected_version = selected_version;
+
+ id_error = 0;
+ switch (attr->id_req) {
+ case NO_ID_REQ:
+ break;
+ case ANY_ID:
+ if (data->num_id_req > 0)
+ id_error++;
+ data->num_id_req++;
+ break;
+ case FULLAUTH_ID:
+ if (data->num_id_req > 1)
+ id_error++;
+ data->num_id_req++;
+ break;
+ case PERMANENT_ID:
+ if (data->num_id_req > 2)
+ id_error++;
+ data->num_id_req++;
+ break;
+ }
+ if (id_error) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests "
+ "used within one authentication");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ return eap_sim_response_start(sm, data, id, attr->id_req);
+}
+
+
+static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ u8 id,
+ const struct wpabuf *reqData,
+ struct eap_sim_attrs *attr)
+{
+ const u8 *identity;
+ size_t identity_len;
+ struct eap_sim_attrs eattr;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge");
+ data->reauth = 0;
+ if (!attr->mac || !attr->rand) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+ "did not include%s%s",
+ !attr->mac ? " AT_MAC" : "",
+ !attr->rand ? " AT_RAND" : "");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges",
+ (unsigned long) attr->num_chal);
+ if (attr->num_chal < data->min_num_chal) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of "
+ "challenges (%lu)", (unsigned long) attr->num_chal);
+ return eap_sim_client_error(data, id,
+ EAP_SIM_INSUFFICIENT_NUM_OF_CHAL);
+ }
+ if (attr->num_chal > 3) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges "
+ "(%lu)", (unsigned long) attr->num_chal);
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ /* Verify that RANDs are different */
+ if (os_memcmp(attr->rand, attr->rand + GSM_RAND_LEN,
+ GSM_RAND_LEN) == 0 ||
+ (attr->num_chal > 2 &&
+ (os_memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN,
+ GSM_RAND_LEN) == 0 ||
+ os_memcmp(attr->rand + GSM_RAND_LEN,
+ attr->rand + 2 * GSM_RAND_LEN,
+ GSM_RAND_LEN) == 0))) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_RAND_NOT_FRESH);
+ }
+
+ os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN);
+ data->num_chal = attr->num_chal;
+
+ if (eap_sim_gsm_auth(sm, data)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+ if (data->last_eap_identity) {
+ identity = data->last_eap_identity;
+ identity_len = data->last_eap_identity_len;
+ } else if (data->pseudonym) {
+ identity = data->pseudonym;
+ identity_len = data->pseudonym_len;
+ } else
+ identity = eap_get_config_identity(sm, &identity_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK "
+ "derivation", identity, identity_len);
+ eap_sim_derive_mk(identity, identity_len, data->nonce_mt,
+ data->selected_version, data->ver_list,
+ data->ver_list_len, data->num_chal,
+ (const u8 *) data->kc, data->mk);
+ eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
+ data->emsk);
+ if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, data->nonce_mt,
+ EAP_SIM_NONCE_MT_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+ "used invalid AT_MAC");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ /* Old reauthentication and pseudonym identities must not be used
+ * anymore. In other words, if no new identities are received, full
+ * authentication will be used on next reauthentication. */
+ eap_sim_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
+ CLEAR_EAP_ID);
+
+ if (attr->encr_data) {
+ u8 *decrypted;
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv,
+ &eattr, 0);
+ if (decrypted == NULL) {
+ return eap_sim_client_error(
+ data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+ eap_sim_learn_ids(data, &eattr);
+ os_free(decrypted);
+ }
+
+ if (data->result_ind && attr->result_ind)
+ data->use_result_ind = 1;
+
+ if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+ eap_sim_state(data, data->use_result_ind ?
+ RESULT_SUCCESS : SUCCESS);
+ }
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ /* RFC 4186 specifies that counter is initialized to one after
+ * fullauth, but initializing it to zero makes it easier to implement
+ * reauth verification. */
+ data->counter = 0;
+ return eap_sim_response_challenge(data, id);
+}
+
+
+static int eap_sim_process_notification_reauth(struct eap_sim_data *data,
+ struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+ u8 *decrypted;
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after "
+ "reauth did not include encrypted data");
+ return -1;
+ }
+
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr,
+ 0);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+ "data from notification message");
+ return -1;
+ }
+
+ if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification "
+ "message does not match with counter in reauth "
+ "message");
+ os_free(decrypted);
+ return -1;
+ }
+
+ os_free(decrypted);
+ return 0;
+}
+
+
+static int eap_sim_process_notification_auth(struct eap_sim_data *data,
+ const struct wpabuf *reqData,
+ struct eap_sim_attrs *attr)
+{
+ if (attr->mac == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth "
+ "Notification message");
+ return -1;
+ }
+
+ if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
+ {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Notification message "
+ "used invalid AT_MAC");
+ return -1;
+ }
+
+ if (data->reauth &&
+ eap_sim_process_notification_reauth(data, attr)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification "
+ "message after reauth");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_sim_process_notification(
+ struct eap_sm *sm, struct eap_sim_data *data, u8 id,
+ const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification");
+ if (data->num_notification > 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: too many notification "
+ "rounds (only one allowed)");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+ data->num_notification++;
+ if (attr->notification == -1) {
+ wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in "
+ "Notification message");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if ((attr->notification & 0x4000) == 0 &&
+ eap_sim_process_notification_auth(data, reqData, attr)) {
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ eap_sim_report_notification(sm->msg_ctx, attr->notification, 0);
+ if (attr->notification >= 0 && attr->notification < 32768) {
+ eap_sim_state(data, FAILURE);
+ } else if (attr->notification == EAP_SIM_SUCCESS &&
+ data->state == RESULT_SUCCESS)
+ eap_sim_state(data, SUCCESS);
+ return eap_sim_response_notification(data, id, attr->notification);
+}
+
+
+static struct wpabuf * eap_sim_process_reauthentication(
+ struct eap_sm *sm, struct eap_sim_data *data, u8 id,
+ const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+ u8 *decrypted;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication");
+
+ if (data->reauth_id == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying "
+ "reauthentication, but no reauth_id available");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ data->reauth = 1;
+ if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
+ {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+ "did not have valid AT_MAC");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+ "message did not include encrypted data");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr,
+ 0);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+ "data from reauthentication message");
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eattr.nonce_s == NULL || eattr.counter < 0) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet",
+ !eattr.nonce_s ? " AT_NONCE_S" : "",
+ eattr.counter < 0 ? " AT_COUNTER" : "");
+ os_free(decrypted);
+ return eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ }
+
+ if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
+ wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter "
+ "(%d <= %d)", eattr.counter, data->counter);
+ data->counter_too_small = eattr.counter;
+ /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
+ * reauth_id must not be used to start a new reauthentication.
+ * However, since it was used in the last EAP-Response-Identity
+ * packet, it has to saved for the following fullauth to be
+ * used in MK derivation. */
+ os_free(data->last_eap_identity);
+ data->last_eap_identity = data->reauth_id;
+ data->last_eap_identity_len = data->reauth_id_len;
+ data->reauth_id = NULL;
+ data->reauth_id_len = 0;
+ os_free(decrypted);
+ return eap_sim_response_reauth(data, id, 1);
+ }
+ data->counter = eattr.counter;
+
+ os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S",
+ data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+ eap_sim_derive_keys_reauth(data->counter,
+ data->reauth_id, data->reauth_id_len,
+ data->nonce_s, data->mk, data->msk,
+ data->emsk);
+ eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_sim_learn_ids(data, &eattr);
+
+ if (data->result_ind && attr->result_ind)
+ data->use_result_ind = 1;
+
+ if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+ eap_sim_state(data, data->use_result_ind ?
+ RESULT_SUCCESS : SUCCESS);
+ }
+
+ data->num_id_req = 0;
+ data->num_notification = 0;
+ if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of "
+ "fast reauths performed - force fullauth");
+ eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ }
+ os_free(decrypted);
+ return eap_sim_response_reauth(data, id, 0);
+}
+
+
+static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_sim_data *data = priv;
+ const struct eap_hdr *req;
+ u8 subtype, id;
+ struct wpabuf *res;
+ const u8 *pos;
+ struct eap_sim_attrs attr;
+ size_t len;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-SIM: EAP data", reqData);
+ if (eap_get_config_identity(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured");
+ eap_sm_request_identity(sm);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len);
+ if (pos == NULL || len < 1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ req = wpabuf_head(reqData);
+ id = req->identifier;
+ len = be_to_host16(req->length);
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ subtype = *pos++;
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype);
+ pos += 2; /* Reserved */
+
+ if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 0,
+ 0)) {
+ res = eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ goto done;
+ }
+
+ switch (subtype) {
+ case EAP_SIM_SUBTYPE_START:
+ res = eap_sim_process_start(sm, data, id, &attr);
+ break;
+ case EAP_SIM_SUBTYPE_CHALLENGE:
+ res = eap_sim_process_challenge(sm, data, id, reqData, &attr);
+ break;
+ case EAP_SIM_SUBTYPE_NOTIFICATION:
+ res = eap_sim_process_notification(sm, data, id, reqData,
+ &attr);
+ break;
+ case EAP_SIM_SUBTYPE_REAUTHENTICATION:
+ res = eap_sim_process_reauthentication(sm, data, id, reqData,
+ &attr);
+ break;
+ case EAP_SIM_SUBTYPE_CLIENT_ERROR:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error");
+ res = eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype);
+ res = eap_sim_client_error(data, id,
+ EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+ break;
+ }
+
+done:
+ if (data->state == FAILURE) {
+ ret->decision = DECISION_FAIL;
+ ret->methodState = METHOD_DONE;
+ } else if (data->state == SUCCESS) {
+ ret->decision = data->use_result_ind ?
+ DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
+ ret->methodState = data->use_result_ind ?
+ METHOD_DONE : METHOD_MAY_CONT;
+ } else if (data->state == RESULT_FAILURE)
+ ret->methodState = METHOD_CONT;
+ else if (data->state == RESULT_SUCCESS)
+ ret->methodState = METHOD_CONT;
+
+ if (ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ }
+
+ return res;
+}
+
+
+static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ return data->pseudonym || data->reauth_id;
+}
+
+
+static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ eap_sim_clear_identities(data, CLEAR_EAP_ID);
+ data->use_result_ind = 0;
+}
+
+
+static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ if (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_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sim_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_sim_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_sim_init;
+ eap->deinit = eap_sim_deinit;
+ eap->process = eap_sim_process;
+ eap->isKeyAvailable = eap_sim_isKeyAvailable;
+ eap->getKey = eap_sim_getKey;
+ eap->has_reauth_data = eap_sim_has_reauth_data;
+ eap->deinit_for_reauth = eap_sim_deinit_for_reauth;
+ eap->init_for_reauth = eap_sim_init_for_reauth;
+ eap->get_identity = eap_sim_get_identity;
+ eap->get_emsk = eap_sim_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
new file mode 100644
index 0000000..20b2212
--- /dev/null
+++ b/src/eap_peer/eap_tls.c
@@ -0,0 +1,289 @@
+/*
+ * EAP peer method: EAP-TLS (RFC 2716)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
+static void eap_tls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_tls_data {
+ struct eap_ssl_data ssl;
+ u8 *key_data;
+};
+
+
+static void * eap_tls_init(struct eap_sm *sm)
+{
+ struct eap_tls_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL ||
+ ((sm->init_phase2 ? config->private_key2 : config->private_key)
+ == NULL &&
+ (sm->init_phase2 ? config->engine2 : config->engine) == 0)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+ eap_tls_deinit(sm, data);
+ if (config->engine) {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard "
+ "PIN");
+ eap_sm_request_pin(sm);
+ sm->ignore = TRUE;
+ } else if (config->private_key && !config->private_key_passwd)
+ {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private "
+ "key passphrase");
+ eap_sm_request_passphrase(sm);
+ sm->ignore = TRUE;
+ }
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_tls_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ if (data == NULL)
+ return;
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
+ os_free(data->key_data);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
+ struct eap_tls_data *data,
+ struct eap_method_ret *ret, int res,
+ struct wpabuf *resp, u8 id)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed");
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+
+ if (res == -1) {
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config) {
+ /*
+ * The TLS handshake failed. So better forget the old
+ * PIN. It may be wrong, we cannot be sure but trying
+ * the wrong one again might block it on the card--so
+ * better ask the user again.
+ */
+ os_free(config->pin);
+ config->pin = NULL;
+ }
+ }
+
+ if (resp) {
+ /*
+ * This is likely an alert message, so send it instead of just
+ * ACKing the error.
+ */
+ return resp;
+ }
+
+ return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0);
+}
+
+
+static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
+ struct eap_method_ret *ret)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
+
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+
+ os_free(data->key_data);
+ data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
+ "client EAP encryption",
+ EAP_TLS_KEY_LEN +
+ EAP_EMSK_LEN);
+ if (data->key_data) {
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key",
+ data->key_data, EAP_TLS_KEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK",
+ data->key_data + EAP_TLS_KEY_LEN,
+ EAP_EMSK_LEN);
+ } else {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key");
+ }
+}
+
+
+static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ size_t left;
+ int res;
+ struct wpabuf *resp;
+ u8 flags, id;
+ const u8 *pos;
+ struct eap_tls_data *data = priv;
+
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TLS, ret,
+ reqData, &left, &flags);
+ if (pos == NULL)
+ return NULL;
+ id = eap_get_id(reqData);
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Start");
+ left = 0; /* make sure that this frame is empty, even though it
+ * should always be, anyway */
+ }
+
+ resp = NULL;
+ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id,
+ pos, left, &resp);
+
+ if (res < 0) {
+ return eap_tls_failure(sm, data, ret, res, resp, id);
+ }
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
+ eap_tls_success(sm, data, ret);
+
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0);
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ os_free(data->key_data);
+ data->key_data = NULL;
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ return priv;
+}
+
+
+static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_tls_data *data = priv;
+ return eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+}
+
+
+static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ return data->key_data != NULL;
+}
+
+
+static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_tls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL)
+ return NULL;
+
+ key = os_malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+ os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
+static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_tls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+ os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
+
+ return key;
+}
+
+
+int eap_peer_tls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_tls_init;
+ eap->deinit = eap_tls_deinit;
+ eap->process = eap_tls_process;
+ eap->isKeyAvailable = eap_tls_isKeyAvailable;
+ eap->getKey = eap_tls_getKey;
+ eap->get_status = eap_tls_get_status;
+ eap->has_reauth_data = eap_tls_has_reauth_data;
+ eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
+ eap->init_for_reauth = eap_tls_init_for_reauth;
+ eap->get_emsk = eap_tls_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
new file mode 100644
index 0000000..d1567e9
--- /dev/null
+++ b/src/eap_peer/eap_tls_common.c
@@ -0,0 +1,1021 @@
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
+static int eap_tls_check_blob(struct eap_sm *sm, const char **name,
+ const u8 **data, size_t *data_len)
+{
+ const struct wpa_config_blob *blob;
+
+ if (*name == NULL || os_strncmp(*name, "blob://", 7) != 0)
+ return 0;
+
+ blob = eap_get_config_blob(sm, *name + 7);
+ if (blob == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not "
+ "found", __func__, *name + 7);
+ return -1;
+ }
+
+ *name = NULL;
+ *data = blob->data;
+ *data_len = blob->len;
+
+ return 0;
+}
+
+
+static void eap_tls_params_flags(struct tls_connection_params *params,
+ const char *txt)
+{
+ if (txt == NULL)
+ return;
+ if (os_strstr(txt, "tls_allow_md5=1"))
+ params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
+ if (os_strstr(txt, "tls_disable_time_checks=1"))
+ params->flags |= TLS_CONN_DISABLE_TIME_CHECKS;
+}
+
+
+static void eap_tls_params_from_conf1(struct tls_connection_params *params,
+ struct eap_peer_config *config)
+{
+ params->ca_cert = (char *) config->ca_cert;
+ params->ca_path = (char *) config->ca_path;
+ params->client_cert = (char *) config->client_cert;
+ params->private_key = (char *) config->private_key;
+ params->private_key_passwd = (char *) config->private_key_passwd;
+ params->dh_file = (char *) config->dh_file;
+ params->subject_match = (char *) config->subject_match;
+ params->altsubject_match = (char *) config->altsubject_match;
+ params->engine = config->engine;
+ params->engine_id = config->engine_id;
+ params->pin = config->pin;
+ params->key_id = config->key_id;
+ params->cert_id = config->cert_id;
+ params->ca_cert_id = config->ca_cert_id;
+ eap_tls_params_flags(params, config->phase1);
+}
+
+
+static void eap_tls_params_from_conf2(struct tls_connection_params *params,
+ struct eap_peer_config *config)
+{
+ params->ca_cert = (char *) config->ca_cert2;
+ params->ca_path = (char *) config->ca_path2;
+ params->client_cert = (char *) config->client_cert2;
+ params->private_key = (char *) config->private_key2;
+ params->private_key_passwd = (char *) config->private_key2_passwd;
+ params->dh_file = (char *) config->dh_file2;
+ params->subject_match = (char *) config->subject_match2;
+ params->altsubject_match = (char *) config->altsubject_match2;
+ params->engine = config->engine2;
+ params->engine_id = config->engine2_id;
+ params->pin = config->pin2;
+ params->key_id = config->key2_id;
+ params->cert_id = config->cert2_id;
+ params->ca_cert_id = config->ca_cert2_id;
+ eap_tls_params_flags(params, config->phase2);
+}
+
+
+static int eap_tls_params_from_conf(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ struct tls_connection_params *params,
+ struct eap_peer_config *config, int phase2)
+{
+ os_memset(params, 0, sizeof(*params));
+ if (phase2) {
+ wpa_printf(MSG_DEBUG, "TLS: using phase2 config options");
+ eap_tls_params_from_conf2(params, config);
+ } else {
+ wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
+ eap_tls_params_from_conf1(params, config);
+ }
+ params->tls_ia = data->tls_ia;
+
+ /*
+ * Use blob data, if available. Otherwise, leave reference to external
+ * file as-is.
+ */
+ if (eap_tls_check_blob(sm, &params->ca_cert, &params->ca_cert_blob,
+ &params->ca_cert_blob_len) ||
+ eap_tls_check_blob(sm, &params->client_cert,
+ &params->client_cert_blob,
+ &params->client_cert_blob_len) ||
+ eap_tls_check_blob(sm, &params->private_key,
+ &params->private_key_blob,
+ &params->private_key_blob_len) ||
+ eap_tls_check_blob(sm, &params->dh_file, &params->dh_blob,
+ &params->dh_blob_len)) {
+ wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_tls_init_connection(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ struct eap_peer_config *config,
+ struct tls_connection_params *params)
+{
+ int res;
+
+ data->conn = tls_connection_init(sm->ssl_ctx);
+ if (data->conn == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
+ "connection");
+ return -1;
+ }
+
+ res = tls_connection_set_params(sm->ssl_ctx, data->conn, params);
+ if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
+ /*
+ * At this point with the pkcs11 engine the PIN might be wrong.
+ * We reset the PIN in the configuration to be sure to not use
+ * it again and the calling function must request a new one.
+ */
+ os_free(config->pin);
+ config->pin = NULL;
+ } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
+ wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+ /*
+ * We do not know exactly but maybe the PIN was wrong,
+ * so ask for a new one.
+ */
+ os_free(config->pin);
+ config->pin = NULL;
+ eap_sm_request_pin(sm);
+ sm->ignore = TRUE;
+ tls_connection_deinit(sm->ssl_ctx, data->conn);
+ data->conn = NULL;
+ return -1;
+ } else if (res) {
+ wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
+ "parameters");
+ tls_connection_deinit(sm->ssl_ctx, data->conn);
+ data->conn = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_init - Initialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @config: Pointer to the network configuration
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to initialize shared TLS functionality for EAP-TLS,
+ * EAP-PEAP, EAP-TTLS, and EAP-FAST.
+ */
+int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+ struct eap_peer_config *config)
+{
+ struct tls_connection_params params;
+
+ if (config == NULL)
+ return -1;
+
+ data->eap = sm;
+ data->phase2 = sm->init_phase2;
+ if (eap_tls_params_from_conf(sm, data, &params, config, data->phase2) <
+ 0)
+ return -1;
+
+ if (eap_tls_init_connection(sm, data, config, &params) < 0)
+ return -1;
+
+ data->tls_out_limit = config->fragment_size;
+ if (data->phase2) {
+ /* Limit the fragment size in the inner TLS authentication
+ * since the outer authentication with EAP-PEAP does not yet
+ * support fragmentation */
+ if (data->tls_out_limit > 100)
+ data->tls_out_limit -= 100;
+ }
+
+ if (config->phase1 &&
+ os_strstr(config->phase1, "include_tls_length=1")) {
+ wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in "
+ "unfragmented packets");
+ data->include_tls_length = 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ *
+ * This function deinitializes shared TLS functionality that was initialized
+ * with eap_peer_tls_ssl_init().
+ */
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+ tls_connection_deinit(sm->ssl_ctx, data->conn);
+ eap_peer_tls_reset_input(data);
+ eap_peer_tls_reset_output(data);
+}
+
+
+/**
+ * eap_peer_tls_derive_key - Derive a key based on TLS session data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @label: Label string for deriving the keys, e.g., "client EAP encryption"
+ * @len: Length of the key material to generate (usually 64 for MSK)
+ * Returns: Pointer to allocated key on success or %NULL on failure
+ *
+ * This function uses TLS-PRF to generate pseudo-random data based on the TLS
+ * session data (client/server random and master key). Each key type may use a
+ * different label to bind the key usage into the generated material.
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ const char *label, size_t len)
+{
+ struct tls_keys keys;
+ u8 *rnd = NULL, *out;
+
+ out = os_malloc(len);
+ if (out == NULL)
+ return NULL;
+
+ /* First, try to use TLS library function for PRF, if available. */
+ if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
+ 0)
+ return out;
+
+ /*
+ * TLS library did not support key generation, so get the needed TLS
+ * session parameters and use an internal implementation of TLS PRF to
+ * derive the key.
+ */
+ if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+ goto fail;
+
+ if (keys.client_random == NULL || keys.server_random == NULL ||
+ keys.master_key == NULL)
+ goto fail;
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ if (rnd == NULL)
+ goto fail;
+ os_memcpy(rnd, keys.client_random, keys.client_random_len);
+ os_memcpy(rnd + keys.client_random_len, keys.server_random,
+ keys.server_random_len);
+
+ if (tls_prf(keys.master_key, keys.master_key_len,
+ label, rnd, keys.client_random_len +
+ keys.server_random_len, out, len))
+ goto fail;
+
+ os_free(rnd);
+ return out;
+
+fail:
+ os_free(out);
+ os_free(rnd);
+ return NULL;
+}
+
+
+/**
+ * eap_peer_tls_reassemble_fragment - Reassemble a received fragment
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * Returns: 0 on success, 1 if more data is needed for the full message, or
+ * -1 on error
+ */
+static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
+ const struct wpabuf *in_data)
+{
+ size_t tls_in_len, in_len;
+
+ tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0;
+ in_len = in_data ? wpabuf_len(in_data) : 0;
+
+ if (tls_in_len + in_len == 0) {
+ /* No message data received?! */
+ wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: "
+ "tls_in_left=%lu tls_in_len=%lu in_len=%lu",
+ (unsigned long) data->tls_in_left,
+ (unsigned long) tls_in_len,
+ (unsigned long) in_len);
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+
+ if (tls_in_len + in_len > 65536) {
+ /*
+ * Limit length to avoid rogue servers from causing large
+ * memory allocations.
+ */
+ wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over "
+ "64 kB)");
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+
+ if (in_len > data->tls_in_left) {
+ /* Sender is doing something odd - reject message */
+ wpa_printf(MSG_INFO, "SSL: more data than TLS message length "
+ "indicated");
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+
+ if (wpabuf_resize(&data->tls_in, in_len) < 0) {
+ wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS "
+ "data");
+ eap_peer_tls_reset_input(data);
+ return -1;
+ }
+ 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(sm->ssl_ctx, data->conn,
+ msg, &appl_data);
+
+ eap_peer_tls_reset_input(data);
+
+ if (appl_data &&
+ tls_connection_established(sm->ssl_ctx, data->conn) &&
+ !tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data",
+ appl_data);
+ *out_data = appl_data;
+ return 2;
+ }
+
+ wpabuf_free(appl_data);
+
+ return 0;
+}
+
+
+/**
+ * eap_tls_process_output - Process outgoing TLS message
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @ret: Return value to use on success
+ * @out_data: Buffer for returning the allocated output buffer
+ * Returns: ret (0 or 1) on success, -1 on failure
+ */
+static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
+ int peap_version, u8 id, int ret,
+ struct wpabuf **out_data)
+{
+ size_t len;
+ u8 *flags;
+ int more_fragments, length_included;
+
+ if (data->tls_out == NULL)
+ return -1;
+ len = wpabuf_len(data->tls_out) - data->tls_out_pos;
+ wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
+ "%lu bytes)",
+ (unsigned long) len,
+ (unsigned long) wpabuf_len(data->tls_out));
+
+ /*
+ * Limit outgoing message to the configured maximum size. Fragment
+ * message if needed.
+ */
+ if (len > data->tls_out_limit) {
+ more_fragments = 1;
+ len = data->tls_out_limit;
+ wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
+ "will follow", (unsigned long) len);
+ } else
+ more_fragments = 0;
+
+ length_included = data->tls_out_pos == 0 &&
+ (wpabuf_len(data->tls_out) > data->tls_out_limit ||
+ data->include_tls_length);
+ if (!length_included &&
+ eap_type == EAP_TYPE_PEAP && peap_version == 0 &&
+ !tls_connection_established(data->eap->ssl_ctx, data->conn)) {
+ /*
+ * Windows Server 2008 NPS really wants to have the TLS Message
+ * length included in phase 0 even for unfragmented frames or
+ * it will get very confused with Compound MAC calculation and
+ * Outer TLVs.
+ */
+ length_included = 1;
+ }
+
+ *out_data = eap_msg_alloc(EAP_VENDOR_IETF, eap_type,
+ 1 + length_included * 4 + len,
+ EAP_CODE_RESPONSE, id);
+ if (*out_data == NULL)
+ return -1;
+
+ flags = wpabuf_put(*out_data, 1);
+ *flags = peap_version;
+ if (more_fragments)
+ *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
+ if (length_included) {
+ *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
+ wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out));
+ }
+
+ wpabuf_put_data(*out_data,
+ wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
+ len);
+ data->tls_out_pos += len;
+
+ if (!more_fragments)
+ eap_peer_tls_reset_output(data);
+
+ return ret;
+}
+
+
+/**
+ * eap_peer_tls_process_helper - Process TLS handshake message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Message received from the server
+ * @in_len: Length of in_data
+ * @out_data: Buffer for returning a pointer to the response message
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, or -1 on failure
+ *
+ * This function can be used to process TLS handshake messages. It reassembles
+ * the received fragments and uses a TLS library to process the messages. The
+ * response data from the TLS library is fragmented to suitable output messages
+ * that the caller can send out.
+ *
+ * out_data is used to return the response message if the return value of this
+ * function is 0, 2, or -1. In case of failure, the message is likely a TLS
+ * alarm message. The caller is responsible for freeing the allocated buffer if
+ * *out_data is not %NULL.
+ *
+ * This function is called for each received TLS message during the TLS
+ * handshake after eap_peer_tls_process_init() call and possible processing of
+ * TLS Flags field. Once the handshake has been completed, i.e., when
+ * tls_connection_established() returns 1, EAP method specific decrypting of
+ * the tunneled data is used.
+ */
+int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version,
+ u8 id, const u8 *in_data, size_t in_len,
+ struct wpabuf **out_data)
+{
+ int ret = 0;
+
+ *out_data = NULL;
+
+ if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output "
+ "fragments are waiting to be sent out");
+ return -1;
+ }
+
+ if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+ /*
+ * No more data to send out - expect to receive more data from
+ * the AS.
+ */
+ int res = eap_tls_process_input(sm, data, in_data, in_len,
+ out_data);
+ if (res) {
+ /*
+ * Input processing failed (res = -1) or more data is
+ * needed (res = 1).
+ */
+ return res;
+ }
+
+ /*
+ * The incoming message has been reassembled and processed. The
+ * response was allocated into data->tls_out buffer.
+ */
+ }
+
+ if (data->tls_out == NULL) {
+ /*
+ * No outgoing fragments remaining from the previous message
+ * and no new message generated. This indicates an error in TLS
+ * processing.
+ */
+ eap_peer_tls_reset_output(data);
+ return -1;
+ }
+
+ if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+ /* TLS processing has failed - return error */
+ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
+ "report error");
+ ret = -1;
+ /* TODO: clean pin if engine used? */
+ }
+
+ if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+ /*
+ * TLS negotiation should now be complete since all other cases
+ * needing more data should have been caught above based on
+ * the TLS Message Length field.
+ */
+ wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
+ wpabuf_free(data->tls_out);
+ data->tls_out = NULL;
+ return 1;
+ }
+
+ /* Send the pending message (in fragments, if needed). */
+ return eap_tls_process_output(data, eap_type, peap_version, id, ret,
+ out_data);
+}
+
+
+/**
+ * eap_peer_tls_build_ack - Build a TLS ACK frame
+ * @id: EAP identifier for the response
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * Returns: Pointer to the allocated ACK frame or %NULL on failure
+ */
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+ int peap_version)
+{
+ struct wpabuf *resp;
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_RESPONSE,
+ id);
+ if (resp == NULL)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)",
+ (int) eap_type, id, peap_version);
+ wpabuf_put_u8(resp, peap_version); /* Flags */
+ return resp;
+}
+
+
+/**
+ * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+ eap_peer_tls_reset_input(data);
+ eap_peer_tls_reset_output(data);
+ return tls_connection_shutdown(sm->ssl_ctx, data->conn);
+}
+
+
+/**
+ * eap_peer_tls_status - Get TLS status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ */
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *buf, size_t buflen, int verbose)
+{
+ char name[128];
+ int len = 0, ret;
+
+ if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "EAP TLS cipher=%s\n", name);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+ }
+
+ return len;
+}
+
+
+/**
+ * eap_peer_tls_process_init - Initial validation/processing of EAP requests
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * @len: Buffer for returning length of the remaining payload
+ * @flags: Buffer for returning TLS flags
+ * Returns: Pointer to payload after TLS flags and length or %NULL on failure
+ *
+ * This function validates the EAP header and processes the optional TLS
+ * Message Length field. If this is the first fragment of a TLS message, the
+ * TLS reassembly code is initialized to receive the indicated number of bytes.
+ *
+ * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this
+ * function as the first step in processing received messages. They will need
+ * to process the flags (apart from Message Length Included) that are returned
+ * through the flags pointer and the message payload that will be returned (and
+ * the length is returned through the len pointer). Return values (ret) are set
+ * for continuation of EAP method processing. The caller is responsible for
+ * setting these to indicate completion (either success or failure) based on
+ * the authentication result.
+ */
+const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ EapType eap_type,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ size_t *len, u8 *flags)
+{
+ const u8 *pos;
+ size_t left;
+ unsigned int tls_msg_len;
+
+ if (tls_get_errors(sm->ssl_ctx)) {
+ wpa_printf(MSG_INFO, "SSL: TLS errors detected");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left);
+ if (pos == NULL) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ if (left == 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags "
+ "octet included");
+ if (!sm->workaround) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags "
+ "indicates ACK frame");
+ *flags = 0;
+ } else {
+ *flags = *pos++;
+ left--;
+ }
+ wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - "
+ "Flags 0x%02x", (unsigned long) wpabuf_len(reqData),
+ *flags);
+ if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (left < 4) {
+ wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
+ "length");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ tls_msg_len = WPA_GET_BE32(pos);
+ wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
+ tls_msg_len);
+ if (data->tls_in_left == 0) {
+ data->tls_in_total = tls_msg_len;
+ data->tls_in_left = tls_msg_len;
+ wpabuf_free(data->tls_in);
+ data->tls_in = NULL;
+ }
+ pos += 4;
+ left -= 4;
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = TRUE;
+
+ *len = left;
+ return pos;
+}
+
+
+/**
+ * eap_peer_tls_reset_input - Reset input buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for input buffers and resets input
+ * state.
+ */
+void eap_peer_tls_reset_input(struct eap_ssl_data *data)
+{
+ data->tls_in_left = data->tls_in_total = 0;
+ wpabuf_free(data->tls_in);
+ data->tls_in = NULL;
+}
+
+
+/**
+ * eap_peer_tls_reset_output - Reset output buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for output buffers and resets
+ * output state.
+ */
+void eap_peer_tls_reset_output(struct eap_ssl_data *data)
+{
+ data->tls_out_pos = 0;
+ wpabuf_free(data->tls_out);
+ data->tls_out = NULL;
+}
+
+
+/**
+ * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
+ * @in_decrypted: Buffer for returning a pointer to the decrypted message
+ * Returns: 0 on success, 1 if more input data is needed, or -1 on failure
+ */
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ const struct wpabuf *in_data,
+ struct wpabuf **in_decrypted)
+{
+ const struct wpabuf *msg;
+ int need_more_input;
+
+ msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
+ if (msg == NULL)
+ return need_more_input ? 1 : -1;
+
+ *in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg);
+ eap_peer_tls_reset_input(data);
+ if (*in_decrypted == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_encrypt - Encrypt phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments
+ * @out_data: Buffer for returning a pointer to the encrypted response message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version, u8 id,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ if (in_data) {
+ eap_peer_tls_reset_output(data);
+ data->tls_out = tls_connection_encrypt(sm->ssl_ctx, data->conn,
+ in_data);
+ if (data->tls_out == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
+ "data (in_len=%lu)",
+ (unsigned long) wpabuf_len(in_data));
+ eap_peer_tls_reset_output(data);
+ return -1;
+ }
+ }
+
+ return eap_tls_process_output(data, eap_type, peap_version, id, 0,
+ out_data);
+}
+
+
+/**
+ * eap_peer_select_phase2_methods - Select phase 2 EAP method
+ * @config: Pointer to the network configuration
+ * @prefix: 'phase2' configuration prefix, e.g., "auth="
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to parse EAP method list and select allowed methods
+ * for Phase2 authentication.
+ */
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+ const char *prefix,
+ struct eap_method_type **types,
+ size_t *num_types)
+{
+ char *start, *pos, *buf;
+ struct eap_method_type *methods = NULL, *_methods;
+ u8 method;
+ size_t num_methods = 0, prefix_len;
+
+ if (config == NULL || config->phase2 == NULL)
+ goto get_defaults;
+
+ start = buf = os_strdup(config->phase2);
+ if (buf == NULL)
+ return -1;
+
+ prefix_len = os_strlen(prefix);
+
+ while (start && *start != '\0') {
+ int vendor;
+ pos = os_strstr(start, prefix);
+ if (pos == NULL)
+ break;
+ if (start != pos && *(pos - 1) != ' ') {
+ start = pos + prefix_len;
+ continue;
+ }
+
+ start = pos + prefix_len;
+ pos = os_strchr(start, ' ');
+ if (pos)
+ *pos++ = '\0';
+ method = eap_get_phase2_type(start, &vendor);
+ if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) {
+ wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP "
+ "method '%s'", start);
+ } else {
+ num_methods++;
+ _methods = os_realloc(methods,
+ num_methods * sizeof(*methods));
+ if (_methods == NULL) {
+ os_free(methods);
+ os_free(buf);
+ return -1;
+ }
+ methods = _methods;
+ methods[num_methods - 1].vendor = vendor;
+ methods[num_methods - 1].method = method;
+ }
+
+ start = pos;
+ }
+
+ os_free(buf);
+
+get_defaults:
+ if (methods == NULL)
+ methods = eap_get_phase2_types(config, &num_methods);
+
+ if (methods == NULL) {
+ wpa_printf(MSG_ERROR, "TLS: No Phase2 EAP methods available");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types",
+ (u8 *) methods,
+ num_methods * sizeof(struct eap_method_type));
+
+ *types = methods;
+ *num_types = num_methods;
+
+ return 0;
+}
+
+
+/**
+ * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * @hdr: EAP-Request header (and the following EAP type octet)
+ * @resp: Buffer for returning the EAP-Nak message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+ struct eap_hdr *hdr, struct wpabuf **resp)
+{
+ u8 *pos = (u8 *) (hdr + 1);
+ size_t i;
+
+ /* TODO: add support for expanded Nak */
+ wpa_printf(MSG_DEBUG, "TLS: Phase 2 Request: Nak type=%d", *pos);
+ wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types",
+ (u8 *) types, num_types * sizeof(struct eap_method_type));
+ *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types,
+ EAP_CODE_RESPONSE, hdr->identifier);
+ if (*resp == NULL)
+ return -1;
+
+ for (i = 0; i < num_types; i++) {
+ if (types[i].vendor == EAP_VENDOR_IETF &&
+ types[i].method < 256)
+ wpabuf_put_u8(*resp, types[i].method);
+ }
+
+ eap_update_len(*resp);
+
+ return 0;
+}
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
new file mode 100644
index 0000000..e9e0998
--- /dev/null
+++ b/src/eap_peer/eap_tls_common.h
@@ -0,0 +1,126 @@
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_TLS_COMMON_H
+#define EAP_TLS_COMMON_H
+
+/**
+ * struct eap_ssl_data - TLS data for EAP methods
+ */
+struct eap_ssl_data {
+ /**
+ * conn - TLS connection context data from tls_connection_init()
+ */
+ struct tls_connection *conn;
+
+ /**
+ * tls_out - TLS message to be sent out in fragments
+ */
+ struct wpabuf *tls_out;
+
+ /**
+ * tls_out_pos - The current position in the outgoing TLS message
+ */
+ size_t tls_out_pos;
+
+ /**
+ * tls_out_limit - Maximum fragment size for outgoing TLS messages
+ */
+ size_t tls_out_limit;
+
+ /**
+ * tls_in - Received TLS message buffer for re-assembly
+ */
+ struct wpabuf *tls_in;
+
+ /**
+ * tls_in_left - Number of remaining bytes in the incoming TLS message
+ */
+ size_t tls_in_left;
+
+ /**
+ * tls_in_total - Total number of bytes in the incoming TLS message
+ */
+ size_t tls_in_total;
+
+ /**
+ * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
+ */
+ int phase2;
+
+ /**
+ * include_tls_length - Whether the TLS length field is included even
+ * if the TLS data is not fragmented
+ */
+ int include_tls_length;
+
+ /**
+ * tls_ia - Whether TLS/IA is enabled for this TLS connection
+ */
+ int tls_ia;
+
+ /**
+ * eap - EAP state machine allocated with eap_peer_sm_init()
+ */
+ struct eap_sm *eap;
+};
+
+
+/* EAP TLS Flags */
+#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TLS_FLAGS_START 0x20
+#define EAP_TLS_VERSION_MASK 0x07
+
+ /* could be up to 128 bytes, but only the first 64 bytes are used */
+#define EAP_TLS_KEY_LEN 64
+
+
+int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+ struct eap_peer_config *config);
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ const char *label, size_t len);
+int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version,
+ u8 id, const u8 *in_data, size_t in_len,
+ struct wpabuf **out_data);
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+ int peap_version);
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data);
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *buf, size_t buflen, int verbose);
+const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ EapType eap_type,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ size_t *len, u8 *flags);
+void eap_peer_tls_reset_input(struct eap_ssl_data *data);
+void eap_peer_tls_reset_output(struct eap_ssl_data *data);
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ const struct wpabuf *in_data,
+ struct wpabuf **in_decrypted);
+int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+ EapType eap_type, int peap_version, u8 id,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data);
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+ const char *prefix,
+ struct eap_method_type **types,
+ size_t *num_types);
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+ struct eap_hdr *hdr, struct wpabuf **resp);
+
+#endif /* EAP_TLS_COMMON_H */
diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c
new file mode 100644
index 0000000..6c95f72
--- /dev/null
+++ b/src/eap_peer/eap_tnc.c
@@ -0,0 +1,434 @@
+/*
+ * EAP peer method: EAP-TNC (Trusted Network Connect)
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "eap_i.h"
+#include "tncc.h"
+
+
+struct eap_tnc_data {
+ enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state;
+ struct tncc_data *tncc;
+ struct wpabuf *in_buf;
+ struct wpabuf *out_buf;
+ size_t out_used;
+ size_t fragment_size;
+};
+
+
+/* EAP-TNC Flags */
+#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TNC_FLAGS_START 0x20
+#define EAP_TNC_VERSION_MASK 0x07
+
+#define EAP_TNC_VERSION 1
+
+
+static void * eap_tnc_init(struct eap_sm *sm)
+{
+ struct eap_tnc_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = WAIT_START;
+ data->fragment_size = 1300;
+ data->tncc = tncc_init();
+ if (data->tncc == NULL) {
+ os_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_tnc_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_tnc_data *data = priv;
+
+ wpabuf_free(data->in_buf);
+ wpabuf_free(data->out_buf);
+ tncc_deinit(data->tncc);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code)
+{
+ struct wpabuf *msg;
+
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory "
+ "for fragment ack");
+ return NULL;
+ }
+ wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */
+
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack");
+
+ return msg;
+}
+
+
+static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data,
+ struct eap_method_ret *ret, u8 id)
+{
+ struct wpabuf *resp;
+ u8 flags;
+ size_t send_len, plen;
+
+ ret->ignore = FALSE;
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Response");
+ ret->allowNotifications = TRUE;
+
+ flags = EAP_TNC_VERSION;
+ send_len = wpabuf_len(data->out_buf) - data->out_used;
+ if (1 + send_len > data->fragment_size) {
+ send_len = data->fragment_size - 1;
+ flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS;
+ if (data->out_used == 0) {
+ flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED;
+ send_len -= 4;
+ }
+ }
+
+ plen = 1 + send_len;
+ if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
+ plen += 4;
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen,
+ EAP_CODE_RESPONSE, id);
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, flags); /* Flags */
+ if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
+ wpabuf_put_be32(resp, wpabuf_len(data->out_buf));
+
+ wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
+ send_len);
+ data->out_used += send_len;
+
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+
+ if (data->out_used == wpabuf_len(data->out_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
+ "(message sent completely)",
+ (unsigned long) send_len);
+ wpabuf_free(data->out_buf);
+ data->out_buf = NULL;
+ data->out_used = 0;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
+ "(%lu more to send)", (unsigned long) send_len,
+ (unsigned long) wpabuf_len(data->out_buf) -
+ data->out_used);
+ data->state = WAIT_FRAG_ACK;
+ }
+
+ return resp;
+}
+
+
+static int eap_tnc_process_cont(struct eap_tnc_data *data,
+ const u8 *buf, size_t len)
+{
+ /* Process continuation of a pending message */
+ if (len > wpabuf_tailroom(data->in_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow");
+ data->state = FAIL;
+ return -1;
+ }
+
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for "
+ "%lu bytes more", (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_tnc_process_fragment(struct eap_tnc_data *data,
+ struct eap_method_ret *ret,
+ u8 id, u8 flags,
+ u32 message_length,
+ const u8 *buf, size_t len)
+{
+ /* Process a fragment that is not the last one of the message */
+ if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a "
+ "fragmented packet");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->in_buf == NULL) {
+ /* First fragment of the message */
+ data->in_buf = wpabuf_alloc(message_length);
+ if (data->in_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for "
+ "message");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first "
+ "fragment, waiting for %lu bytes more",
+ (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+ }
+
+ return eap_tnc_build_frag_ack(id, EAP_CODE_RESPONSE);
+}
+
+
+static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_tnc_data *data = priv;
+ struct wpabuf *resp;
+ const u8 *pos, *end;
+ u8 *rpos, *rpos1;
+ size_t len, rlen;
+ size_t imc_len;
+ char *start_buf, *end_buf;
+ size_t start_len, end_len;
+ int tncs_done = 0;
+ u8 flags, id;
+ u32 message_length = 0;
+ struct wpabuf tmpbuf;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, reqData, &len);
+ if (pos == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (pos=%p len=%lu)",
+ pos, (unsigned long) len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ id = eap_get_id(reqData);
+
+ end = pos + len;
+
+ if (len == 0)
+ flags = 0; /* fragment ack */
+ else
+ flags = *pos++;
+
+ if (len > 0 && (flags & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
+ flags & EAP_TNC_VERSION_MASK);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ message_length = WPA_GET_BE32(pos);
+ pos += 4;
+
+ if (message_length < (u32) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
+ "Length (%d; %ld remaining in this msg)",
+ message_length, (long) (end - pos));
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x "
+ "Message Length %u", flags, message_length);
+
+ if (data->state == WAIT_FRAG_ACK) {
+ if (len > 1) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload in "
+ "WAIT_FRAG_ACK state");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
+ data->state = PROC_MSG;
+ return eap_tnc_build_msg(data, ret, id);
+ }
+
+ if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
+ return eap_tnc_process_fragment(data, ret, id, flags,
+ message_length, pos,
+ end - pos);
+ }
+
+ if (data->in_buf == NULL) {
+ /* Wrap unfragmented messages as wpabuf without extra copy */
+ wpabuf_set(&tmpbuf, pos, end - pos);
+ data->in_buf = &tmpbuf;
+ }
+
+ if (data->state == WAIT_START) {
+ if (!(flags & EAP_TNC_FLAGS_START)) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Server did not use "
+ "start flag in the first message");
+ ret->ignore = TRUE;
+ goto fail;
+ }
+
+ tncc_init_connection(data->tncc);
+
+ data->state = PROC_MSG;
+ } else {
+ enum tncc_process_res res;
+
+ if (flags & EAP_TNC_FLAGS_START) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: Server used start "
+ "flag again");
+ ret->ignore = TRUE;
+ goto fail;
+ }
+
+ res = tncc_process_if_tnccs(data->tncc,
+ wpabuf_head(data->in_buf),
+ wpabuf_len(data->in_buf));
+ switch (res) {
+ case TNCCS_PROCESS_ERROR:
+ ret->ignore = TRUE;
+ goto fail;
+ case TNCCS_PROCESS_OK_NO_RECOMMENDATION:
+ case TNCCS_RECOMMENDATION_ERROR:
+ wpa_printf(MSG_DEBUG, "EAP-TNC: No "
+ "TNCCS-Recommendation received");
+ break;
+ case TNCCS_RECOMMENDATION_ALLOW:
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ "TNC: Recommendation = allow");
+ tncs_done = 1;
+ break;
+ case TNCCS_RECOMMENDATION_NONE:
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ "TNC: Recommendation = none");
+ tncs_done = 1;
+ break;
+ case TNCCS_RECOMMENDATION_ISOLATE:
+ wpa_msg(sm->msg_ctx, MSG_INFO,
+ "TNC: Recommendation = isolate");
+ tncs_done = 1;
+ break;
+ }
+ }
+
+ if (data->in_buf != &tmpbuf)
+ wpabuf_free(data->in_buf);
+ data->in_buf = NULL;
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_UNCOND_SUCC;
+ ret->allowNotifications = TRUE;
+
+ if (data->out_buf) {
+ data->state = PROC_MSG;
+ return eap_tnc_build_msg(data, ret, id);
+ }
+
+ if (tncs_done) {
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, EAP_TNC_VERSION);
+ wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS done - reply with an "
+ "empty ACK message");
+ return resp;
+ }
+
+ imc_len = tncc_total_send_len(data->tncc);
+
+ start_buf = tncc_if_tnccs_start(data->tncc);
+ if (start_buf == NULL)
+ return NULL;
+ start_len = os_strlen(start_buf);
+ end_buf = tncc_if_tnccs_end();
+ if (end_buf == NULL) {
+ os_free(start_buf);
+ return NULL;
+ }
+ end_len = os_strlen(end_buf);
+
+ rlen = start_len + imc_len + end_len;
+ resp = wpabuf_alloc(rlen);
+ if (resp == NULL) {
+ os_free(start_buf);
+ os_free(end_buf);
+ return NULL;
+ }
+
+ wpabuf_put_data(resp, start_buf, start_len);
+ os_free(start_buf);
+
+ rpos1 = wpabuf_put(resp, 0);
+ rpos = tncc_copy_send_buf(data->tncc, rpos1);
+ wpabuf_put(resp, rpos - rpos1);
+
+ wpabuf_put_data(resp, end_buf, end_len);
+ os_free(end_buf);
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Response",
+ wpabuf_head(resp), wpabuf_len(resp));
+
+ data->out_buf = resp;
+ data->state = PROC_MSG;
+ return eap_tnc_build_msg(data, ret, id);
+
+fail:
+ if (data->in_buf == &tmpbuf)
+ data->in_buf = NULL;
+ return NULL;
+}
+
+
+int eap_peer_tnc_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_tnc_init;
+ eap->deinit = eap_tnc_deinit;
+ eap->process = eap_tnc_process;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
new file mode 100644
index 0000000..e8f0f38
--- /dev/null
+++ b/src/eap_peer/eap_ttls.c
@@ -0,0 +1,1986 @@
+/*
+ * EAP peer method: EAP-TTLS (RFC 5281)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/ms_funcs.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_common/chap.h"
+#include "eap_common/eap_ttls.h"
+#include "mschapv2.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
+/* Maximum supported TTLS version
+ * 0 = RFC 5281
+ * 1 = draft-funk-eap-ttls-v1-00.txt
+ */
+#ifndef EAP_TTLS_VERSION
+#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
+#endif /* EAP_TTLS_VERSION */
+
+
+#define MSCHAPV2_KEY_LEN 16
+#define MSCHAPV2_NT_RESPONSE_LEN 24
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_ttls_data {
+ struct eap_ssl_data ssl;
+ int ssl_initialized;
+
+ int ttls_version, force_ttls_version;
+
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int phase2_success;
+ int phase2_start;
+
+ enum phase2_types {
+ EAP_TTLS_PHASE2_EAP,
+ EAP_TTLS_PHASE2_MSCHAPV2,
+ EAP_TTLS_PHASE2_MSCHAP,
+ EAP_TTLS_PHASE2_PAP,
+ EAP_TTLS_PHASE2_CHAP
+ } phase2_type;
+ struct eap_method_type phase2_eap_type;
+ struct eap_method_type *phase2_eap_types;
+ size_t num_phase2_eap_types;
+
+ u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+ int auth_response_valid;
+ u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */
+ u8 ident;
+ int resuming; /* starting a resumed session */
+ int reauth; /* reauthentication */
+ u8 *key_data;
+
+ struct wpabuf *pending_phase2_req;
+
+#ifdef EAP_TNC
+ int ready_for_tnc;
+ int tnc_started;
+#endif /* EAP_TNC */
+};
+
+
+static void * eap_ttls_init(struct eap_sm *sm)
+{
+ struct eap_ttls_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+ char *selected;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->ttls_version = EAP_TTLS_VERSION;
+ data->force_ttls_version = -1;
+ selected = "EAP";
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;
+
+#if EAP_TTLS_VERSION > 0
+ if (config && config->phase1) {
+ const char *pos = os_strstr(config->phase1, "ttlsver=");
+ if (pos) {
+ data->force_ttls_version = atoi(pos + 8);
+ data->ttls_version = data->force_ttls_version;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version "
+ "%d", data->force_ttls_version);
+ }
+ }
+#endif /* EAP_TTLS_VERSION */
+
+ if (config && config->phase2) {
+ if (os_strstr(config->phase2, "autheap=")) {
+ selected = "EAP";
+ data->phase2_type = EAP_TTLS_PHASE2_EAP;
+ } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) {
+ selected = "MSCHAPV2";
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
+ } else if (os_strstr(config->phase2, "auth=MSCHAP")) {
+ selected = "MSCHAP";
+ data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;
+ } else if (os_strstr(config->phase2, "auth=PAP")) {
+ selected = "PAP";
+ data->phase2_type = EAP_TTLS_PHASE2_PAP;
+ } else if (os_strstr(config->phase2, "auth=CHAP")) {
+ selected = "CHAP";
+ data->phase2_type = EAP_TTLS_PHASE2_CHAP;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected);
+
+ if (data->phase2_type == EAP_TTLS_PHASE2_EAP) {
+ if (eap_peer_select_phase2_methods(config, "autheap=",
+ &data->phase2_eap_types,
+ &data->num_phase2_eap_types)
+ < 0) {
+ eap_ttls_deinit(sm, data);
+ return NULL;
+ }
+
+ data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_eap_type.method = EAP_TYPE_NONE;
+ }
+
+#if EAP_TTLS_VERSION > 0
+ if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
+ data->ttls_version > 0) {
+ if (data->force_ttls_version > 0) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
+ "TLS library does not support TLS/IA.",
+ data->force_ttls_version);
+ eap_ttls_deinit(sm, data);
+ return NULL;
+ }
+ data->ttls_version = 0;
+ }
+#endif /* EAP_TTLS_VERSION */
+
+ return data;
+}
+
+
+static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+ if (data->phase2_priv && data->phase2_method) {
+ data->phase2_method->deinit(sm, data->phase2_priv);
+ data->phase2_method = NULL;
+ data->phase2_priv = NULL;
+ }
+}
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ if (data == NULL)
+ return;
+ eap_ttls_phase2_eap_deinit(sm, data);
+ os_free(data->phase2_eap_types);
+ if (data->ssl_initialized)
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
+ os_free(data->key_data);
+ wpabuf_free(data->pending_phase2_req);
+ os_free(data);
+}
+
+
+static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
+ int mandatory, size_t len)
+{
+ struct ttls_avp_vendor *avp;
+ u8 flags;
+ size_t hdrlen;
+
+ avp = (struct ttls_avp_vendor *) avphdr;
+ flags = mandatory ? AVP_FLAGS_MANDATORY : 0;
+ if (vendor_id) {
+ flags |= AVP_FLAGS_VENDOR;
+ hdrlen = sizeof(*avp);
+ avp->vendor_id = host_to_be32(vendor_id);
+ } else {
+ hdrlen = sizeof(struct ttls_avp);
+ }
+
+ avp->avp_code = host_to_be32(avp_code);
+ avp->avp_length = host_to_be32((flags << 24) | (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;
+}
+
+
+#if EAP_TTLS_VERSION > 0
+static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ const u8 *key, size_t key_len)
+{
+ u8 *buf;
+ size_t buf_len;
+ int ret;
+
+ if (key) {
+ buf_len = 2 + key_len;
+ buf = os_malloc(buf_len);
+ if (buf == NULL)
+ return -1;
+ WPA_PUT_BE16(buf, key_len);
+ os_memcpy(buf + 2, key, key_len);
+ } else {
+ buf = NULL;
+ buf_len = 0;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
+ "secret permutation", buf, buf_len);
+ ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
+ data->ssl.conn,
+ buf, buf_len);
+ os_free(buf);
+
+ return ret;
+}
+#endif /* EAP_TTLS_VERSION */
+
+
+static int eap_ttls_v0_derive_key(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+ os_free(data->key_data);
+ data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
+ "ttls keying material",
+ EAP_TLS_KEY_LEN);
+ if (!data->key_data) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+ data->key_data, EAP_TLS_KEY_LEN);
+
+ return 0;
+}
+
+
+#if EAP_TTLS_VERSION > 0
+static int eap_ttls_v1_derive_key(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+ struct tls_keys keys;
+ u8 *rnd;
+
+ os_free(data->key_data);
+ data->key_data = NULL;
+
+ os_memset(&keys, 0, sizeof(keys));
+ if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
+ keys.client_random == NULL || keys.server_random == NULL ||
+ keys.inner_secret == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
+ "client random, or server random to derive keying "
+ "material");
+ return -1;
+ }
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ data->key_data = os_malloc(EAP_TLS_KEY_LEN);
+ if (rnd == NULL || data->key_data == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
+ os_free(rnd);
+ os_free(data->key_data);
+ data->key_data = NULL;
+ return -1;
+ }
+ os_memcpy(rnd, keys.client_random, keys.client_random_len);
+ os_memcpy(rnd + keys.client_random_len, keys.server_random,
+ keys.server_random_len);
+
+ if (tls_prf(keys.inner_secret, keys.inner_secret_len,
+ "ttls v1 keying material", rnd, keys.client_random_len +
+ keys.server_random_len, data->key_data, EAP_TLS_KEY_LEN)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
+ os_free(rnd);
+ os_free(data->key_data);
+ data->key_data = NULL;
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
+ rnd, keys.client_random_len + keys.server_random_len);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
+ keys.inner_secret, keys.inner_secret_len);
+
+ os_free(rnd);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+ data->key_data, EAP_TLS_KEY_LEN);
+
+ return 0;
+}
+#endif /* EAP_TTLS_VERSION */
+
+
+static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
+ struct eap_ttls_data *data, size_t len)
+{
+#if EAP_TTLS_VERSION > 0
+ struct tls_keys keys;
+ u8 *challenge, *rnd;
+#endif /* EAP_TTLS_VERSION */
+
+ if (data->ttls_version == 0) {
+ return eap_peer_tls_derive_key(sm, &data->ssl,
+ "ttls challenge", len);
+ }
+
+#if EAP_TTLS_VERSION > 0
+
+ os_memset(&keys, 0, sizeof(keys));
+ if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
+ keys.client_random == NULL || keys.server_random == NULL ||
+ keys.inner_secret == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
+ "client random, or server random to derive "
+ "implicit challenge");
+ return NULL;
+ }
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ challenge = os_malloc(len);
+ if (rnd == NULL || challenge == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
+ "challenge derivation");
+ os_free(rnd);
+ os_free(challenge);
+ return NULL;
+ }
+ os_memcpy(rnd, keys.server_random, keys.server_random_len);
+ os_memcpy(rnd + keys.server_random_len, keys.client_random,
+ keys.client_random_len);
+
+ if (tls_prf(keys.inner_secret, keys.inner_secret_len,
+ "inner application challenge", rnd,
+ keys.client_random_len + keys.server_random_len,
+ challenge, len)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
+ "challenge");
+ os_free(rnd);
+ os_free(challenge);
+ return NULL;
+ }
+
+ os_free(rnd);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
+ challenge, len);
+
+ return challenge;
+
+#else /* EAP_TTLS_VERSION */
+
+ return NULL;
+
+#endif /* EAP_TTLS_VERSION */
+}
+
+
+static void eap_ttlsv1_phase2_eap_finish(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret)
+{
+#if EAP_TTLS_VERSION > 0
+ if (data->ttls_version > 0) {
+ const struct eap_method *m = data->phase2_method;
+ void *priv = data->phase2_priv;
+
+ /* TTLSv1 requires TLS/IA FinalPhaseFinished */
+ if (ret->decision == DECISION_UNCOND_SUCC)
+ ret->decision = DECISION_COND_SUCC;
+ ret->methodState = METHOD_CONT;
+
+ if (ret->decision == DECISION_COND_SUCC &&
+ m->isKeyAvailable && m->getKey &&
+ m->isKeyAvailable(sm, priv)) {
+ u8 *key;
+ size_t key_len;
+ key = m->getKey(sm, priv, &key_len);
+ if (key) {
+ eap_ttls_ia_permute_inner_secret(
+ sm, data, key, key_len);
+ os_free(key);
+ }
+ }
+ }
+#endif /* EAP_TTLS_VERSION */
+}
+
+
+static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,
+ u8 method)
+{
+ size_t i;
+ for (i = 0; i < data->num_phase2_eap_types; i++) {
+ if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF ||
+ data->phase2_eap_types[i].method != method)
+ continue;
+
+ data->phase2_eap_type.vendor =
+ data->phase2_eap_types[i].vendor;
+ data->phase2_eap_type.method =
+ data->phase2_eap_types[i].method;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+ "Phase 2 EAP vendor %d method %d",
+ data->phase2_eap_type.vendor,
+ data->phase2_eap_type.method);
+ break;
+ }
+}
+
+
+static int eap_ttls_phase2_eap_process(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr, size_t len,
+ struct wpabuf **resp)
+{
+ struct wpabuf msg;
+ struct eap_method_ret iret;
+
+ os_memset(&iret, 0, sizeof(iret));
+ wpabuf_set(&msg, hdr, len);
+ *resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+ &msg);
+ if ((iret.methodState == METHOD_DONE ||
+ iret.methodState == METHOD_MAY_CONT) &&
+ (iret.decision == DECISION_UNCOND_SUCC ||
+ iret.decision == DECISION_COND_SUCC ||
+ iret.decision == DECISION_FAIL)) {
+ ret->methodState = iret.methodState;
+ ret->decision = iret.decision;
+ }
+ eap_ttlsv1_phase2_eap_finish(sm, data, ret);
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr, size_t len,
+ u8 method, struct wpabuf **resp)
+{
+#ifdef EAP_TNC
+ if (data->tnc_started && data->phase2_method &&
+ data->phase2_priv && method == EAP_TYPE_TNC &&
+ data->phase2_eap_type.method == EAP_TYPE_TNC)
+ return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len,
+ resp);
+
+ if (data->ready_for_tnc && !data->tnc_started &&
+ method == EAP_TYPE_TNC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+ "EAP method");
+ data->tnc_started = 1;
+ }
+
+ if (data->tnc_started) {
+ if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF ||
+ data->phase2_eap_type.method == EAP_TYPE_TNC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP "
+ "type %d for TNC", method);
+ return -1;
+ }
+
+ data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+ data->phase2_eap_type.method = method;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+ "Phase 2 EAP vendor %d method %d (TNC)",
+ data->phase2_eap_type.vendor,
+ data->phase2_eap_type.method);
+
+ if (data->phase2_type == EAP_TTLS_PHASE2_EAP)
+ eap_ttls_phase2_eap_deinit(sm, data);
+ }
+#endif /* EAP_TNC */
+
+ if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF &&
+ data->phase2_eap_type.method == EAP_TYPE_NONE)
+ eap_ttls_phase2_select_eap_method(data, method);
+
+ if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE)
+ {
+ if (eap_peer_tls_phase2_nak(data->phase2_eap_types,
+ data->num_phase2_eap_types,
+ hdr, resp))
+ return -1;
+ return 0;
+ }
+
+ if (data->phase2_priv == NULL) {
+ data->phase2_method = eap_peer_get_eap_method(
+ EAP_VENDOR_IETF, method);
+ if (data->phase2_method) {
+ sm->init_phase2 = 1;
+ data->phase2_priv = data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ }
+ }
+ if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize "
+ "Phase 2 EAP method %d", method);
+ return -1;
+ }
+
+ return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp);
+}
+
+
+static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr,
+ struct wpabuf **resp)
+{
+ size_t len = be_to_host16(hdr->length);
+ u8 *pos;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ if (len <= sizeof(struct eap_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: too short "
+ "Phase 2 request (len=%lu)", (unsigned long) len);
+ return -1;
+ }
+ pos = (u8 *) (hdr + 1);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos);
+ switch (*pos) {
+ case EAP_TYPE_IDENTITY:
+ *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+ break;
+ default:
+ if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len,
+ *pos, resp) < 0)
+ return -1;
+ break;
+ }
+
+ if (*resp == NULL &&
+ (config->pending_req_identity || config->pending_req_password ||
+ config->pending_req_otp)) {
+ return 0;
+ }
+
+ if (*resp == NULL)
+ return -1;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response",
+ *resp);
+ return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1);
+}
+
+
+static void eap_ttlsv1_permute_inner(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+#if EAP_TTLS_VERSION > 0
+ u8 session_key[2 * MSCHAPV2_KEY_LEN];
+
+ if (data->ttls_version == 0)
+ return;
+
+ get_asymetric_start_key(data->master_key, session_key,
+ MSCHAPV2_KEY_LEN, 0, 0);
+ get_asymetric_start_key(data->master_key,
+ session_key + MSCHAPV2_KEY_LEN,
+ MSCHAPV2_KEY_LEN, 1, 0);
+ eap_ttls_ia_permute_inner_secret(sm, data, session_key,
+ sizeof(session_key));
+#endif /* EAP_TTLS_VERSION */
+}
+
+
+static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+ struct wpabuf *msg;
+ u8 *buf, *pos, *challenge, *peer_challenge;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + 1000);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/MSCHAPV2: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* MS-CHAP-Challenge */
+ challenge = eap_ttls_implicit_challenge(
+ sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);
+ if (challenge == NULL) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+ peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+
+ /* MS-CHAP2-Response */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ EAP_TTLS_MSCHAPV2_RESPONSE_LEN);
+ data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
+ *pos++ = data->ident;
+ *pos++ = 0; /* Flags */
+ os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+ pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
+ os_memset(pos, 0, 8); /* Reserved, must be zero */
+ pos += 8;
+ if (mschapv2_derive_response(identity, identity_len, password,
+ password_len, pwhash, challenge,
+ peer_challenge, pos, data->auth_response,
+ data->master_key)) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
+ "response");
+ return -1;
+ }
+ data->auth_response_valid = 1;
+
+ eap_ttlsv1_permute_inner(sm, data);
+
+ pos += 24;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ if (sm->workaround && data->ttls_version == 0) {
+ /* At least FreeRADIUS seems to be terminating
+ * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success
+ * packet. */
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - "
+ "allow success without tunneled response");
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+ struct wpabuf *msg;
+ u8 *buf, *pos, *challenge;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+ int pwhash;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password2(sm, &password_len, &pwhash);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + 1000);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/MSCHAP: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* MS-CHAP-Challenge */
+ challenge = eap_ttls_implicit_challenge(
+ sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
+ if (challenge == NULL) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+
+ /* MS-CHAP-Response */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE,
+ RADIUS_VENDOR_ID_MICROSOFT, 1,
+ EAP_TTLS_MSCHAP_RESPONSE_LEN);
+ data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN];
+ *pos++ = data->ident;
+ *pos++ = 1; /* Flags: Use NT style passwords */
+ os_memset(pos, 0, 24); /* LM-Response */
+ pos += 24;
+ if (pwhash) {
+ challenge_response(challenge, password, pos); /* NT-Response */
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash",
+ password, 16);
+ } else {
+ nt_challenge_response(challenge, password, password_len,
+ pos); /* NT-Response */
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password",
+ password, password_len);
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge",
+ challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24);
+ pos += 24;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ if (data->ttls_version > 0) {
+ /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
+ * so do not allow connection to be terminated yet. */
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ } else {
+ /* EAP-TTLS/MSCHAP does not provide tunneled success
+ * notification, so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_pap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+ struct wpabuf *msg;
+ u8 *buf, *pos;
+ size_t pad;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password(sm, &password_len);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + password_len + 100);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/PAP: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts
+ * the data, so no separate encryption is used in the AVP itself.
+ * However, the password is padded to obfuscate its length. */
+ pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15;
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1,
+ password_len + pad);
+ os_memcpy(pos, password, password_len);
+ pos += password_len;
+ os_memset(pos, 0, pad);
+ pos += pad;
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ if (data->ttls_version > 0) {
+ /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
+ * so do not allow connection to be terminated yet. */
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ } else {
+ /* EAP-TTLS/PAP does not provide tunneled success notification,
+ * so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct wpabuf **resp)
+{
+ struct wpabuf *msg;
+ u8 *buf, *pos, *challenge;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request");
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ password = eap_get_config_password(sm, &password_len);
+ if (identity == NULL || password == NULL)
+ return -1;
+
+ msg = wpabuf_alloc(identity_len + 1000);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR,
+ "EAP-TTLS/CHAP: Failed to allocate memory");
+ return -1;
+ }
+ pos = buf = wpabuf_mhead(msg);
+
+ /* User-Name */
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+ identity, identity_len);
+
+ /* CHAP-Challenge */
+ challenge = eap_ttls_implicit_challenge(
+ sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
+ if (challenge == NULL) {
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive "
+ "implicit challenge");
+ return -1;
+ }
+
+ pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1,
+ challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+
+ /* CHAP-Password */
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1,
+ 1 + EAP_TTLS_CHAP_PASSWORD_LEN);
+ data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN];
+ *pos++ = data->ident;
+
+ /* MD5(Ident + Password + Challenge) */
+ chap_md5(data->ident, password, password_len, challenge,
+ EAP_TTLS_CHAP_CHALLENGE_LEN, pos);
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username",
+ identity, identity_len);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password",
+ password, password_len);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge",
+ challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password",
+ pos, EAP_TTLS_CHAP_PASSWORD_LEN);
+ pos += EAP_TTLS_CHAP_PASSWORD_LEN;
+ os_free(challenge);
+ AVP_PAD(buf, pos);
+
+ wpabuf_put(msg, pos - buf);
+ *resp = msg;
+
+ if (data->ttls_version > 0) {
+ /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
+ * so do not allow connection to be terminated yet. */
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ } else {
+ /* EAP-TTLS/CHAP does not provide tunneled success
+ * notification, so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_phase2_request(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct eap_hdr *hdr,
+ struct wpabuf **resp)
+{
+ int res = 0;
+ size_t len;
+ enum phase2_types phase2_type = data->phase2_type;
+
+#ifdef EAP_TNC
+ if (data->tnc_started) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC");
+ phase2_type = EAP_TTLS_PHASE2_EAP;
+ }
+#endif /* EAP_TNC */
+
+ if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 ||
+ phase2_type == EAP_TTLS_PHASE2_MSCHAP ||
+ phase2_type == EAP_TTLS_PHASE2_PAP ||
+ phase2_type == EAP_TTLS_PHASE2_CHAP) {
+ if (eap_get_config_identity(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO,
+ "EAP-TTLS: Identity not configured");
+ eap_sm_request_identity(sm);
+ if (eap_get_config_password(sm, &len) == NULL)
+ eap_sm_request_password(sm);
+ return 0;
+ }
+
+ if (eap_get_config_password(sm, &len) == NULL) {
+ wpa_printf(MSG_INFO,
+ "EAP-TTLS: Password not configured");
+ eap_sm_request_password(sm);
+ return 0;
+ }
+ }
+
+ switch (phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp);
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);
+ break;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ res = eap_ttls_phase2_request_mschap(sm, data, ret, resp);
+ break;
+ case EAP_TTLS_PHASE2_PAP:
+ res = eap_ttls_phase2_request_pap(sm, data, ret, resp);
+ break;
+ case EAP_TTLS_PHASE2_CHAP:
+ res = eap_ttls_phase2_request_chap(sm, data, ret, resp);
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown");
+ res = -1;
+ break;
+ }
+
+ if (res < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return res;
+}
+
+
+#if EAP_TTLS_VERSION > 0
+static struct wpabuf * eap_ttls_build_phase_finished(
+ struct eap_sm *sm, struct eap_ttls_data *data, int id, int final)
+{
+ struct wpabuf *req, *buf;
+
+ buf = tls_connection_ia_send_phase_finished(sm->ssl_ctx,
+ data->ssl.conn,
+ final);
+ if (buf == NULL)
+ return NULL;
+
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS,
+ 1 + wpabuf_len(buf),
+ EAP_CODE_RESPONSE, id);
+ if (req == NULL) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ wpabuf_put_u8(req, data->ttls_version);
+ wpabuf_put_buf(req, buf);
+ wpabuf_free(buf);
+ eap_update_len(req);
+
+ return req;
+}
+#endif /* EAP_TTLS_VERSION */
+
+
+struct ttls_parse_avp {
+ u8 *mschapv2;
+ u8 *eapdata;
+ size_t eap_len;
+ int mschapv2_error;
+};
+
+
+static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen,
+ struct ttls_parse_avp *parse)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
+ if (parse->eapdata == NULL) {
+ parse->eapdata = os_malloc(dlen);
+ if (parse->eapdata == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+ "memory for Phase 2 EAP data");
+ return -1;
+ }
+ os_memcpy(parse->eapdata, dpos, dlen);
+ parse->eap_len = dlen;
+ } else {
+ u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen);
+ if (neweap == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+ "memory for Phase 2 EAP data");
+ return -1;
+ }
+ os_memcpy(neweap + parse->eap_len, dpos, dlen);
+ parse->eapdata = neweap;
+ parse->eap_len += dlen;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_parse_avp(u8 *pos, size_t left,
+ struct ttls_parse_avp *parse)
+{
+ struct ttls_avp *avp;
+ u32 avp_code, avp_length, vendor_id = 0;
+ u8 avp_flags, *dpos;
+ size_t dlen;
+
+ avp = (struct ttls_avp *) pos;
+ avp_code = be_to_host32(avp->avp_code);
+ avp_length = be_to_host32(avp->avp_length);
+ avp_flags = (avp_length >> 24) & 0xff;
+ avp_length &= 0xffffff;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "
+ "length=%d", (int) avp_code, avp_flags,
+ (int) avp_length);
+
+ if (avp_length > left) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow "
+ "(len=%d, left=%lu) - dropped",
+ (int) avp_length, (unsigned long) left);
+ return -1;
+ }
+
+ if (avp_length < sizeof(*avp)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d",
+ avp_length);
+ return -1;
+ }
+
+ dpos = (u8 *) (avp + 1);
+ dlen = avp_length - sizeof(*avp);
+ if (avp_flags & AVP_FLAGS_VENDOR) {
+ if (dlen < 4) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP "
+ "underflow");
+ return -1;
+ }
+ vendor_id = WPA_GET_BE32(dpos);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d",
+ (int) vendor_id);
+ dpos += 4;
+ dlen -= 4;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
+
+ if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
+ if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0)
+ return -1;
+ } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) {
+ /* This is an optional message that can be displayed to
+ * the user. */
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message",
+ dpos, dlen);
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success",
+ dpos, dlen);
+ if (dlen != 43) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected "
+ "MS-CHAP2-Success length "
+ "(len=%lu, expected 43)",
+ (unsigned long) dlen);
+ return -1;
+ }
+ parse->mschapv2 = dpos;
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP_ERROR) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error",
+ dpos, dlen);
+ parse->mschapv2_error = 1;
+ } else if (avp_flags & AVP_FLAGS_MANDATORY) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP "
+ "code %d vendor_id %d - dropped",
+ (int) avp_code, (int) vendor_id);
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP "
+ "code %d vendor_id %d",
+ (int) avp_code, (int) vendor_id);
+ }
+
+ return avp_length;
+}
+
+
+static int eap_ttls_parse_avps(struct wpabuf *in_decrypted,
+ struct ttls_parse_avp *parse)
+{
+ u8 *pos;
+ size_t left, pad;
+ int avp_length;
+
+ pos = wpabuf_mhead(in_decrypted);
+ left = wpabuf_len(in_decrypted);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left);
+ if (left < sizeof(struct ttls_avp)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame"
+ " len=%lu expected %lu or more - dropped",
+ (unsigned long) left,
+ (unsigned long) sizeof(struct ttls_avp));
+ return -1;
+ }
+
+ /* Parse AVPs */
+ os_memset(parse, 0, sizeof(*parse));
+
+ while (left > 0) {
+ avp_length = eap_ttls_parse_avp(pos, left, parse);
+ if (avp_length < 0)
+ return -1;
+
+ pad = (4 - (avp_length & 3)) & 3;
+ pos += avp_length + pad;
+ if (left < avp_length + pad)
+ left = 0;
+ else
+ left -= avp_length + pad;
+ }
+
+ return 0;
+}
+
+
+static u8 * eap_ttls_fake_identity_request(void)
+{
+ struct eap_hdr *hdr;
+ u8 *buf;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of "
+ "Phase 2 - use fake EAP-Request Identity");
+ buf = os_malloc(sizeof(*hdr) + 1);
+ if (buf == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate "
+ "memory for fake EAP-Identity Request");
+ return NULL;
+ }
+
+ hdr = (struct eap_hdr *) buf;
+ hdr->code = EAP_CODE_REQUEST;
+ hdr->identifier = 0;
+ hdr->length = host_to_be16(sizeof(*hdr) + 1);
+ buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY;
+
+ return buf;
+}
+
+
+static int eap_ttls_encrypt_response(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct wpabuf *resp, u8 identifier,
+ struct wpabuf **out_data)
+{
+ if (resp == NULL)
+ return 0;
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data",
+ resp);
+ if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, identifier,
+ resp, out_data)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 "
+ "frame");
+ return -1;
+ }
+ wpabuf_free(resp);
+
+ return 0;
+}
+
+
+static int eap_ttls_process_phase2_eap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse,
+ struct wpabuf **resp)
+{
+ struct eap_hdr *hdr;
+ size_t len;
+
+ if (parse->eapdata == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the "
+ "packet - dropped");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP",
+ parse->eapdata, parse->eap_len);
+ hdr = (struct eap_hdr *) parse->eapdata;
+
+ if (parse->eap_len < sizeof(*hdr)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP "
+ "frame (len=%lu, expected %lu or more) - dropped",
+ (unsigned long) parse->eap_len,
+ (unsigned long) sizeof(*hdr));
+ return -1;
+ }
+ len = be_to_host16(hdr->length);
+ if (len > parse->eap_len) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 "
+ "EAP frame (EAP hdr len=%lu, EAP data len in "
+ "AVP=%lu)",
+ (unsigned long) len,
+ (unsigned long) parse->eap_len);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d "
+ "identifier=%d length=%lu",
+ hdr->code, hdr->identifier, (unsigned long) len);
+ switch (hdr->code) {
+ case EAP_CODE_REQUEST:
+ if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+ "processing failed");
+ return -1;
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse)
+{
+ if (parse->mschapv2_error) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received "
+ "MS-CHAP-Error - failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ /* Reply with empty data to ACK error */
+ return 1;
+ }
+
+ if (parse->mschapv2 == NULL) {
+#ifdef EAP_TNC
+ if (data->phase2_success && parse->eapdata) {
+ /*
+ * Allow EAP-TNC to be started after successfully
+ * completed MSCHAPV2.
+ */
+ return 1;
+ }
+#endif /* EAP_TNC */
+ wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP "
+ "received for Phase2 MSCHAPV2");
+ return -1;
+ }
+ if (parse->mschapv2[0] != data->ident) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 "
+ "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)",
+ parse->mschapv2[0], data->ident);
+ return -1;
+ }
+ if (!data->auth_response_valid ||
+ mschapv2_verify_auth_response(data->auth_response,
+ parse->mschapv2 + 1, 42)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator "
+ "response in Phase 2 MSCHAPV2 success request");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 "
+ "authentication succeeded");
+ if (data->ttls_version > 0) {
+ /*
+ * EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report
+ * success, so do not allow connection to be terminated
+ * yet.
+ */
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_COND_SUCC;
+ } else {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
+ }
+
+ /*
+ * Reply with empty data; authentication server will reply
+ * with EAP-Success after this.
+ */
+ return 1;
+}
+
+
+#ifdef EAP_TNC
+static int eap_ttls_process_tnc_start(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ struct ttls_parse_avp *parse,
+ struct wpabuf **resp)
+{
+ /* TNC uses inner EAP method after non-EAP TTLS phase 2. */
+ if (parse->eapdata == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
+ "unexpected tunneled data (no EAP)");
+ return -1;
+ }
+
+ if (!data->ready_for_tnc) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
+ "EAP after non-EAP, but not ready for TNC");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+ "non-EAP method");
+ data->tnc_started = 1;
+
+ if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0)
+ return -1;
+
+ return 0;
+}
+#endif /* EAP_TNC */
+
+
+static int eap_ttls_process_decrypted(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ struct ttls_parse_avp *parse,
+ struct wpabuf *in_decrypted,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *resp = NULL;
+ struct eap_peer_config *config = eap_get_config(sm);
+ int res;
+ enum phase2_types phase2_type = data->phase2_type;
+
+#ifdef EAP_TNC
+ if (data->tnc_started)
+ phase2_type = EAP_TTLS_PHASE2_EAP;
+#endif /* EAP_TNC */
+
+ switch (phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) <
+ 0)
+ return -1;
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse);
+#ifdef EAP_TNC
+ if (res == 1 && parse->eapdata && data->phase2_success) {
+ /*
+ * TNC may be required as the next
+ * authentication method within the tunnel.
+ */
+ ret->methodState = METHOD_MAY_CONT;
+ data->ready_for_tnc = 1;
+ if (eap_ttls_process_tnc_start(sm, data, ret, parse,
+ &resp) == 0)
+ break;
+ }
+#endif /* EAP_TNC */
+ return res;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ case EAP_TTLS_PHASE2_PAP:
+ case EAP_TTLS_PHASE2_CHAP:
+#ifdef EAP_TNC
+ if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) <
+ 0)
+ return -1;
+ break;
+#else /* EAP_TNC */
+ /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled
+ * requests to the supplicant */
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected "
+ "tunneled data");
+ return -1;
+#endif /* EAP_TNC */
+ }
+
+ if (resp) {
+ if (eap_ttls_encrypt_response(sm, data, resp, identifier,
+ out_data) < 0)
+ return -1;
+ } else if (config->pending_req_identity ||
+ config->pending_req_password ||
+ config->pending_req_otp ||
+ config->pending_req_new_password) {
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_dup(in_decrypted);
+ }
+
+ return 0;
+}
+
+
+#if EAP_TTLS_VERSION > 0
+static void eap_ttls_final_phase_finished(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ struct wpabuf **out_data)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received");
+ wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication succeeded");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
+ *out_data = eap_ttls_build_phase_finished(sm, data, identifier, 1);
+ eap_ttls_v1_derive_key(sm, data);
+}
+#endif /* EAP_TTLS_VERSION */
+
+
+static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ struct wpabuf **out_data)
+{
+ int retval = 0;
+ struct eap_hdr *hdr;
+ struct wpabuf *resp;
+
+ hdr = (struct eap_hdr *) eap_ttls_fake_identity_request();
+ if (hdr == NULL) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
+
+ resp = NULL;
+ if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+ "processing failed");
+ retval = -1;
+ } else {
+ retval = eap_ttls_encrypt_response(sm, data, resp, identifier,
+ out_data);
+ }
+
+ os_free(hdr);
+
+ if (retval < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return retval;
+}
+
+
+static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data,
+ struct eap_method_ret *ret, u8 identifier,
+ struct wpabuf **out_data)
+{
+ data->phase2_start = 0;
+
+ /*
+ * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only
+ * if TLS part was indeed resuming a previous session. Most
+ * Authentication Servers terminate EAP-TTLS before reaching this
+ * point, but some do not. Make wpa_supplicant stop phase 2 here, if
+ * needed.
+ */
+ if (data->reauth &&
+ tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - "
+ "skip phase 2");
+ *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS,
+ data->ttls_version);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
+ return 0;
+ }
+
+ return eap_ttls_implicit_identity_request(sm, data, ret, identifier,
+ out_data);
+}
+
+
+static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,
+ struct eap_method_ret *ret, u8 identifier,
+ const struct wpabuf *in_data,
+ struct wpabuf **out_data)
+{
+ struct wpabuf *in_decrypted = NULL;
+ int retval = 0;
+ struct ttls_parse_avp parse;
+
+ os_memset(&parse, 0, sizeof(parse));
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
+ " Phase 2",
+ in_data ? (unsigned long) wpabuf_len(in_data) : 0);
+
+ if (data->pending_phase2_req) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - "
+ "skip decryption and use old data");
+ /* Clear TLS reassembly state. */
+ eap_peer_tls_reset_input(&data->ssl);
+
+ in_decrypted = data->pending_phase2_req;
+ data->pending_phase2_req = NULL;
+ if (wpabuf_len(in_decrypted) == 0) {
+ wpabuf_free(in_decrypted);
+ return eap_ttls_implicit_identity_request(
+ sm, data, ret, identifier, out_data);
+ }
+ goto continue_req;
+ }
+
+ if ((in_data == NULL || wpabuf_len(in_data) == 0) &&
+ data->phase2_start) {
+ return eap_ttls_phase2_start(sm, data, ret, identifier,
+ out_data);
+ }
+
+ if (in_data == NULL || wpabuf_len(in_data) == 0) {
+ /* Received TLS ACK - requesting more fragments */
+ return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version,
+ identifier, NULL, out_data);
+ }
+
+ retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+ if (retval)
+ goto done;
+
+#if EAP_TTLS_VERSION > 0
+ if (data->ttls_version > 0 &&
+ (in_decrypted == NULL || wpabuf_len(in_decrypted) == 0) &&
+ tls_connection_ia_final_phase_finished(sm->ssl_ctx,
+ data->ssl.conn)) {
+ eap_ttls_final_phase_finished(sm, data, ret, identifier,
+ out_data);
+ goto done;
+ }
+#endif /* EAP_TTLS_VERSION */
+
+continue_req:
+ data->phase2_start = 0;
+
+ if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) {
+ retval = -1;
+ goto done;
+ }
+
+ retval = eap_ttls_process_decrypted(sm, data, ret, identifier,
+ &parse, in_decrypted, out_data);
+
+done:
+ wpabuf_free(in_decrypted);
+ os_free(parse.eapdata);
+
+ if (retval < 0) {
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ }
+
+ return retval;
+}
+
+
+static int eap_ttls_process_start(struct eap_sm *sm,
+ struct eap_ttls_data *data, u8 flags,
+ struct eap_method_ret *ret)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)",
+ flags & EAP_TLS_VERSION_MASK, data->ttls_version);
+#if EAP_TTLS_VERSION > 0
+ if ((flags & EAP_TLS_VERSION_MASK) < data->ttls_version)
+ data->ttls_version = flags & EAP_TLS_VERSION_MASK;
+ if (data->force_ttls_version >= 0 &&
+ data->force_ttls_version != data->ttls_version) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select "
+ "forced TTLS version %d",
+ data->force_ttls_version);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d",
+ data->ttls_version);
+
+ if (data->ttls_version > 0)
+ data->ssl.tls_ia = 1;
+#endif /* EAP_TTLS_VERSION */
+ if (!data->ssl_initialized &&
+ eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+ return -1;
+ }
+ data->ssl_initialized = 1;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start");
+
+ return 0;
+}
+
+
+static int eap_ttls_process_handshake(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret,
+ u8 identifier,
+ const u8 *in_data, size_t in_len,
+ struct wpabuf **out_data)
+{
+ int res;
+
+ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, identifier,
+ in_data, in_len, out_data);
+
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
+ "Phase 2");
+ if (data->resuming) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may "
+ "skip Phase 2");
+ ret->decision = DECISION_COND_SUCC;
+ ret->methodState = METHOD_MAY_CONT;
+ }
+ data->phase2_start = 1;
+ if (data->ttls_version == 0)
+ eap_ttls_v0_derive_key(sm, data);
+
+ if (*out_data == NULL || wpabuf_len(*out_data) == 0) {
+ if (eap_ttls_decrypt(sm, data, ret, identifier,
+ NULL, out_data)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: "
+ "failed to process early "
+ "start for Phase 2");
+ }
+ res = 0;
+ }
+ data->resuming = 0;
+ }
+
+ if (res == 2) {
+ struct wpabuf msg;
+ /*
+ * Application data included in the handshake message.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = *out_data;
+ *out_data = NULL;
+ wpabuf_set(&msg, in_data, in_len);
+ res = eap_ttls_decrypt(sm, data, ret, identifier, &msg,
+ out_data);
+ }
+
+ return res;
+}
+
+
+static void eap_ttls_check_auth_status(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct eap_method_ret *ret)
+{
+ if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) {
+ ret->allowNotifications = FALSE;
+ if (ret->decision == DECISION_UNCOND_SUCC ||
+ ret->decision == DECISION_COND_SUCC) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+ "completed successfully");
+ data->phase2_success = 1;
+#ifdef EAP_TNC
+ if (!data->ready_for_tnc && !data->tnc_started) {
+ /*
+ * TNC may be required as the next
+ * authentication method within the tunnel.
+ */
+ ret->methodState = METHOD_MAY_CONT;
+ data->ready_for_tnc = 1;
+ }
+#endif /* EAP_TNC */
+ }
+ } else if (data->ttls_version == 0 &&
+ ret->methodState == METHOD_MAY_CONT &&
+ (ret->decision == DECISION_UNCOND_SUCC ||
+ ret->decision == DECISION_COND_SUCC)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+ "completed successfully (MAY_CONT)");
+ data->phase2_success = 1;
+ }
+}
+
+
+static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ size_t left;
+ int res;
+ u8 flags, id;
+ struct wpabuf *resp;
+ const u8 *pos;
+ struct eap_ttls_data *data = priv;
+
+ pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret,
+ reqData, &left, &flags);
+ if (pos == NULL)
+ return NULL;
+ id = eap_get_id(reqData);
+
+ if (flags & EAP_TLS_FLAGS_START) {
+ if (eap_ttls_process_start(sm, data, flags, ret) < 0)
+ return NULL;
+
+ /* RFC 5281, Ch. 9.2:
+ * "This packet MAY contain additional information in the form
+ * of AVPs, which may provide useful hints to the client"
+ * For now, ignore any potential extra data.
+ */
+ left = 0;
+ } else if (!data->ssl_initialized) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not "
+ "include Start flag");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+ return NULL;
+ }
+
+ resp = NULL;
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ !data->resuming) {
+ struct wpabuf msg;
+ wpabuf_set(&msg, pos, left);
+ res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp);
+ } else {
+ res = eap_ttls_process_handshake(sm, data, ret, id,
+ pos, left, &resp);
+ }
+
+ eap_ttls_check_auth_status(sm, data, ret);
+
+ /* FIX: what about res == -1? Could just move all error processing into
+ * the other functions and get rid of this res==1 case here. */
+ if (res == 1) {
+ wpabuf_free(resp);
+ return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS,
+ data->ttls_version);
+ }
+ return resp;
+}
+
+
+static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+ data->phase2_success;
+}
+
+
+static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = NULL;
+#ifdef EAP_TNC
+ data->ready_for_tnc = 0;
+ data->tnc_started = 0;
+#endif /* EAP_TNC */
+}
+
+
+static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ os_free(data->key_data);
+ data->key_data = NULL;
+ if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+ os_free(data);
+ return NULL;
+ }
+ if (data->phase2_priv && data->phase2_method &&
+ data->phase2_method->init_for_reauth)
+ data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+ data->phase2_start = 0;
+ data->phase2_success = 0;
+ data->resuming = 1;
+ data->reauth = 1;
+ return priv;
+}
+
+
+static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
+ size_t buflen, int verbose)
+{
+ struct eap_ttls_data *data = priv;
+ int len, ret;
+
+ len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+ ret = os_snprintf(buf + len, buflen - len,
+ "EAP-TTLSv%d Phase2 method=",
+ data->ttls_version);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+ switch (data->phase2_type) {
+ case EAP_TTLS_PHASE2_EAP:
+ ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n",
+ data->phase2_method ?
+ data->phase2_method->name : "?");
+ break;
+ case EAP_TTLS_PHASE2_MSCHAPV2:
+ ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n");
+ break;
+ case EAP_TTLS_PHASE2_MSCHAP:
+ ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n");
+ break;
+ case EAP_TTLS_PHASE2_PAP:
+ ret = os_snprintf(buf + len, buflen - len, "PAP\n");
+ break;
+ case EAP_TTLS_PHASE2_CHAP:
+ ret = os_snprintf(buf + len, buflen - len, "CHAP\n");
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ return len;
+}
+
+
+static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ttls_data *data = priv;
+ u8 *key;
+
+ if (data->key_data == NULL || !data->phase2_success)
+ return NULL;
+
+ key = os_malloc(EAP_TLS_KEY_LEN);
+ if (key == NULL)
+ return NULL;
+
+ *len = EAP_TLS_KEY_LEN;
+ os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+ return key;
+}
+
+
+int eap_peer_ttls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_ttls_init;
+ eap->deinit = eap_ttls_deinit;
+ eap->process = eap_ttls_process;
+ eap->isKeyAvailable = eap_ttls_isKeyAvailable;
+ eap->getKey = eap_ttls_getKey;
+ eap->get_status = eap_ttls_get_status;
+ eap->has_reauth_data = eap_ttls_has_reauth_data;
+ eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;
+ eap->init_for_reauth = eap_ttls_init_for_reauth;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c
new file mode 100644
index 0000000..3e114c1
--- /dev/null
+++ b/src/eap_peer/eap_vendor_test.c
@@ -0,0 +1,195 @@
+/*
+ * EAP peer method: Test method for vendor specific (expanded) EAP type
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file implements a vendor specific test method using EAP expanded types.
+ * This is only for test use and must not be used for authentication since no
+ * security is provided.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#ifdef TEST_PENDING_REQUEST
+#include "eloop.h"
+#endif /* TEST_PENDING_REQUEST */
+
+
+#define EAP_VENDOR_ID 0xfffefd
+#define EAP_VENDOR_TYPE 0xfcfbfaf9
+
+
+/* #define TEST_PENDING_REQUEST */
+
+struct eap_vendor_test_data {
+ enum { INIT, CONFIRM, SUCCESS } state;
+ int first_try;
+};
+
+
+static void * eap_vendor_test_init(struct eap_sm *sm)
+{
+ struct eap_vendor_test_data *data;
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = INIT;
+ data->first_try = 1;
+ return data;
+}
+
+
+static void eap_vendor_test_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_vendor_test_data *data = priv;
+ os_free(data);
+}
+
+
+#ifdef TEST_PENDING_REQUEST
+static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx)
+{
+ struct eap_sm *sm = eloop_ctx;
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Ready to re-process pending "
+ "request");
+ eap_notify_pending(sm);
+}
+#endif /* TEST_PENDING_REQUEST */
+
+
+static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_vendor_test_data *data = priv;
+ struct wpabuf *resp;
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, reqData, &len);
+ if (pos == NULL || len < 1) {
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->state == INIT && *pos != 1) {
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
+ "%d in INIT state", *pos);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->state == CONFIRM && *pos != 3) {
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
+ "%d in CONFIRM state", *pos);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->state == SUCCESS) {
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
+ "in SUCCESS state");
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ if (data->state == CONFIRM) {
+#ifdef TEST_PENDING_REQUEST
+ if (data->first_try) {
+ data->first_try = 0;
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing "
+ "pending request");
+ ret->ignore = TRUE;
+ eloop_register_timeout(1, 0, eap_vendor_ready, sm,
+ NULL);
+ return NULL;
+ }
+#endif /* TEST_PENDING_REQUEST */
+ }
+
+ ret->ignore = FALSE;
+
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Generating Response");
+ ret->allowNotifications = TRUE;
+
+ resp = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL)
+ return NULL;
+
+ if (data->state == INIT) {
+ wpabuf_put_u8(resp, 2);
+ data->state = CONFIRM;
+ ret->methodState = METHOD_CONT;
+ ret->decision = DECISION_FAIL;
+ } else {
+ wpabuf_put_u8(resp, 4);
+ data->state = SUCCESS;
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_vendor_test_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+ struct eap_vendor_test_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_vendor_test_data *data = priv;
+ u8 *key;
+ const int key_len = 64;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(key_len);
+ if (key == NULL)
+ return NULL;
+
+ os_memset(key, 0x11, key_len / 2);
+ os_memset(key + key_len / 2, 0x22, key_len / 2);
+ *len = key_len;
+
+ return key;
+}
+
+
+int eap_peer_vendor_test_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_ID, EAP_VENDOR_TYPE,
+ "VENDOR-TEST");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_vendor_test_init;
+ eap->deinit = eap_vendor_test_deinit;
+ eap->process = eap_vendor_test_process;
+ eap->isKeyAvailable = eap_vendor_test_isKeyAvailable;
+ eap->getKey = eap_vendor_test_getKey;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
new file mode 100644
index 0000000..09d8a1c
--- /dev/null
+++ b/src/eap_peer/eap_wsc.c
@@ -0,0 +1,553 @@
+/*
+ * EAP-WSC peer for Wi-Fi Protected Setup
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "uuid.h"
+#include "eap_i.h"
+#include "eap_common/eap_wsc_common.h"
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+
+
+struct eap_wsc_data {
+ enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
+ int registrar;
+ struct wpabuf *in_buf;
+ struct wpabuf *out_buf;
+ enum wsc_op_code in_op_code, out_op_code;
+ size_t out_used;
+ size_t fragment_size;
+ struct wps_data *wps;
+ struct wps_context *wps_ctx;
+};
+
+
+static const char * eap_wsc_state_txt(int state)
+{
+ switch (state) {
+ case WAIT_START:
+ return "WAIT_START";
+ case MESG:
+ return "MESG";
+ case FRAG_ACK:
+ return "FRAG_ACK";
+ case WAIT_FRAG_ACK:
+ return "WAIT_FRAG_ACK";
+ case DONE:
+ return "DONE";
+ case FAIL:
+ return "FAIL";
+ default:
+ return "?";
+ }
+}
+
+
+static void eap_wsc_state(struct eap_wsc_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
+ eap_wsc_state_txt(data->state),
+ eap_wsc_state_txt(state));
+ data->state = state;
+}
+
+
+static int eap_wsc_new_ap_settings(struct wps_credential *cred,
+ const char *params)
+{
+ const char *pos, *end;
+ size_t len;
+
+ os_memset(cred, 0, sizeof(*cred));
+
+ pos = os_strstr(params, "new_ssid=");
+ if (pos == NULL)
+ return 0;
+ pos += 9;
+ end = os_strchr(pos, ' ');
+ if (end == NULL)
+ len = os_strlen(pos);
+ else
+ len = end - pos;
+ if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
+ hexstr2bin(pos, cred->ssid, len / 2))
+ return -1;
+ cred->ssid_len = len / 2;
+
+ pos = os_strstr(params, "new_auth=");
+ if (pos == NULL)
+ return -1;
+ if (os_strncmp(pos + 9, "OPEN", 4) == 0)
+ cred->auth_type = WPS_AUTH_OPEN;
+ else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
+ cred->auth_type = WPS_AUTH_WPAPSK;
+ else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
+ cred->auth_type = WPS_AUTH_WPA2PSK;
+ else
+ return -1;
+
+ pos = os_strstr(params, "new_encr=");
+ if (pos == NULL)
+ return -1;
+ if (os_strncmp(pos + 9, "NONE", 4) == 0)
+ cred->encr_type = WPS_ENCR_NONE;
+ else if (os_strncmp(pos + 9, "WEP", 3) == 0)
+ cred->encr_type = WPS_ENCR_WEP;
+ else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
+ cred->encr_type = WPS_ENCR_TKIP;
+ else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
+ cred->encr_type = WPS_ENCR_AES;
+ else
+ return -1;
+
+ pos = os_strstr(params, "new_key=");
+ if (pos == NULL)
+ return 0;
+ pos += 8;
+ end = os_strchr(pos, ' ');
+ if (end == NULL)
+ len = os_strlen(pos);
+ else
+ len = end - pos;
+ if ((len & 1) || len > 2 * sizeof(cred->key) ||
+ hexstr2bin(pos, cred->key, len / 2))
+ return -1;
+ cred->key_len = len / 2;
+
+ return 1;
+}
+
+
+static void * eap_wsc_init(struct eap_sm *sm)
+{
+ struct eap_wsc_data *data;
+ const u8 *identity;
+ size_t identity_len;
+ int registrar;
+ struct wps_config cfg;
+ const char *pos;
+ const char *phase1;
+ struct wps_context *wps;
+ struct wps_credential new_ap_settings;
+ int res;
+
+ wps = sm->wps;
+ if (wps == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
+ return NULL;
+ }
+
+ identity = eap_get_config_identity(sm, &identity_len);
+
+ if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
+ os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
+ registrar = 1; /* Supplicant is Registrar */
+ else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
+ os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
+ registrar = 0; /* Supplicant is Enrollee */
+ else {
+ wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
+ identity, identity_len);
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = registrar ? MESG : WAIT_START;
+ data->registrar = registrar;
+ data->wps_ctx = wps;
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ cfg.wps = wps;
+ cfg.registrar = registrar;
+
+ phase1 = eap_get_config_phase1(sm);
+ if (phase1 == NULL) {
+ wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
+ "set");
+ os_free(data);
+ return NULL;
+ }
+
+ pos = os_strstr(phase1, "pin=");
+ if (pos) {
+ pos += 4;
+ cfg.pin = (const u8 *) pos;
+ while (*pos != '\0' && *pos != ' ')
+ pos++;
+ cfg.pin_len = pos - (const char *) cfg.pin;
+ } else {
+ pos = os_strstr(phase1, "pbc=1");
+ if (pos)
+ cfg.pbc = 1;
+ }
+
+ if (cfg.pin == NULL && !cfg.pbc) {
+ wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
+ "configuration data");
+ os_free(data);
+ return NULL;
+ }
+
+ 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);
+ return NULL;
+ }
+ if (res == 1) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
+ "WPS");
+ cfg.new_ap_settings = &new_ap_settings;
+ }
+
+ data->wps = wps_init(&cfg);
+ if (data->wps == NULL) {
+ os_free(data);
+ return NULL;
+ }
+ 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/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c
new file mode 100644
index 0000000..1e169a0
--- /dev/null
+++ b/src/eap_peer/ikev2.c
@@ -0,0 +1,1304 @@
+/*
+ * IKEv2 responder (RFC 4306) for EAP-IKEV2
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/dh_groups.h"
+#include "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: <Proposals> */
+
+ if (sai1 == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: SAi1 not received");
+ return -1;
+ }
+
+ os_memset(&prop, 0, sizeof(prop));
+ prop.proposal_num = 1;
+
+ pos = sai1;
+ end = sai1 + sai1_len;
+
+ while (pos < end) {
+ int plen;
+
+ prop.integ = -1;
+ prop.prf = -1;
+ prop.encr = -1;
+ prop.dh = -1;
+ plen = ikev2_parse_proposal(&prop, pos, end);
+ if (plen < 0)
+ return -1;
+
+ if (!found && prop.integ != -1 && prop.prf != -1 &&
+ prop.encr != -1 && prop.dh != -1) {
+ os_memcpy(&data->proposal, &prop, sizeof(prop));
+ data->dh = dh_groups_get(prop.dh);
+ found = 1;
+ }
+
+ pos += plen;
+ }
+
+ if (pos != end) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposals");
+ return -1;
+ }
+
+ if (!found) {
+ wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d "
+ "INTEG:%d D-H:%d", data->proposal.proposal_num,
+ data->proposal.encr, data->proposal.prf,
+ data->proposal.integ, data->proposal.dh);
+
+ return 0;
+}
+
+
+static int ikev2_process_kei(struct ikev2_responder_data *data,
+ const u8 *kei, size_t kei_len)
+{
+ u16 group;
+
+ /*
+ * Key Exchange Payload:
+ * DH Group # (16 bits)
+ * RESERVED (16 bits)
+ * Key Exchange Data (Diffie-Hellman public value)
+ */
+
+ if (kei == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: KEi not received");
+ return -1;
+ }
+
+ if (kei_len < 4 + 96) {
+ wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload");
+ return -1;
+ }
+
+ group = WPA_GET_BE16(kei);
+ wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u", group);
+
+ if (group != data->proposal.dh) {
+ wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u does not match "
+ "with the selected proposal (%u)",
+ group, data->proposal.dh);
+ /* Reject message with Notify payload of type
+ * INVALID_KE_PAYLOAD (RFC 4306, Sect. 3.4) */
+ data->error_type = INVALID_KE_PAYLOAD;
+ data->state = NOTIFY;
+ return -1;
+ }
+
+ if (data->dh == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group");
+ return -1;
+ }
+
+ /* RFC 4306, Section 3.4:
+ * The length of DH public value MUST be equal to the lenght of the
+ * prime modulus.
+ */
+ if (kei_len - 4 != data->dh->prime_len) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length "
+ "%ld (expected %ld)",
+ (long) (kei_len - 4), (long) data->dh->prime_len);
+ return -1;
+ }
+
+ wpabuf_free(data->i_dh_public);
+ data->i_dh_public = wpabuf_alloc(kei_len - 4);
+ if (data->i_dh_public == NULL)
+ return -1;
+ wpabuf_put_data(data->i_dh_public, kei + 4, kei_len - 4);
+
+ wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEi Diffie-Hellman Public Value",
+ data->i_dh_public);
+
+ return 0;
+}
+
+
+static int ikev2_process_ni(struct ikev2_responder_data *data,
+ const u8 *ni, size_t ni_len)
+{
+ if (ni == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Ni not received");
+ return -1;
+ }
+
+ if (ni_len < IKEV2_NONCE_MIN_LEN || ni_len > IKEV2_NONCE_MAX_LEN) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid Ni length %ld",
+ (long) ni_len);
+ return -1;
+ }
+
+#ifdef CCNS_PL
+ /* Zeros are removed incorrectly from the beginning of the nonces */
+ while (ni_len > 1 && *ni == 0) {
+ ni_len--;
+ ni++;
+ }
+#endif /* CCNS_PL */
+
+ data->i_nonce_len = ni_len;
+ os_memcpy(data->i_nonce, ni, ni_len);
+ wpa_hexdump(MSG_MSGDUMP, "IKEV2: Ni",
+ data->i_nonce, data->i_nonce_len);
+
+ return 0;
+}
+
+
+static int ikev2_process_sa_init(struct ikev2_responder_data *data,
+ const struct ikev2_hdr *hdr,
+ struct ikev2_payloads *pl)
+{
+ if (ikev2_process_sai1(data, pl->sa, pl->sa_len) < 0 ||
+ ikev2_process_kei(data, pl->ke, pl->ke_len) < 0 ||
+ ikev2_process_ni(data, pl->nonce, pl->nonce_len) < 0)
+ return -1;
+
+ os_memcpy(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN);
+
+ return 0;
+}
+
+
+static int ikev2_process_idi(struct ikev2_responder_data *data,
+ const u8 *idi, size_t idi_len)
+{
+ u8 id_type;
+
+ if (idi == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No IDi received");
+ return -1;
+ }
+
+ if (idi_len < 4) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short IDi payload");
+ return -1;
+ }
+
+ id_type = idi[0];
+ idi += 4;
+ idi_len -= 4;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: IDi ID Type %d", id_type);
+ wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDi", idi, idi_len);
+ os_free(data->IDi);
+ data->IDi = os_malloc(idi_len);
+ if (data->IDi == NULL)
+ return -1;
+ os_memcpy(data->IDi, idi, idi_len);
+ data->IDi_len = idi_len;
+ data->IDi_type = id_type;
+
+ return 0;
+}
+
+
+static int ikev2_process_cert(struct ikev2_responder_data *data,
+ const u8 *cert, size_t cert_len)
+{
+ u8 cert_encoding;
+
+ if (cert == NULL) {
+ if (data->peer_auth == PEER_AUTH_CERT) {
+ wpa_printf(MSG_INFO, "IKEV2: No Certificate received");
+ return -1;
+ }
+ return 0;
+ }
+
+ if (cert_len < 1) {
+ wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field");
+ return -1;
+ }
+
+ cert_encoding = cert[0];
+ cert++;
+ cert_len--;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding);
+ wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len);
+
+ /* TODO: validate certificate */
+
+ return 0;
+}
+
+
+static int ikev2_process_auth_cert(struct ikev2_responder_data *data,
+ u8 method, const u8 *auth, size_t auth_len)
+{
+ if (method != AUTH_RSA_SIGN) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+ "method %d", method);
+ return -1;
+ }
+
+ /* TODO: validate AUTH */
+ return 0;
+}
+
+
+static int ikev2_process_auth_secret(struct ikev2_responder_data *data,
+ u8 method, const u8 *auth,
+ size_t auth_len)
+{
+ u8 auth_data[IKEV2_MAX_HASH_LEN];
+ const struct ikev2_prf_alg *prf;
+
+ if (method != AUTH_SHARED_KEY_MIC) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+ "method %d", method);
+ return -1;
+ }
+
+ /* msg | Nr | prf(SK_pi,IDi') */
+ if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg,
+ data->IDi, data->IDi_len, data->IDi_type,
+ &data->keys, 1, data->shared_secret,
+ data->shared_secret_len,
+ data->r_nonce, data->r_nonce_len,
+ data->key_pad, data->key_pad_len,
+ auth_data) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+ return -1;
+ }
+
+ wpabuf_free(data->i_sign_msg);
+ data->i_sign_msg = NULL;
+
+ prf = ikev2_get_prf(data->proposal.prf);
+ if (prf == NULL)
+ return -1;
+
+ if (auth_len != prf->hash_len ||
+ os_memcmp(auth, auth_data, auth_len) != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data");
+ wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data",
+ auth, auth_len);
+ wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data",
+ auth_data, prf->hash_len);
+ data->error_type = AUTHENTICATION_FAILED;
+ data->state = NOTIFY;
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Server authenticated successfully "
+ "using shared keys");
+
+ return 0;
+}
+
+
+static int ikev2_process_auth(struct ikev2_responder_data *data,
+ const u8 *auth, size_t auth_len)
+{
+ u8 auth_method;
+
+ if (auth == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload");
+ return -1;
+ }
+
+ if (auth_len < 4) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short Authentication "
+ "Payload");
+ return -1;
+ }
+
+ auth_method = auth[0];
+ auth += 4;
+ auth_len -= 4;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method);
+ wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len);
+
+ switch (data->peer_auth) {
+ case PEER_AUTH_CERT:
+ return ikev2_process_auth_cert(data, auth_method, auth,
+ auth_len);
+ case PEER_AUTH_SECRET:
+ return ikev2_process_auth_secret(data, auth_method, auth,
+ auth_len);
+ }
+
+ return -1;
+}
+
+
+static int ikev2_process_sa_auth_decrypted(struct ikev2_responder_data *data,
+ u8 next_payload,
+ u8 *payload, size_t payload_len)
+{
+ struct ikev2_payloads pl;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads");
+
+ if (ikev2_parse_payloads(&pl, next_payload, payload, payload +
+ payload_len) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted "
+ "payloads");
+ return -1;
+ }
+
+ if (ikev2_process_idi(data, pl.idi, pl.idi_len) < 0 ||
+ ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 ||
+ ikev2_process_auth(data, pl.auth, pl.auth_len) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int ikev2_process_sa_auth(struct ikev2_responder_data *data,
+ const struct ikev2_hdr *hdr,
+ struct ikev2_payloads *pl)
+{
+ u8 *decrypted;
+ size_t decrypted_len;
+ int ret;
+
+ decrypted = ikev2_decrypt_payload(data->proposal.encr,
+ data->proposal.integ,
+ &data->keys, 1, hdr, pl->encrypted,
+ pl->encrypted_len, &decrypted_len);
+ if (decrypted == NULL)
+ return -1;
+
+ ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload,
+ decrypted, decrypted_len);
+ os_free(decrypted);
+
+ return ret;
+}
+
+
+static int ikev2_validate_rx_state(struct ikev2_responder_data *data,
+ u8 exchange_type, u32 message_id)
+{
+ switch (data->state) {
+ case SA_INIT:
+ /* Expect to receive IKE_SA_INIT: HDR, SAi1, KEi, Ni */
+ if (exchange_type != IKE_SA_INIT) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+ "%u in SA_INIT state", exchange_type);
+ return -1;
+ }
+ if (message_id != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+ "in SA_INIT state", message_id);
+ return -1;
+ }
+ break;
+ case SA_AUTH:
+ /* Expect to receive IKE_SA_AUTH:
+ * HDR, SK {IDi, [CERT,] [CERTREQ,] [IDr,]
+ * AUTH, SAi2, TSi, TSr}
+ */
+ if (exchange_type != IKE_SA_AUTH) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+ "%u in SA_AUTH state", exchange_type);
+ return -1;
+ }
+ if (message_id != 1) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+ "in SA_AUTH state", message_id);
+ return -1;
+ }
+ break;
+ case CHILD_SA:
+ if (exchange_type != CREATE_CHILD_SA) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+ "%u in CHILD_SA state", exchange_type);
+ return -1;
+ }
+ if (message_id != 2) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+ "in CHILD_SA state", message_id);
+ return -1;
+ }
+ break;
+ case NOTIFY:
+ case IKEV2_DONE:
+ case IKEV2_FAILED:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int ikev2_responder_process(struct ikev2_responder_data *data,
+ const struct wpabuf *buf)
+{
+ const struct ikev2_hdr *hdr;
+ u32 length, message_id;
+ const u8 *pos, *end;
+ struct ikev2_payloads pl;
+
+ wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)",
+ (unsigned long) wpabuf_len(buf));
+
+ if (wpabuf_len(buf) < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR");
+ return -1;
+ }
+
+ data->error_type = 0;
+ hdr = (const struct ikev2_hdr *) wpabuf_head(buf);
+ end = wpabuf_head_u8(buf) + wpabuf_len(buf);
+ message_id = WPA_GET_BE32(hdr->message_id);
+ length = WPA_GET_BE32(hdr->length);
+
+ wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI",
+ hdr->i_spi, IKEV2_SPI_LEN);
+ wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Responder's SPI",
+ hdr->r_spi, IKEV2_SPI_LEN);
+ wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Version: 0x%x "
+ "Exchange Type: %u",
+ hdr->next_payload, hdr->version, hdr->exchange_type);
+ wpa_printf(MSG_DEBUG, "IKEV2: Message ID: %u Length: %u",
+ message_id, length);
+
+ if (hdr->version != IKEV2_VERSION) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x "
+ "(expected 0x%x)", hdr->version, IKEV2_VERSION);
+ return -1;
+ }
+
+ if (length != wpabuf_len(buf)) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != "
+ "RX: %lu)", (unsigned long) length,
+ (unsigned long) wpabuf_len(buf));
+ return -1;
+ }
+
+ if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0)
+ return -1;
+
+ if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) !=
+ IKEV2_HDR_INITIATOR) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x",
+ hdr->flags);
+ return -1;
+ }
+
+ if (data->state != SA_INIT) {
+ if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+ "Initiator's SPI");
+ return -1;
+ }
+ if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+ "Responder's SPI");
+ return -1;
+ }
+ }
+
+ pos = (const u8 *) (hdr + 1);
+ if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0)
+ return -1;
+
+ if (data->state == SA_INIT) {
+ data->last_msg = LAST_MSG_SA_INIT;
+ if (ikev2_process_sa_init(data, hdr, &pl) < 0) {
+ if (data->state == NOTIFY)
+ return 0;
+ return -1;
+ }
+ wpabuf_free(data->i_sign_msg);
+ data->i_sign_msg = wpabuf_dup(buf);
+ }
+
+ if (data->state == SA_AUTH) {
+ data->last_msg = LAST_MSG_SA_AUTH;
+ if (ikev2_process_sa_auth(data, hdr, &pl) < 0) {
+ if (data->state == NOTIFY)
+ return 0;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void ikev2_build_hdr(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 exchange_type,
+ u8 next_payload, u32 message_id)
+{
+ struct ikev2_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR");
+
+ /* HDR - RFC 4306, Sect. 3.1 */
+ hdr = wpabuf_put(msg, sizeof(*hdr));
+ os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN);
+ os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN);
+ hdr->next_payload = next_payload;
+ hdr->version = IKEV2_VERSION;
+ hdr->exchange_type = exchange_type;
+ hdr->flags = IKEV2_HDR_RESPONSE;
+ WPA_PUT_BE32(hdr->message_id, message_id);
+}
+
+
+static int ikev2_build_sar1(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+ struct ikev2_proposal *p;
+ struct ikev2_transform *t;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding SAr1 payload");
+
+ /* SAr1 - RFC 4306, Sect. 2.7 and 3.3 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+
+ p = wpabuf_put(msg, sizeof(*p));
+#ifdef CCNS_PL
+ /* Seems to require that the Proposal # is 1 even though RFC 4306
+ * Sect 3.3.1 has following requirement "When a proposal is accepted,
+ * all of the proposal numbers in the SA payload MUST be the same and
+ * MUST match the number on the proposal sent that was accepted.".
+ */
+ p->proposal_num = 1;
+#else /* CCNS_PL */
+ p->proposal_num = data->proposal.proposal_num;
+#endif /* CCNS_PL */
+ p->protocol_id = IKEV2_PROTOCOL_IKE;
+ p->num_transforms = 4;
+
+ t = wpabuf_put(msg, sizeof(*t));
+ t->type = 3;
+ t->transform_type = IKEV2_TRANSFORM_ENCR;
+ WPA_PUT_BE16(t->transform_id, data->proposal.encr);
+ if (data->proposal.encr == ENCR_AES_CBC) {
+ /* Transform Attribute: Key Len = 128 bits */
+#ifdef CCNS_PL
+ wpabuf_put_be16(msg, 0x001d); /* ?? */
+#else /* CCNS_PL */
+ wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */
+#endif /* CCNS_PL */
+ wpabuf_put_be16(msg, 128); /* 128-bit key */
+ }
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t;
+ WPA_PUT_BE16(t->transform_length, plen);
+
+ t = wpabuf_put(msg, sizeof(*t));
+ t->type = 3;
+ WPA_PUT_BE16(t->transform_length, sizeof(*t));
+ t->transform_type = IKEV2_TRANSFORM_PRF;
+ WPA_PUT_BE16(t->transform_id, data->proposal.prf);
+
+ t = wpabuf_put(msg, sizeof(*t));
+ t->type = 3;
+ WPA_PUT_BE16(t->transform_length, sizeof(*t));
+ t->transform_type = IKEV2_TRANSFORM_INTEG;
+ WPA_PUT_BE16(t->transform_id, data->proposal.integ);
+
+ t = wpabuf_put(msg, sizeof(*t));
+ WPA_PUT_BE16(t->transform_length, sizeof(*t));
+ t->transform_type = IKEV2_TRANSFORM_DH;
+ WPA_PUT_BE16(t->transform_id, data->proposal.dh);
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p;
+ WPA_PUT_BE16(p->proposal_length, plen);
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+
+ return 0;
+}
+
+
+static int ikev2_build_ker(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+ struct wpabuf *pv;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding KEr payload");
+
+ pv = dh_init(data->dh, &data->r_dh_private);
+ if (pv == NULL) {
+ wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH");
+ return -1;
+ }
+
+ /* KEr - RFC 4306, Sect. 3.4 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+
+ wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */
+ wpabuf_put(msg, 2); /* RESERVED */
+ /*
+ * RFC 4306, Sect. 3.4: possible zero padding for public value to
+ * match the length of the prime.
+ */
+ wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv));
+ wpabuf_put_buf(msg, pv);
+ wpabuf_free(pv);
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static int ikev2_build_nr(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding Nr payload");
+
+ /* Nr - RFC 4306, Sect. 3.9 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+ wpabuf_put_data(msg, data->r_nonce, data->r_nonce_len);
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static int ikev2_build_idr(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding IDr payload");
+
+ if (data->IDr == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No IDr available");
+ return -1;
+ }
+
+ /* IDr - RFC 4306, Sect. 3.5 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+ wpabuf_put_u8(msg, ID_KEY_ID);
+ wpabuf_put(msg, 3); /* RESERVED */
+ wpabuf_put_data(msg, data->IDr, data->IDr_len);
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static int ikev2_build_auth(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+ const struct ikev2_prf_alg *prf;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload");
+
+ prf = ikev2_get_prf(data->proposal.prf);
+ if (prf == NULL)
+ return -1;
+
+ /* Authentication - RFC 4306, Sect. 3.8 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+ wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC);
+ wpabuf_put(msg, 3); /* RESERVED */
+
+ /* msg | Ni | prf(SK_pr,IDr') */
+ if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg,
+ data->IDr, data->IDr_len, ID_KEY_ID,
+ &data->keys, 0, data->shared_secret,
+ data->shared_secret_len,
+ data->i_nonce, data->i_nonce_len,
+ data->key_pad, data->key_pad_len,
+ wpabuf_put(msg, prf->hash_len)) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+ return -1;
+ }
+ wpabuf_free(data->r_sign_msg);
+ data->r_sign_msg = NULL;
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static int ikev2_build_notification(struct ikev2_responder_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding Notification payload");
+
+ if (data->error_type == 0) {
+ wpa_printf(MSG_INFO, "IKEV2: No Notify Message Type "
+ "available");
+ return -1;
+ }
+
+ /* Notify - RFC 4306, Sect. 3.10 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+#ifdef CCNS_PL
+ wpabuf_put_u8(msg, 1); /* Protocol ID: IKE_SA notification */
+#else /* CCNS_PL */
+ wpabuf_put_u8(msg, 0); /* Protocol ID: no existing SA */
+#endif /* CCNS_PL */
+ wpabuf_put_u8(msg, 0); /* SPI Size */
+ wpabuf_put_be16(msg, data->error_type);
+
+ switch (data->error_type) {
+ case INVALID_KE_PAYLOAD:
+ if (data->proposal.dh == -1) {
+ wpa_printf(MSG_INFO, "IKEV2: No DH Group selected for "
+ "INVALID_KE_PAYLOAD notifications");
+ return -1;
+ }
+ wpabuf_put_be16(msg, data->proposal.dh);
+ wpa_printf(MSG_DEBUG, "IKEV2: INVALID_KE_PAYLOAD - request "
+ "DH Group #%d", data->proposal.dh);
+ break;
+ case AUTHENTICATION_FAILED:
+ /* no associated data */
+ break;
+ default:
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported Notify Message Type "
+ "%d", data->error_type);
+ return -1;
+ }
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data)
+{
+ struct wpabuf *msg;
+
+ /* build IKE_SA_INIT: HDR, SAr1, KEr, Nr, [CERTREQ], [SK{IDr}] */
+
+ if (os_get_random(data->r_spi, IKEV2_SPI_LEN))
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Responder's SPI",
+ data->r_spi, IKEV2_SPI_LEN);
+
+ data->r_nonce_len = IKEV2_NONCE_MIN_LEN;
+ if (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/src/eap_peer/ikev2.h b/src/eap_peer/ikev2.h
new file mode 100644
index 0000000..9ca0ca5
--- /dev/null
+++ b/src/eap_peer/ikev2.h
@@ -0,0 +1,65 @@
+/*
+ * IKEv2 responder (RFC 4306) for EAP-IKEV2
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef IKEV2_H
+#define IKEV2_H
+
+#include "eap_common/ikev2_common.h"
+
+struct ikev2_proposal_data {
+ u8 proposal_num;
+ int integ;
+ int prf;
+ int encr;
+ int dh;
+};
+
+
+struct ikev2_responder_data {
+ enum { SA_INIT, SA_AUTH, CHILD_SA, NOTIFY, IKEV2_DONE, IKEV2_FAILED }
+ state;
+ u8 i_spi[IKEV2_SPI_LEN];
+ u8 r_spi[IKEV2_SPI_LEN];
+ u8 i_nonce[IKEV2_NONCE_MAX_LEN];
+ size_t i_nonce_len;
+ u8 r_nonce[IKEV2_NONCE_MAX_LEN];
+ size_t r_nonce_len;
+ struct wpabuf *i_dh_public;
+ struct wpabuf *r_dh_private;
+ struct ikev2_proposal_data proposal;
+ const struct dh_group *dh;
+ struct ikev2_keys keys;
+ u8 *IDi;
+ size_t IDi_len;
+ u8 IDi_type;
+ u8 *IDr;
+ size_t IDr_len;
+ struct wpabuf *r_sign_msg;
+ struct wpabuf *i_sign_msg;
+ u8 *shared_secret;
+ size_t shared_secret_len;
+ enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth;
+ u8 *key_pad;
+ size_t key_pad_len;
+ u16 error_type;
+ enum { LAST_MSG_SA_INIT, LAST_MSG_SA_AUTH } last_msg;
+};
+
+
+void ikev2_responder_deinit(struct ikev2_responder_data *data);
+int ikev2_responder_process(struct ikev2_responder_data *data,
+ const struct wpabuf *buf);
+struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data);
+
+#endif /* IKEV2_H */
diff --git a/src/eap_peer/mschapv2.c b/src/eap_peer/mschapv2.c
new file mode 100644
index 0000000..b8fb075
--- /dev/null
+++ b/src/eap_peer/mschapv2.c
@@ -0,0 +1,123 @@
+/*
+ * MSCHAPV2 (RFC 2759)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/ms_funcs.h"
+#include "mschapv2.h"
+
+const u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
+{
+ size_t i;
+
+ /*
+ * MSCHAPv2 does not include optional domain name in the
+ * challenge-response calculation, so remove domain prefix
+ * (if present).
+ */
+
+ for (i = 0; i < *len; i++) {
+ if (username[i] == '\\') {
+ *len -= i + 1;
+ return username + i + 1;
+ }
+ }
+
+ return username;
+}
+
+
+int mschapv2_derive_response(const u8 *identity, size_t identity_len,
+ const u8 *password, size_t password_len,
+ int pwhash,
+ const u8 *auth_challenge,
+ const u8 *peer_challenge,
+ u8 *nt_response, u8 *auth_response,
+ u8 *master_key)
+{
+ const u8 *username;
+ size_t username_len;
+ u8 password_hash[16], password_hash_hash[16];
+
+ wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity",
+ identity, identity_len);
+ username_len = identity_len;
+ username = mschapv2_remove_domain(identity, &username_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username",
+ username, username_len);
+
+ wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge",
+ auth_challenge, MSCHAPV2_CHAL_LEN);
+ wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge",
+ peer_challenge, MSCHAPV2_CHAL_LEN);
+ wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username",
+ username, username_len);
+ /* Authenticator response is not really needed yet, but calculate it
+ * here so that challenges need not be saved. */
+ if (pwhash) {
+ wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash",
+ password, password_len);
+ generate_nt_response_pwhash(auth_challenge, peer_challenge,
+ username, username_len,
+ password, nt_response);
+ generate_authenticator_response_pwhash(
+ password, peer_challenge, auth_challenge,
+ username, username_len, nt_response, auth_response);
+ } else {
+ wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password",
+ password, password_len);
+ generate_nt_response(auth_challenge, peer_challenge,
+ username, username_len,
+ password, password_len, nt_response);
+ generate_authenticator_response(password, password_len,
+ peer_challenge, auth_challenge,
+ username, username_len,
+ nt_response, auth_response);
+ }
+ wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response",
+ nt_response, MSCHAPV2_NT_RESPONSE_LEN);
+ wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response",
+ auth_response, MSCHAPV2_AUTH_RESPONSE_LEN);
+
+ /* Generate master_key here since we have the needed data available. */
+ if (pwhash) {
+ if (hash_nt_password_hash(password, password_hash_hash))
+ return -1;
+ } else {
+ if (nt_password_hash(password, password_len, password_hash) ||
+ hash_nt_password_hash(password_hash, password_hash_hash))
+ return -1;
+ }
+ get_master_key(password_hash_hash, nt_response, master_key);
+ wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key",
+ master_key, MSCHAPV2_MASTER_KEY_LEN);
+
+ return 0;
+}
+
+
+int mschapv2_verify_auth_response(const u8 *auth_response,
+ const u8 *buf, size_t buf_len)
+{
+ u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+ if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN ||
+ buf[0] != 'S' || buf[1] != '=' ||
+ hexstr2bin((char *) (buf + 2), recv_response,
+ MSCHAPV2_AUTH_RESPONSE_LEN) ||
+ os_memcmp(auth_response, recv_response,
+ MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
+ return -1;
+ return 0;
+}
diff --git a/src/eap_peer/mschapv2.h b/src/eap_peer/mschapv2.h
new file mode 100644
index 0000000..90dad31
--- /dev/null
+++ b/src/eap_peer/mschapv2.h
@@ -0,0 +1,34 @@
+/*
+ * MSCHAPV2 (RFC 2759)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef MSCHAPV2_H
+#define MSCHAPV2_H
+
+#define MSCHAPV2_CHAL_LEN 16
+#define MSCHAPV2_NT_RESPONSE_LEN 24
+#define MSCHAPV2_AUTH_RESPONSE_LEN 20
+#define MSCHAPV2_MASTER_KEY_LEN 16
+
+const u8 * mschapv2_remove_domain(const u8 *username, size_t *len);
+int mschapv2_derive_response(const u8 *username, size_t username_len,
+ const u8 *password, size_t password_len,
+ int pwhash,
+ const u8 *auth_challenge,
+ const u8 *peer_challenge,
+ u8 *nt_response, u8 *auth_response,
+ u8 *master_key);
+int mschapv2_verify_auth_response(const u8 *auth_response,
+ const u8 *buf, size_t buf_len);
+
+#endif /* MSCHAPV2_H */
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
new file mode 100644
index 0000000..a70d70c
--- /dev/null
+++ b/src/eap_peer/tncc.c
@@ -0,0 +1,1369 @@
+/*
+ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <dlfcn.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include "common.h"
+#include "base64.h"
+#include "tncc.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_defs.h"
+
+
+#ifdef UNICODE
+#define TSTR "%S"
+#else /* UNICODE */
+#define TSTR "%s"
+#endif /* UNICODE */
+
+
+#define TNC_CONFIG_FILE "/etc/tnc_config"
+#define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs")
+#define IF_TNCCS_START \
+"<?xml version=\"1.0\"?>\n" \
+"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
+"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
+"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
+"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
+"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
+#define IF_TNCCS_END "\n</TNCCS-Batch>"
+
+/* TNC IF-IMC */
+
+typedef unsigned long TNC_UInt32;
+typedef unsigned char *TNC_BufferReference;
+
+typedef TNC_UInt32 TNC_IMCID;
+typedef TNC_UInt32 TNC_ConnectionID;
+typedef TNC_UInt32 TNC_ConnectionState;
+typedef TNC_UInt32 TNC_RetryReason;
+typedef TNC_UInt32 TNC_MessageType;
+typedef TNC_MessageType *TNC_MessageTypeList;
+typedef TNC_UInt32 TNC_VendorID;
+typedef TNC_UInt32 TNC_MessageSubtype;
+typedef TNC_UInt32 TNC_Version;
+typedef TNC_UInt32 TNC_Result;
+
+typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)(
+ TNC_IMCID imcID,
+ char *functionName,
+ void **pOutfunctionPointer);
+
+#define TNC_RESULT_SUCCESS 0
+#define TNC_RESULT_NOT_INITIALIZED 1
+#define TNC_RESULT_ALREADY_INITIALIZED 2
+#define TNC_RESULT_NO_COMMON_VERSION 3
+#define TNC_RESULT_CANT_RETRY 4
+#define TNC_RESULT_WONT_RETRY 5
+#define TNC_RESULT_INVALID_PARAMETER 6
+#define TNC_RESULT_CANT_RESPOND 7
+#define TNC_RESULT_ILLEGAL_OPERATION 8
+#define TNC_RESULT_OTHER 9
+#define TNC_RESULT_FATAL 10
+
+#define TNC_CONNECTION_STATE_CREATE 0
+#define TNC_CONNECTION_STATE_HANDSHAKE 1
+#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
+#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
+#define TNC_CONNECTION_STATE_ACCESS_NONE 4
+#define TNC_CONNECTION_STATE_DELETE 5
+
+#define TNC_IFIMC_VERSION_1 1
+
+#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
+#define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff)
+
+/* TNCC-TNCS Message Types */
+#define TNC_TNCCS_RECOMMENDATION 0x00000001
+#define TNC_TNCCS_ERROR 0x00000002
+#define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003
+#define TNC_TNCCS_REASONSTRINGS 0x00000004
+
+
+/* IF-TNCCS-SOH - SSoH and SSoHR Attributes */
+enum {
+ SSOH_MS_MACHINE_INVENTORY = 1,
+ SSOH_MS_QUARANTINE_STATE = 2,
+ SSOH_MS_PACKET_INFO = 3,
+ SSOH_MS_SYSTEMGENERATED_IDS = 4,
+ SSOH_MS_MACHINENAME = 5,
+ SSOH_MS_CORRELATIONID = 6,
+ SSOH_MS_INSTALLED_SHVS = 7,
+ SSOH_MS_MACHINE_INVENTORY_EX = 8
+};
+
+struct tnc_if_imc {
+ struct tnc_if_imc *next;
+ char *name;
+ char *path;
+ void *dlhandle; /* from dlopen() */
+ TNC_IMCID imcID;
+ TNC_ConnectionID connectionID;
+ TNC_MessageTypeList supported_types;
+ size_t num_supported_types;
+ u8 *imc_send;
+ size_t imc_send_len;
+
+ /* Functions implemented by IMCs (with TNC_IMC_ prefix) */
+ TNC_Result (*Initialize)(
+ TNC_IMCID imcID,
+ TNC_Version minVersion,
+ TNC_Version maxVersion,
+ TNC_Version *pOutActualVersion);
+ TNC_Result (*NotifyConnectionChange)(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID,
+ TNC_ConnectionState newState);
+ TNC_Result (*BeginHandshake)(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID);
+ TNC_Result (*ReceiveMessage)(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID,
+ TNC_BufferReference messageBuffer,
+ TNC_UInt32 messageLength,
+ TNC_MessageType messageType);
+ TNC_Result (*BatchEnding)(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID);
+ TNC_Result (*Terminate)(TNC_IMCID imcID);
+ TNC_Result (*ProvideBindFunction)(
+ TNC_IMCID imcID,
+ TNC_TNCC_BindFunctionPointer bindFunction);
+};
+
+struct tncc_data {
+ struct tnc_if_imc *imc;
+ unsigned int last_batchid;
+};
+
+#define TNC_MAX_IMC_ID 10
+static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
+
+
+/* TNCC functions that IMCs can call */
+
+TNC_Result TNC_TNCC_ReportMessageTypes(
+ TNC_IMCID imcID,
+ TNC_MessageTypeList supportedTypes,
+ TNC_UInt32 typeCount)
+{
+ TNC_UInt32 i;
+ struct tnc_if_imc *imc;
+
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu "
+ "typeCount=%lu)",
+ (unsigned long) imcID, (unsigned long) typeCount);
+
+ for (i = 0; i < typeCount; i++) {
+ wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
+ i, supportedTypes[i]);
+ }
+
+ if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ imc = tnc_imc[imcID];
+ os_free(imc->supported_types);
+ imc->supported_types =
+ os_malloc(typeCount * sizeof(TNC_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,
+ "<IMC-IMV-Message><Type>%08X</Type>"
+ "<Base64>%s</Base64></IMC-IMV-Message>",
+ (unsigned int) messageType, b64);
+
+ os_free(b64);
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCC_RequestHandshakeRetry(
+ TNC_IMCID imcID,
+ TNC_ConnectionID connectionID,
+ TNC_RetryReason reason)
+{
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry");
+
+ if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ /*
+ * TODO: trigger a call to eapol_sm_request_reauth(). This would
+ * require that the IMC continues to be loaded in memory afer
+ * authentication..
+ */
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity,
+ const char *message)
+{
+ wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu "
+ "severity==%lu message='%s')",
+ imcID, severity, message);
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID,
+ const char *message)
+{
+ wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu "
+ "connectionID==%lu message='%s')",
+ imcID, connectionID, message);
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCC_BindFunction(
+ TNC_IMCID imcID,
+ char *functionName,
+ void **pOutfunctionPointer)
+{
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, "
+ "functionName='%s')", (unsigned long) imcID, functionName);
+
+ if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ if (pOutfunctionPointer == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0)
+ *pOutfunctionPointer = TNC_TNCC_ReportMessageTypes;
+ else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0)
+ *pOutfunctionPointer = TNC_TNCC_SendMessage;
+ else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") ==
+ 0)
+ *pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry;
+ else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0)
+ *pOutfunctionPointer = TNC_9048_LogMessage;
+ else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0)
+ *pOutfunctionPointer = TNC_9048_UserMessage;
+ else
+ *pOutfunctionPointer = NULL;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+static void * tncc_get_sym(void *handle, char *func)
+{
+ void *fptr;
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#ifdef _WIN32_WCE
+ fptr = GetProcAddressA(handle, func);
+#else /* _WIN32_WCE */
+ fptr = GetProcAddress(handle, func);
+#endif /* _WIN32_WCE */
+#else /* CONFIG_NATIVE_WINDOWS */
+ fptr = dlsym(handle, func);
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ return fptr;
+}
+
+
+static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc)
+{
+ void *handle = imc->dlhandle;
+
+ /* Mandatory IMC functions */
+ imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize");
+ if (imc->Initialize == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: IMC does not export "
+ "TNC_IMC_Initialize");
+ return -1;
+ }
+
+ imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake");
+ if (imc->BeginHandshake == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: IMC does not export "
+ "TNC_IMC_BeginHandshake");
+ return -1;
+ }
+
+ imc->ProvideBindFunction =
+ tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction");
+ if (imc->ProvideBindFunction == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: IMC does not export "
+ "TNC_IMC_ProvideBindFunction");
+ return -1;
+ }
+
+ /* Optional IMC functions */
+ imc->NotifyConnectionChange =
+ tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange");
+ imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage");
+ imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding");
+ imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate");
+
+ return 0;
+}
+
+
+static int tncc_imc_initialize(struct tnc_if_imc *imc)
+{
+ TNC_Result res;
+ TNC_Version imc_ver;
+
+ wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'",
+ imc->name);
+ res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1,
+ TNC_IFIMC_VERSION_1, &imc_ver);
+ wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu",
+ (unsigned long) res, (unsigned long) imc_ver);
+
+ return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_imc_terminate(struct tnc_if_imc *imc)
+{
+ TNC_Result res;
+
+ if (imc->Terminate == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'",
+ imc->name);
+ res = imc->Terminate(imc->imcID);
+ wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu",
+ (unsigned long) res);
+
+ return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc)
+{
+ TNC_Result res;
+
+ wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for "
+ "IMC '%s'", imc->name);
+ res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction);
+ wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu",
+ (unsigned long) res);
+
+ return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc,
+ TNC_ConnectionState state)
+{
+ TNC_Result res;
+
+ if (imc->NotifyConnectionChange == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)"
+ " for IMC '%s'", (int) state, imc->name);
+ res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID,
+ state);
+ wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
+ (unsigned long) res);
+
+ return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_imc_begin_handshake(struct tnc_if_imc *imc)
+{
+ TNC_Result res;
+
+ wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC "
+ "'%s'", imc->name);
+ res = imc->BeginHandshake(imc->imcID, imc->connectionID);
+ wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu",
+ (unsigned long) res);
+
+ return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_load_imc(struct tnc_if_imc *imc)
+{
+ if (imc->path == NULL) {
+ wpa_printf(MSG_DEBUG, "TNC: No IMC configured");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)",
+ imc->name, imc->path);
+#ifdef CONFIG_NATIVE_WINDOWS
+#ifdef UNICODE
+ {
+ TCHAR *lib = wpa_strdup_tchar(imc->path);
+ if (lib == NULL)
+ return -1;
+ imc->dlhandle = LoadLibrary(lib);
+ os_free(lib);
+ }
+#else /* UNICODE */
+ imc->dlhandle = LoadLibrary(imc->path);
+#endif /* UNICODE */
+ if (imc->dlhandle == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d",
+ imc->name, imc->path, (int) GetLastError());
+ return -1;
+ }
+#else /* CONFIG_NATIVE_WINDOWS */
+ imc->dlhandle = dlopen(imc->path, RTLD_LAZY);
+ if (imc->dlhandle == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s",
+ imc->name, imc->path, dlerror());
+ return -1;
+ }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ if (tncc_imc_resolve_funcs(imc) < 0) {
+ wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions");
+ return -1;
+ }
+
+ if (tncc_imc_initialize(imc) < 0 ||
+ tncc_imc_provide_bind_function(imc) < 0) {
+ wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void tncc_unload_imc(struct tnc_if_imc *imc)
+{
+ tncc_imc_terminate(imc);
+ tnc_imc[imc->imcID] = NULL;
+
+ if (imc->dlhandle) {
+#ifdef CONFIG_NATIVE_WINDOWS
+ FreeLibrary(imc->dlhandle);
+#else /* CONFIG_NATIVE_WINDOWS */
+ dlclose(imc->dlhandle);
+#endif /* CONFIG_NATIVE_WINDOWS */
+ }
+ os_free(imc->name);
+ os_free(imc->path);
+ os_free(imc->supported_types);
+ os_free(imc->imc_send);
+}
+
+
+static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type)
+{
+ size_t i;
+ unsigned int vendor, subtype;
+
+ if (imc == NULL || imc->supported_types == NULL)
+ return 0;
+
+ vendor = type >> 8;
+ subtype = type & 0xff;
+
+ for (i = 0; i < imc->num_supported_types; i++) {
+ unsigned int svendor, ssubtype;
+ svendor = imc->supported_types[i] >> 8;
+ ssubtype = imc->supported_types[i] & 0xff;
+ if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
+ (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type,
+ const u8 *msg, size_t len)
+{
+ struct tnc_if_imc *imc;
+ TNC_Result res;
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len);
+
+ for (imc = tncc->imc; imc; imc = imc->next) {
+ if (imc->ReceiveMessage == NULL ||
+ !tncc_supported_type(imc, type))
+ continue;
+
+ wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'",
+ imc->name);
+ res = imc->ReceiveMessage(imc->imcID, imc->connectionID,
+ (TNC_BufferReference) msg, len,
+ type);
+ wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
+ (unsigned long) res);
+ }
+}
+
+
+void tncc_init_connection(struct tncc_data *tncc)
+{
+ struct tnc_if_imc *imc;
+
+ for (imc = tncc->imc; imc; imc = imc->next) {
+ tncc_imc_notify_connection_change(
+ imc, TNC_CONNECTION_STATE_CREATE);
+ tncc_imc_notify_connection_change(
+ imc, TNC_CONNECTION_STATE_HANDSHAKE);
+
+ os_free(imc->imc_send);
+ imc->imc_send = NULL;
+ imc->imc_send_len = 0;
+
+ tncc_imc_begin_handshake(imc);
+ }
+}
+
+
+size_t tncc_total_send_len(struct tncc_data *tncc)
+{
+ struct tnc_if_imc *imc;
+
+ size_t len = 0;
+ for (imc = tncc->imc; imc; imc = imc->next)
+ len += imc->imc_send_len;
+ return len;
+}
+
+
+u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos)
+{
+ struct tnc_if_imc *imc;
+
+ for (imc = tncc->imc; imc; imc = imc->next) {
+ if (imc->imc_send == NULL)
+ continue;
+
+ os_memcpy(pos, imc->imc_send, imc->imc_send_len);
+ pos += imc->imc_send_len;
+ os_free(imc->imc_send);
+ imc->imc_send = NULL;
+ imc->imc_send_len = 0;
+ }
+
+ return pos;
+}
+
+
+char * tncc_if_tnccs_start(struct tncc_data *tncc)
+{
+ char *buf = os_malloc(1000);
+ if (buf == NULL)
+ return NULL;
+ tncc->last_batchid++;
+ os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid);
+ return buf;
+}
+
+
+char * tncc_if_tnccs_end(void)
+{
+ char *buf = os_malloc(100);
+ if (buf == NULL)
+ return NULL;
+ os_snprintf(buf, 100, IF_TNCCS_END);
+ return buf;
+}
+
+
+static void tncc_notify_recommendation(struct tncc_data *tncc,
+ enum tncc_process_res res)
+{
+ TNC_ConnectionState state;
+ struct tnc_if_imc *imc;
+
+ switch (res) {
+ case TNCCS_RECOMMENDATION_ALLOW:
+ state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
+ break;
+ case TNCCS_RECOMMENDATION_NONE:
+ state = TNC_CONNECTION_STATE_ACCESS_NONE;
+ break;
+ case TNCCS_RECOMMENDATION_ISOLATE:
+ state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
+ break;
+ default:
+ state = TNC_CONNECTION_STATE_ACCESS_NONE;
+ break;
+ }
+
+ for (imc = tncc->imc; imc; imc = imc->next)
+ tncc_imc_notify_connection_change(imc, state);
+}
+
+
+static int tncc_get_type(char *start, unsigned int *type)
+{
+ char *pos = os_strstr(start, "<Type>");
+ if (pos == NULL)
+ return -1;
+ pos += 6;
+ *type = strtoul(pos, NULL, 16);
+ return 0;
+}
+
+
+static unsigned char * tncc_get_base64(char *start, size_t *decoded_len)
+{
+ char *pos, *pos2;
+ unsigned char *decoded;
+
+ pos = os_strstr(start, "<Base64>");
+ if (pos == NULL)
+ return NULL;
+
+ pos += 8;
+ pos2 = os_strstr(pos, "</Base64>");
+ if (pos2 == NULL)
+ return NULL;
+ *pos2 = '\0';
+
+ decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
+ decoded_len);
+ *pos2 = '<';
+ if (decoded == NULL) {
+ wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
+ }
+
+ return decoded;
+}
+
+
+static enum tncc_process_res tncc_get_recommendation(char *start)
+{
+ char *pos, *pos2, saved;
+ int recom;
+
+ pos = os_strstr(start, "<TNCCS-Recommendation ");
+ if (pos == NULL)
+ return TNCCS_RECOMMENDATION_ERROR;
+
+ pos += 21;
+ pos = os_strstr(pos, " type=");
+ if (pos == NULL)
+ return TNCCS_RECOMMENDATION_ERROR;
+ pos += 6;
+
+ if (*pos == '"')
+ pos++;
+
+ pos2 = pos;
+ while (*pos2 != '\0' && *pos2 != '"' && *pos2 != '>')
+ pos2++;
+
+ if (*pos2 == '\0')
+ return TNCCS_RECOMMENDATION_ERROR;
+
+ saved = *pos2;
+ *pos2 = '\0';
+ wpa_printf(MSG_DEBUG, "TNC: TNCCS-Recommendation: '%s'", pos);
+
+ recom = TNCCS_RECOMMENDATION_ERROR;
+ if (os_strcmp(pos, "allow") == 0)
+ recom = TNCCS_RECOMMENDATION_ALLOW;
+ else if (os_strcmp(pos, "none") == 0)
+ recom = TNCCS_RECOMMENDATION_NONE;
+ else if (os_strcmp(pos, "isolate") == 0)
+ recom = TNCCS_RECOMMENDATION_ISOLATE;
+
+ *pos2 = saved;
+
+ return recom;
+}
+
+
+enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
+ const u8 *msg, size_t len)
+{
+ char *buf, *start, *end, *pos, *pos2, *payload;
+ unsigned int batch_id;
+ unsigned char *decoded;
+ size_t decoded_len;
+ enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION;
+ int recommendation_msg = 0;
+
+ buf = os_malloc(len + 1);
+ if (buf == NULL)
+ return TNCCS_PROCESS_ERROR;
+
+ os_memcpy(buf, msg, len);
+ buf[len] = '\0';
+ start = os_strstr(buf, "<TNCCS-Batch ");
+ end = os_strstr(buf, "</TNCCS-Batch>");
+ if (start == NULL || end == NULL || start > end) {
+ os_free(buf);
+ return TNCCS_PROCESS_ERROR;
+ }
+
+ start += 13;
+ while (*start == ' ')
+ start++;
+ *end = '\0';
+
+ pos = os_strstr(start, "BatchId=");
+ if (pos == NULL) {
+ os_free(buf);
+ return TNCCS_PROCESS_ERROR;
+ }
+
+ pos += 8;
+ if (*pos == '"')
+ pos++;
+ batch_id = atoi(pos);
+ wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
+ batch_id);
+ if (batch_id != tncc->last_batchid + 1) {
+ wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
+ "%u (expected %u)",
+ batch_id, tncc->last_batchid + 1);
+ os_free(buf);
+ return TNCCS_PROCESS_ERROR;
+ }
+ tncc->last_batchid = batch_id;
+
+ while (*pos != '\0' && *pos != '>')
+ pos++;
+ if (*pos == '\0') {
+ os_free(buf);
+ return TNCCS_PROCESS_ERROR;
+ }
+ pos++;
+ payload = start;
+
+ /*
+ * <IMC-IMV-Message>
+ * <Type>01234567</Type>
+ * <Base64>foo==</Base64>
+ * </IMC-IMV-Message>
+ */
+
+ while (*start) {
+ char *endpos;
+ unsigned int type;
+
+ pos = os_strstr(start, "<IMC-IMV-Message>");
+ if (pos == NULL)
+ break;
+ start = pos + 17;
+ end = os_strstr(start, "</IMC-IMV-Message>");
+ if (end == NULL)
+ break;
+ *end = '\0';
+ endpos = end;
+ end += 18;
+
+ if (tncc_get_type(start, &type) < 0) {
+ *endpos = '<';
+ start = end;
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
+
+ decoded = tncc_get_base64(start, &decoded_len);
+ if (decoded == NULL) {
+ *endpos = '<';
+ start = end;
+ continue;
+ }
+
+ tncc_send_to_imcs(tncc, type, decoded, decoded_len);
+
+ os_free(decoded);
+
+ start = end;
+ }
+
+ /*
+ * <TNCC-TNCS-Message>
+ * <Type>01234567</Type>
+ * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
+ * <Base64>foo==</Base64>
+ * </TNCC-TNCS-Message>
+ */
+
+ start = payload;
+ while (*start) {
+ unsigned int type;
+ char *xml, *xmlend, *endpos;
+
+ pos = os_strstr(start, "<TNCC-TNCS-Message>");
+ if (pos == NULL)
+ break;
+ start = pos + 19;
+ end = os_strstr(start, "</TNCC-TNCS-Message>");
+ if (end == NULL)
+ break;
+ *end = '\0';
+ endpos = end;
+ end += 20;
+
+ if (tncc_get_type(start, &type) < 0) {
+ *endpos = '<';
+ start = end;
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
+ type);
+
+ /* Base64 OR XML */
+ decoded = NULL;
+ xml = NULL;
+ xmlend = NULL;
+ pos = os_strstr(start, "<XML>");
+ if (pos) {
+ pos += 5;
+ pos2 = os_strstr(pos, "</XML>");
+ if (pos2 == NULL) {
+ *endpos = '<';
+ start = end;
+ continue;
+ }
+ xmlend = pos2;
+ xml = pos;
+ } else {
+ decoded = tncc_get_base64(start, &decoded_len);
+ if (decoded == NULL) {
+ *endpos = '<';
+ start = end;
+ continue;
+ }
+ }
+
+ if (decoded) {
+ wpa_hexdump_ascii(MSG_MSGDUMP,
+ "TNC: TNCC-TNCS-Message Base64",
+ decoded, decoded_len);
+ os_free(decoded);
+ }
+
+ if (xml) {
+ wpa_hexdump_ascii(MSG_MSGDUMP,
+ "TNC: TNCC-TNCS-Message XML",
+ (unsigned char *) xml,
+ xmlend - xml);
+ }
+
+ if (type == TNC_TNCCS_RECOMMENDATION && xml) {
+ /*
+ * <TNCCS-Recommendation type="allow">
+ * </TNCCS-Recommendation>
+ */
+ *xmlend = '\0';
+ res = tncc_get_recommendation(xml);
+ *xmlend = '<';
+ recommendation_msg = 1;
+ }
+
+ start = end;
+ }
+
+ os_free(buf);
+
+ if (recommendation_msg)
+ tncc_notify_recommendation(tncc, res);
+
+ return res;
+}
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive)
+{
+ HKEY hk, hk2;
+ LONG ret;
+ DWORD i;
+ struct tnc_if_imc *imc, *last;
+ int j;
+
+ last = tncc->imc;
+ while (last && last->next)
+ last = last->next;
+
+ ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS,
+ &hk);
+ if (ret != ERROR_SUCCESS)
+ return 0;
+
+ for (i = 0; ; i++) {
+ TCHAR name[255], *val;
+ DWORD namelen, buflen;
+
+ namelen = 255;
+ ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL,
+ NULL);
+
+ if (ret == ERROR_NO_MORE_ITEMS)
+ break;
+
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x",
+ (unsigned int) ret);
+ break;
+ }
+
+ if (namelen >= 255)
+ namelen = 255 - 1;
+ name[namelen] = '\0';
+
+ wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name);
+
+ ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR
+ "'", name);
+ continue;
+ }
+
+ ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL,
+ &buflen);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "TNC: Could not read Path from "
+ "IMC key '" TSTR "'", name);
+ RegCloseKey(hk2);
+ continue;
+ }
+
+ val = os_malloc(buflen);
+ if (val == NULL) {
+ RegCloseKey(hk2);
+ continue;
+ }
+
+ ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL,
+ (LPBYTE) val, &buflen);
+ if (ret != ERROR_SUCCESS) {
+ os_free(val);
+ RegCloseKey(hk2);
+ continue;
+ }
+
+ RegCloseKey(hk2);
+
+ wpa_unicode2ascii_inplace(val);
+ wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val);
+
+ for (j = 0; j < TNC_MAX_IMC_ID; j++) {
+ if (tnc_imc[j] == NULL)
+ break;
+ }
+ if (j >= TNC_MAX_IMC_ID) {
+ wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
+ os_free(val);
+ continue;
+ }
+
+ imc = os_zalloc(sizeof(*imc));
+ if (imc == NULL) {
+ os_free(val);
+ break;
+ }
+
+ imc->imcID = j;
+
+ wpa_unicode2ascii_inplace(name);
+ imc->name = os_strdup((char *) name);
+ imc->path = os_strdup((char *) val);
+
+ os_free(val);
+
+ if (last == NULL)
+ tncc->imc = imc;
+ else
+ last->next = imc;
+ last = imc;
+
+ tnc_imc[imc->imcID] = imc;
+ }
+
+ RegCloseKey(hk);
+
+ return 0;
+}
+
+
+static int tncc_read_config(struct tncc_data *tncc)
+{
+ if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 ||
+ tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0)
+ return -1;
+ return 0;
+}
+
+#else /* CONFIG_NATIVE_WINDOWS */
+
+static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error)
+{
+ struct tnc_if_imc *imc;
+ char *pos, *pos2;
+ int i;
+
+ for (i = 0; i < TNC_MAX_IMC_ID; i++) {
+ if (tnc_imc[i] == NULL)
+ break;
+ }
+ if (i >= TNC_MAX_IMC_ID) {
+ wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
+ return NULL;
+ }
+
+ imc = os_zalloc(sizeof(*imc));
+ if (imc == NULL) {
+ *error = 1;
+ return NULL;
+ }
+
+ imc->imcID = i;
+
+ pos = start;
+ wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos);
+ if (pos + 1 >= end || *pos != '"') {
+ wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
+ "(no starting quotation mark)", start);
+ os_free(imc);
+ return NULL;
+ }
+
+ pos++;
+ pos2 = pos;
+ while (pos2 < end && *pos2 != '"')
+ pos2++;
+ if (pos2 >= end) {
+ wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
+ "(no ending quotation mark)", start);
+ os_free(imc);
+ return NULL;
+ }
+ *pos2 = '\0';
+ wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
+ imc->name = os_strdup(pos);
+
+ pos = pos2 + 1;
+ if (pos >= end || *pos != ' ') {
+ wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
+ "(no space after name)", start);
+ os_free(imc->name);
+ os_free(imc);
+ return NULL;
+ }
+
+ pos++;
+ wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos);
+ imc->path = os_strdup(pos);
+ tnc_imc[imc->imcID] = imc;
+
+ return imc;
+}
+
+
+static int tncc_read_config(struct tncc_data *tncc)
+{
+ char *config, *end, *pos, *line_end;
+ size_t config_len;
+ struct tnc_if_imc *imc, *last;
+
+ last = NULL;
+
+ config = os_readfile(TNC_CONFIG_FILE, &config_len);
+ if (config == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
+ "file '%s'", TNC_CONFIG_FILE);
+ return -1;
+ }
+
+ end = config + config_len;
+ for (pos = config; pos < end; pos = line_end + 1) {
+ line_end = pos;
+ while (*line_end != '\n' && *line_end != '\r' &&
+ line_end < end)
+ line_end++;
+ *line_end = '\0';
+
+ if (os_strncmp(pos, "IMC ", 4) == 0) {
+ int error = 0;
+
+ imc = tncc_parse_imc(pos + 4, line_end, &error);
+ if (error)
+ return -1;
+ if (imc) {
+ if (last == NULL)
+ tncc->imc = imc;
+ else
+ last->next = imc;
+ last = imc;
+ }
+ }
+ }
+
+ os_free(config);
+
+ return 0;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+struct tncc_data * tncc_init(void)
+{
+ struct tncc_data *tncc;
+ struct tnc_if_imc *imc;
+
+ tncc = os_zalloc(sizeof(*tncc));
+ if (tncc == NULL)
+ return NULL;
+
+ /* TODO:
+ * move loading and Initialize() to a location that is not
+ * re-initialized for every EAP-TNC session (?)
+ */
+
+ if (tncc_read_config(tncc) < 0) {
+ wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
+ goto failed;
+ }
+
+ for (imc = tncc->imc; imc; imc = imc->next) {
+ if (tncc_load_imc(imc)) {
+ wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'",
+ imc->name);
+ goto failed;
+ }
+ }
+
+ return tncc;
+
+failed:
+ tncc_deinit(tncc);
+ return NULL;
+}
+
+
+void tncc_deinit(struct tncc_data *tncc)
+{
+ struct tnc_if_imc *imc, *prev;
+
+ imc = tncc->imc;
+ while (imc) {
+ tncc_unload_imc(imc);
+
+ prev = imc;
+ imc = imc->next;
+ os_free(prev);
+ }
+
+ os_free(tncc);
+}
+
+
+static struct wpabuf * tncc_build_soh(int ver)
+{
+ struct wpabuf *buf;
+ u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end;
+ u8 correlation_id[24];
+ /* TODO: get correct name */
+ char *machinename = "wpa_supplicant@w1.fi";
+
+ if (os_get_random(correlation_id, sizeof(correlation_id)))
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID",
+ correlation_id, sizeof(correlation_id));
+
+ buf = wpabuf_alloc(200);
+ if (buf == NULL)
+ return NULL;
+
+ /* Vendor-Specific TLV (Microsoft) - SoH */
+ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
+ tlv_len = wpabuf_put(buf, 2); /* Length */
+ wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
+ wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */
+ tlv_len2 = wpabuf_put(buf, 2); /* Length */
+
+ /* SoH Header */
+ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */
+ outer_len = wpabuf_put(buf, 2);
+ wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
+ wpabuf_put_be16(buf, ver); /* Inner Type */
+ inner_len = wpabuf_put(buf, 2);
+
+ if (ver == 2) {
+ /* SoH Mode Sub-Header */
+ /* Outer Type */
+ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
+ wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */
+ wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
+ /* Value: */
+ wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
+ wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */
+ wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */
+ }
+
+ /* SSoH TLV */
+ /* System-Health-Id */
+ wpabuf_put_be16(buf, 0x0002); /* Type */
+ wpabuf_put_be16(buf, 4); /* Length */
+ wpabuf_put_be32(buf, 79616);
+ /* Vendor-Specific Attribute */
+ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
+ ssoh_len = wpabuf_put(buf, 2);
+ wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
+
+ /* MS-Packet-Info */
+ wpabuf_put_u8(buf, SSOH_MS_PACKET_INFO);
+ /* Note: IF-TNCCS-SOH v1.0 r8 claims this field to be:
+ * Reserved(4 bits) r(1 bit) Vers(3 bits), but Windows XP
+ * SP3 seems to be sending 0x11 for SSoH, i.e., r(request/response) bit
+ * would not be in the specified location.
+ * [MS-SOH] 4.0.2: Reserved(3 bits) r(1 bit) Vers(4 bits)
+ */
+ wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */
+
+ /* MS-Machine-Inventory */
+ /* TODO: get correct values; 0 = not applicable for OS */
+ wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY);
+ wpabuf_put_be32(buf, 0); /* osVersionMajor */
+ wpabuf_put_be32(buf, 0); /* osVersionMinor */
+ wpabuf_put_be32(buf, 0); /* osVersionBuild */
+ wpabuf_put_be16(buf, 0); /* spVersionMajor */
+ wpabuf_put_be16(buf, 0); /* spVersionMinor */
+ wpabuf_put_be16(buf, 0); /* procArch */
+
+ /* MS-MachineName */
+ wpabuf_put_u8(buf, SSOH_MS_MACHINENAME);
+ wpabuf_put_be16(buf, os_strlen(machinename) + 1);
+ wpabuf_put_data(buf, machinename, os_strlen(machinename) + 1);
+
+ /* MS-CorrelationId */
+ wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID);
+ wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
+
+ /* MS-Quarantine-State */
+ wpabuf_put_u8(buf, SSOH_MS_QUARANTINE_STATE);
+ wpabuf_put_be16(buf, 1); /* Flags: ExtState=0, f=0, qState=1 */
+ wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (hi) */
+ wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (lo) */
+ wpabuf_put_be16(buf, 1); /* urlLenInBytes */
+ wpabuf_put_u8(buf, 0); /* null termination for the url */
+
+ /* MS-Machine-Inventory-Ex */
+ wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY_EX);
+ wpabuf_put_be32(buf, 0); /* Reserved
+ * (note: Windows XP SP3 uses 0xdecafbad) */
+ wpabuf_put_u8(buf, 1); /* ProductType: Client */
+
+ /* Update SSoH Length */
+ end = wpabuf_put(buf, 0);
+ WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2);
+
+ /* TODO: SoHReportEntry TLV (zero or more) */
+
+ /* Update length fields */
+ end = wpabuf_put(buf, 0);
+ WPA_PUT_BE16(tlv_len, end - tlv_len - 2);
+ WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2);
+ WPA_PUT_BE16(outer_len, end - outer_len - 2);
+ WPA_PUT_BE16(inner_len, end - inner_len - 2);
+
+ return buf;
+}
+
+
+struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len)
+{
+ const u8 *pos;
+
+ wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len);
+
+ if (len < 12)
+ return NULL;
+
+ /* SoH Request */
+ pos = data;
+
+ /* TLV Type */
+ if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV)
+ return NULL;
+ pos += 2;
+
+ /* Length */
+ if (WPA_GET_BE16(pos) < 8)
+ return NULL;
+ pos += 2;
+
+ /* Vendor_Id */
+ if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT)
+ return NULL;
+ pos += 4;
+
+ /* TLV Type */
+ if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received");
+
+ return tncc_build_soh(2);
+}
diff --git a/src/eap_peer/tncc.h b/src/eap_peer/tncc.h
new file mode 100644
index 0000000..4d42a05
--- /dev/null
+++ b/src/eap_peer/tncc.h
@@ -0,0 +1,42 @@
+/*
+ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef TNCC_H
+#define TNCC_H
+
+struct tncc_data;
+
+struct tncc_data * tncc_init(void);
+void tncc_deinit(struct tncc_data *tncc);
+void tncc_init_connection(struct tncc_data *tncc);
+size_t tncc_total_send_len(struct tncc_data *tncc);
+u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos);
+char * tncc_if_tnccs_start(struct tncc_data *tncc);
+char * tncc_if_tnccs_end(void);
+
+enum tncc_process_res {
+ TNCCS_PROCESS_ERROR = -1,
+ TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0,
+ TNCCS_RECOMMENDATION_ERROR,
+ TNCCS_RECOMMENDATION_ALLOW,
+ TNCCS_RECOMMENDATION_NONE,
+ TNCCS_RECOMMENDATION_ISOLATE
+};
+
+enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
+ const u8 *msg, size_t len);
+
+struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len);
+
+#endif /* TNCC_H */
diff --git a/src/eap_server/Makefile b/src/eap_server/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/eap_server/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
new file mode 100644
index 0000000..6b29075
--- /dev/null
+++ b/src/eap_server/eap.h
@@ -0,0 +1,128 @@
+/*
+ * hostapd / EAP Full Authenticator state machine (RFC 4137)
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_H
+#define EAP_H
+
+#include "common/defs.h"
+#include "eap_common/eap_defs.h"
+#include "eap_server/eap_methods.h"
+#include "wpabuf.h"
+
+struct eap_sm;
+
+#define EAP_MAX_METHODS 8
+
+#define EAP_TTLS_AUTH_PAP 1
+#define EAP_TTLS_AUTH_CHAP 2
+#define EAP_TTLS_AUTH_MSCHAP 4
+#define EAP_TTLS_AUTH_MSCHAPV2 8
+
+struct eap_user {
+ struct {
+ int vendor;
+ u32 method;
+ } methods[EAP_MAX_METHODS];
+ u8 *password;
+ size_t password_len;
+ int password_hash; /* whether password is hashed with
+ * nt_password_hash() */
+ int phase2;
+ int force_version;
+ int ttls_auth; /* bitfield of
+ * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
+};
+
+struct eap_eapol_interface {
+ /* Lower layer to full authenticator variables */
+ Boolean eapResp; /* shared with EAPOL Backend Authentication */
+ struct wpabuf *eapRespData;
+ Boolean portEnabled;
+ int retransWhile;
+ Boolean eapRestart; /* shared with EAPOL Authenticator PAE */
+ int eapSRTT;
+ int eapRTTVAR;
+
+ /* Full authenticator to lower layer variables */
+ Boolean eapReq; /* shared with EAPOL Backend Authentication */
+ Boolean eapNoReq; /* shared with EAPOL Backend Authentication */
+ Boolean eapSuccess;
+ Boolean eapFail;
+ Boolean eapTimeout;
+ struct wpabuf *eapReqData;
+ u8 *eapKeyData;
+ size_t eapKeyDataLen;
+ Boolean eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */
+
+ /* AAA interface to full authenticator variables */
+ Boolean aaaEapReq;
+ Boolean aaaEapNoReq;
+ Boolean aaaSuccess;
+ Boolean aaaFail;
+ struct wpabuf *aaaEapReqData;
+ u8 *aaaEapKeyData;
+ size_t aaaEapKeyDataLen;
+ Boolean aaaEapKeyAvailable;
+ int aaaMethodTimeout;
+
+ /* Full authenticator to AAA interface variables */
+ Boolean aaaEapResp;
+ struct wpabuf *aaaEapRespData;
+ /* aaaIdentity -> eap_get_identity() */
+ Boolean aaaTimeout;
+};
+
+struct eapol_callbacks {
+ int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
+ int phase2, struct eap_user *user);
+ const char * (*get_eap_req_id_text)(void *ctx, size_t *len);
+};
+
+struct eap_config {
+ void *ssl_ctx;
+ void *msg_ctx;
+ void *eap_sim_db_priv;
+ Boolean backend_auth;
+ int eap_server;
+ 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;
+};
+
+
+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/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
new file mode 100644
index 0000000..daac746
--- /dev/null
+++ b/src/eap_server/eap_i.h
@@ -0,0 +1,201 @@
+/*
+ * hostapd / EAP Authenticator state machine internal structures (RFC 4137)
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_I_H
+#define EAP_I_H
+
+#include "wpabuf.h"
+#include "eap_server/eap.h"
+#include "eap_common/eap_common.h"
+
+/* RFC 4137 - EAP Standalone Authenticator */
+
+/**
+ * struct eap_method - EAP method interface
+ * This structure defines the EAP method interface. Each method will need to
+ * register its own EAP type, EAP name, and set of function pointers for method
+ * specific operations. This interface is based on section 5.4 of RFC 4137.
+ */
+struct eap_method {
+ int vendor;
+ EapType method;
+ const char *name;
+
+ void * (*init)(struct eap_sm *sm);
+ void * (*initPickUp)(struct eap_sm *sm);
+ void (*reset)(struct eap_sm *sm, void *priv);
+
+ struct wpabuf * (*buildReq)(struct eap_sm *sm, void *priv, u8 id);
+ int (*getTimeout)(struct eap_sm *sm, void *priv);
+ Boolean (*check)(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData);
+ void (*process)(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData);
+ Boolean (*isDone)(struct eap_sm *sm, void *priv);
+ u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+ /* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt,
+ * but it is useful in implementing Policy.getDecision() */
+ Boolean (*isSuccess)(struct eap_sm *sm, void *priv);
+
+ /**
+ * free - Free EAP method data
+ * @method: Pointer to the method data registered with
+ * eap_server_method_register().
+ *
+ * This function will be called when the EAP method is being
+ * unregistered. If the EAP method allocated resources during
+ * registration (e.g., allocated struct eap_method), they should be
+ * freed in this function. No other method functions will be called
+ * after this call. If this function is not defined (i.e., function
+ * pointer is %NULL), a default handler is used to release the method
+ * data with free(method). This is suitable for most cases.
+ */
+ void (*free)(struct eap_method *method);
+
+#define EAP_SERVER_METHOD_INTERFACE_VERSION 1
+ /**
+ * version - Version of the EAP server method interface
+ *
+ * The EAP server method implementation should set this variable to
+ * EAP_SERVER_METHOD_INTERFACE_VERSION. This is used to verify that the
+ * EAP method is using supported API version when using dynamically
+ * loadable EAP methods.
+ */
+ int version;
+
+ /**
+ * next - Pointer to the next EAP method
+ *
+ * This variable is used internally in the EAP method registration code
+ * to create a linked list of registered EAP methods.
+ */
+ struct eap_method *next;
+
+ /**
+ * get_emsk - Get EAP method specific keying extended material (EMSK)
+ * @sm: Pointer to EAP state machine allocated with eap_sm_init()
+ * @priv: Pointer to private EAP method data from eap_method::init()
+ * @len: Pointer to a variable to store EMSK length
+ * Returns: EMSK or %NULL if not available
+ *
+ * This function can be used to get the extended keying material from
+ * the EAP method. The key may already be stored in the method-specific
+ * private data or this function may derive the key.
+ */
+ u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
+};
+
+/**
+ * struct eap_sm - EAP server state machine data
+ */
+struct eap_sm {
+ enum {
+ EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED,
+ EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST,
+ EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST,
+ EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE,
+ EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD,
+ EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2,
+ EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2,
+ EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE,
+ EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2
+ } EAP_state;
+
+ /* Constants */
+ int MaxRetrans;
+
+ struct eap_eapol_interface eap_if;
+
+ /* Full authenticator state machine local variables */
+
+ /* Long-term (maintained betwen packets) */
+ EapType currentMethod;
+ int currentId;
+ enum {
+ METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END
+ } methodState;
+ int retransCount;
+ struct wpabuf *lastReqData;
+ int methodTimeout;
+
+ /* Short-term (not maintained between packets) */
+ Boolean rxResp;
+ int respId;
+ EapType respMethod;
+ int respVendor;
+ u32 respVendorMethod;
+ Boolean ignore;
+ enum {
+ DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE,
+ DECISION_PASSTHROUGH
+ } decision;
+
+ /* Miscellaneous variables */
+ const struct eap_method *m; /* selected EAP method */
+ /* not defined in RFC 4137 */
+ Boolean changed;
+ void *eapol_ctx, *msg_ctx;
+ struct eapol_callbacks *eapol_cb;
+ void *eap_method_priv;
+ u8 *identity;
+ size_t identity_len;
+ /* Whether Phase 2 method should validate identity match */
+ int require_identity_match;
+ int lastId; /* Identifier used in the last EAP-Packet */
+ struct eap_user *user;
+ int user_eap_method_index;
+ int init_phase2;
+ void *ssl_ctx;
+ void *eap_sim_db_priv;
+ Boolean backend_auth;
+ Boolean update_user;
+ int eap_server;
+
+ int num_rounds;
+ enum {
+ METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT
+ } method_pending;
+
+ u8 *auth_challenge;
+ u8 *peer_challenge;
+
+ u8 *pac_opaque_encr_key;
+ u8 *eap_fast_a_id;
+ size_t eap_fast_a_id_len;
+ char *eap_fast_a_id_info;
+ enum {
+ NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV
+ } eap_fast_prov;
+ int pac_key_lifetime;
+ int pac_key_refresh_time;
+ int eap_sim_aka_result_ind;
+ int tnc;
+ 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 eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
+ int phase2);
+void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len);
+
+#endif /* EAP_I_H */
diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h
new file mode 100644
index 0000000..4a5296e
--- /dev/null
+++ b/src/eap_server/eap_methods.h
@@ -0,0 +1,54 @@
+/*
+ * EAP server method registration
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_SERVER_METHODS_H
+#define EAP_SERVER_METHODS_H
+
+#include "eap_common/eap_defs.h"
+
+const struct eap_method * eap_server_get_eap_method(int vendor,
+ EapType method);
+struct eap_method * eap_server_method_alloc(int version, int vendor,
+ EapType method, const char *name);
+void eap_server_method_free(struct eap_method *method);
+int eap_server_method_register(struct eap_method *method);
+
+EapType eap_server_get_type(const char *name, int *vendor);
+void eap_server_unregister_methods(void);
+const char * eap_server_get_name(int vendor, EapType type);
+
+/* EAP server method registration calls for statically linked in methods */
+int eap_server_identity_register(void);
+int eap_server_md5_register(void);
+int eap_server_tls_register(void);
+int eap_server_mschapv2_register(void);
+int eap_server_peap_register(void);
+int eap_server_tlv_register(void);
+int eap_server_gtc_register(void);
+int eap_server_ttls_register(void);
+int eap_server_sim_register(void);
+int eap_server_aka_register(void);
+int eap_server_aka_prime_register(void);
+int eap_server_pax_register(void);
+int eap_server_psk_register(void);
+int eap_server_sake_register(void);
+int eap_server_gpsk_register(void);
+int eap_server_vendor_test_register(void);
+int eap_server_fast_register(void);
+int eap_server_wsc_register(void);
+int eap_server_ikev2_register(void);
+int eap_server_tnc_register(void);
+int eap_server_pwd_register(void);
+
+#endif /* EAP_SERVER_METHODS_H */
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
new file mode 100644
index 0000000..41416b1
--- /dev/null
+++ b/src/eap_server/eap_server.c
@@ -0,0 +1,1384 @@
+/*
+ * hostapd / EAP Full Authenticator state machine (RFC 4137)
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This state machine is based on the full authenticator state machine defined
+ * in RFC 4137. However, to support backend authentication in RADIUS
+ * authentication server functionality, parts of backend authenticator (also
+ * from RFC 4137) are mixed in. This functionality is enabled by setting
+ * backend_auth configuration variable to TRUE.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "state_machine.h"
+#include "common/wpa_ctrl.h"
+
+#define STATE_MACHINE_DATA struct eap_sm
+#define STATE_MACHINE_DEBUG_PREFIX "EAP"
+
+#define EAP_MAX_AUTH_ROUNDS 50
+
+static void eap_user_free(struct eap_user *user);
+
+
+/* EAP state machines are described in RFC 4137 */
+
+static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
+ int eapSRTT, int eapRTTVAR,
+ int methodTimeout);
+static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp);
+static int eap_sm_getId(const struct wpabuf *data);
+static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id);
+static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id);
+static int eap_sm_nextId(struct eap_sm *sm, int id);
+static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
+ size_t len);
+static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor);
+static int eap_sm_Policy_getDecision(struct eap_sm *sm);
+static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
+
+
+static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src)
+{
+ if (src == NULL)
+ return -1;
+
+ wpabuf_free(*dst);
+ *dst = wpabuf_dup(src);
+ return *dst ? 0 : -1;
+}
+
+
+static int eap_copy_data(u8 **dst, size_t *dst_len,
+ const u8 *src, size_t src_len)
+{
+ if (src == NULL)
+ return -1;
+
+ os_free(*dst);
+ *dst = os_malloc(src_len);
+ if (*dst) {
+ os_memcpy(*dst, src, src_len);
+ *dst_len = src_len;
+ return 0;
+ } else {
+ *dst_len = 0;
+ return -1;
+ }
+}
+
+#define EAP_COPY(dst, src) \
+ eap_copy_data((dst), (dst ## Len), (src), (src ## Len))
+
+
+/**
+ * eap_user_get - Fetch user information from the database
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * @identity: Identity (User-Name) of the user
+ * @identity_len: Length of identity in bytes
+ * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user
+ * Returns: 0 on success, or -1 on failure
+ *
+ * This function is used to fetch user information for EAP. The user will be
+ * selected based on the specified identity. sm->user and
+ * sm->user_eap_method_index are updated for the new user when a matching user
+ * is found. sm->user can be used to get user information (e.g., password).
+ */
+int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
+ int phase2)
+{
+ struct eap_user *user;
+
+ if (sm == NULL || sm->eapol_cb == NULL ||
+ sm->eapol_cb->get_eap_user == NULL)
+ return -1;
+
+ eap_user_free(sm->user);
+ sm->user = NULL;
+
+ user = os_zalloc(sizeof(*user));
+ if (user == NULL)
+ return -1;
+
+ if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity,
+ identity_len, phase2, user) != 0) {
+ eap_user_free(user);
+ return -1;
+ }
+
+ sm->user = user;
+ sm->user_eap_method_index = 0;
+
+ return 0;
+}
+
+
+SM_STATE(EAP, DISABLED)
+{
+ SM_ENTRY(EAP, DISABLED);
+ sm->num_rounds = 0;
+}
+
+
+SM_STATE(EAP, INITIALIZE)
+{
+ SM_ENTRY(EAP, INITIALIZE);
+
+ sm->currentId = -1;
+ sm->eap_if.eapSuccess = FALSE;
+ sm->eap_if.eapFail = FALSE;
+ sm->eap_if.eapTimeout = FALSE;
+ os_free(sm->eap_if.eapKeyData);
+ sm->eap_if.eapKeyData = NULL;
+ sm->eap_if.eapKeyDataLen = 0;
+ sm->eap_if.eapKeyAvailable = FALSE;
+ sm->eap_if.eapRestart = FALSE;
+
+ /*
+ * This is not defined in RFC 4137, but method state needs to be
+ * reseted here so that it does not remain in success state when
+ * re-authentication starts.
+ */
+ if (sm->m && sm->eap_method_priv) {
+ sm->m->reset(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ }
+ sm->m = NULL;
+ sm->user_eap_method_index = 0;
+
+ if (sm->backend_auth) {
+ sm->currentMethod = EAP_TYPE_NONE;
+ /* parse rxResp, respId, respMethod */
+ eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
+ if (sm->rxResp) {
+ sm->currentId = sm->respId;
+ }
+ }
+ sm->num_rounds = 0;
+ sm->method_pending = METHOD_PENDING_NONE;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
+ MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, PICK_UP_METHOD)
+{
+ SM_ENTRY(EAP, PICK_UP_METHOD);
+
+ if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) {
+ sm->currentMethod = sm->respMethod;
+ if (sm->m && sm->eap_method_priv) {
+ sm->m->reset(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ }
+ sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF,
+ sm->currentMethod);
+ if (sm->m && sm->m->initPickUp) {
+ sm->eap_method_priv = sm->m->initPickUp(sm);
+ if (sm->eap_method_priv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP: Failed to "
+ "initialize EAP method %d",
+ sm->currentMethod);
+ sm->m = NULL;
+ sm->currentMethod = EAP_TYPE_NONE;
+ }
+ } else {
+ sm->m = NULL;
+ sm->currentMethod = EAP_TYPE_NONE;
+ }
+ }
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+ "method=%u", sm->currentMethod);
+}
+
+
+SM_STATE(EAP, IDLE)
+{
+ SM_ENTRY(EAP, IDLE);
+
+ sm->eap_if.retransWhile = eap_sm_calculateTimeout(
+ sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
+ sm->methodTimeout);
+}
+
+
+SM_STATE(EAP, RETRANSMIT)
+{
+ SM_ENTRY(EAP, RETRANSMIT);
+
+ sm->retransCount++;
+ if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
+ if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
+ sm->eap_if.eapReq = TRUE;
+ }
+}
+
+
+SM_STATE(EAP, RECEIVED)
+{
+ SM_ENTRY(EAP, RECEIVED);
+
+ /* parse rxResp, respId, respMethod */
+ eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
+ sm->num_rounds++;
+}
+
+
+SM_STATE(EAP, DISCARD)
+{
+ SM_ENTRY(EAP, DISCARD);
+ sm->eap_if.eapResp = FALSE;
+ sm->eap_if.eapNoReq = TRUE;
+}
+
+
+SM_STATE(EAP, SEND_REQUEST)
+{
+ SM_ENTRY(EAP, SEND_REQUEST);
+
+ sm->retransCount = 0;
+ if (sm->eap_if.eapReqData) {
+ if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
+ {
+ sm->eap_if.eapResp = FALSE;
+ sm->eap_if.eapReq = TRUE;
+ } else {
+ sm->eap_if.eapResp = FALSE;
+ sm->eap_if.eapReq = FALSE;
+ }
+ } else {
+ wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData");
+ sm->eap_if.eapResp = FALSE;
+ sm->eap_if.eapReq = FALSE;
+ sm->eap_if.eapNoReq = TRUE;
+ }
+}
+
+
+SM_STATE(EAP, INTEGRITY_CHECK)
+{
+ SM_ENTRY(EAP, INTEGRITY_CHECK);
+
+ if (sm->m->check) {
+ sm->ignore = sm->m->check(sm, sm->eap_method_priv,
+ sm->eap_if.eapRespData);
+ }
+}
+
+
+SM_STATE(EAP, METHOD_REQUEST)
+{
+ SM_ENTRY(EAP, METHOD_REQUEST);
+
+ if (sm->m == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP: method not initialized");
+ return;
+ }
+
+ sm->currentId = eap_sm_nextId(sm, sm->currentId);
+ wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d",
+ sm->currentId);
+ sm->lastId = sm->currentId;
+ wpabuf_free(sm->eap_if.eapReqData);
+ sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv,
+ sm->currentId);
+ if (sm->m->getTimeout)
+ sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv);
+ else
+ sm->methodTimeout = 0;
+}
+
+
+SM_STATE(EAP, METHOD_RESPONSE)
+{
+ SM_ENTRY(EAP, METHOD_RESPONSE);
+
+ sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData);
+ if (sm->m->isDone(sm, sm->eap_method_priv)) {
+ eap_sm_Policy_update(sm, NULL, 0);
+ os_free(sm->eap_if.eapKeyData);
+ if (sm->m->getKey) {
+ sm->eap_if.eapKeyData = sm->m->getKey(
+ sm, sm->eap_method_priv,
+ &sm->eap_if.eapKeyDataLen);
+ } else {
+ sm->eap_if.eapKeyData = NULL;
+ sm->eap_if.eapKeyDataLen = 0;
+ }
+ sm->methodState = METHOD_END;
+ } else {
+ sm->methodState = METHOD_CONTINUE;
+ }
+}
+
+
+SM_STATE(EAP, PROPOSE_METHOD)
+{
+ int vendor;
+ EapType type;
+
+ SM_ENTRY(EAP, PROPOSE_METHOD);
+
+ type = eap_sm_Policy_getNextMethod(sm, &vendor);
+ if (vendor == EAP_VENDOR_IETF)
+ sm->currentMethod = type;
+ else
+ sm->currentMethod = EAP_TYPE_EXPANDED;
+ if (sm->m && sm->eap_method_priv) {
+ sm->m->reset(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ }
+ sm->m = eap_server_get_eap_method(vendor, type);
+ if (sm->m) {
+ sm->eap_method_priv = sm->m->init(sm);
+ if (sm->eap_method_priv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP "
+ "method %d", sm->currentMethod);
+ sm->m = NULL;
+ sm->currentMethod = EAP_TYPE_NONE;
+ }
+ }
+ if (sm->currentMethod == EAP_TYPE_IDENTITY ||
+ sm->currentMethod == EAP_TYPE_NOTIFICATION)
+ sm->methodState = METHOD_CONTINUE;
+ else
+ sm->methodState = METHOD_PROPOSED;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+ "vendor=%u method=%u", vendor, sm->currentMethod);
+}
+
+
+SM_STATE(EAP, NAK)
+{
+ const struct eap_hdr *nak;
+ size_t len = 0;
+ const u8 *pos;
+ const u8 *nak_list = NULL;
+
+ SM_ENTRY(EAP, NAK);
+
+ if (sm->eap_method_priv) {
+ sm->m->reset(sm, sm->eap_method_priv);
+ sm->eap_method_priv = NULL;
+ }
+ sm->m = NULL;
+
+ nak = wpabuf_head(sm->eap_if.eapRespData);
+ if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) {
+ len = be_to_host16(nak->length);
+ if (len > wpabuf_len(sm->eap_if.eapRespData))
+ len = wpabuf_len(sm->eap_if.eapRespData);
+ pos = (const u8 *) (nak + 1);
+ len -= sizeof(*nak);
+ if (*pos == EAP_TYPE_NAK) {
+ pos++;
+ len--;
+ nak_list = pos;
+ }
+ }
+ eap_sm_Policy_update(sm, nak_list, len);
+}
+
+
+SM_STATE(EAP, SELECT_ACTION)
+{
+ SM_ENTRY(EAP, SELECT_ACTION);
+
+ sm->decision = eap_sm_Policy_getDecision(sm);
+}
+
+
+SM_STATE(EAP, TIMEOUT_FAILURE)
+{
+ SM_ENTRY(EAP, TIMEOUT_FAILURE);
+
+ sm->eap_if.eapTimeout = TRUE;
+}
+
+
+SM_STATE(EAP, FAILURE)
+{
+ SM_ENTRY(EAP, FAILURE);
+
+ wpabuf_free(sm->eap_if.eapReqData);
+ sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId);
+ wpabuf_free(sm->lastReqData);
+ sm->lastReqData = NULL;
+ sm->eap_if.eapFail = TRUE;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+ MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, SUCCESS)
+{
+ SM_ENTRY(EAP, SUCCESS);
+
+ wpabuf_free(sm->eap_if.eapReqData);
+ sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId);
+ wpabuf_free(sm->lastReqData);
+ sm->lastReqData = NULL;
+ if (sm->eap_if.eapKeyData)
+ sm->eap_if.eapKeyAvailable = TRUE;
+ sm->eap_if.eapSuccess = TRUE;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+ MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, INITIALIZE_PASSTHROUGH)
+{
+ SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH);
+
+ wpabuf_free(sm->eap_if.aaaEapRespData);
+ sm->eap_if.aaaEapRespData = NULL;
+}
+
+
+SM_STATE(EAP, IDLE2)
+{
+ SM_ENTRY(EAP, IDLE2);
+
+ sm->eap_if.retransWhile = eap_sm_calculateTimeout(
+ sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
+ sm->methodTimeout);
+}
+
+
+SM_STATE(EAP, RETRANSMIT2)
+{
+ SM_ENTRY(EAP, RETRANSMIT2);
+
+ sm->retransCount++;
+ if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
+ if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
+ sm->eap_if.eapReq = TRUE;
+ }
+}
+
+
+SM_STATE(EAP, RECEIVED2)
+{
+ SM_ENTRY(EAP, RECEIVED2);
+
+ /* parse rxResp, respId, respMethod */
+ eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
+}
+
+
+SM_STATE(EAP, DISCARD2)
+{
+ SM_ENTRY(EAP, DISCARD2);
+ sm->eap_if.eapResp = FALSE;
+ sm->eap_if.eapNoReq = TRUE;
+}
+
+
+SM_STATE(EAP, SEND_REQUEST2)
+{
+ SM_ENTRY(EAP, SEND_REQUEST2);
+
+ sm->retransCount = 0;
+ if (sm->eap_if.eapReqData) {
+ if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
+ {
+ sm->eap_if.eapResp = FALSE;
+ sm->eap_if.eapReq = TRUE;
+ } else {
+ sm->eap_if.eapResp = FALSE;
+ sm->eap_if.eapReq = FALSE;
+ }
+ } else {
+ wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData");
+ sm->eap_if.eapResp = FALSE;
+ sm->eap_if.eapReq = FALSE;
+ sm->eap_if.eapNoReq = TRUE;
+ }
+}
+
+
+SM_STATE(EAP, AAA_REQUEST)
+{
+ SM_ENTRY(EAP, AAA_REQUEST);
+
+ if (sm->eap_if.eapRespData == NULL) {
+ wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData");
+ return;
+ }
+
+ /*
+ * if (respMethod == IDENTITY)
+ * aaaIdentity = eapRespData
+ * This is already taken care of by the EAP-Identity method which
+ * stores the identity into sm->identity.
+ */
+
+ eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData);
+}
+
+
+SM_STATE(EAP, AAA_RESPONSE)
+{
+ SM_ENTRY(EAP, AAA_RESPONSE);
+
+ eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
+ sm->currentId = eap_sm_getId(sm->eap_if.eapReqData);
+ sm->methodTimeout = sm->eap_if.aaaMethodTimeout;
+}
+
+
+SM_STATE(EAP, AAA_IDLE)
+{
+ SM_ENTRY(EAP, AAA_IDLE);
+
+ sm->eap_if.aaaFail = FALSE;
+ sm->eap_if.aaaSuccess = FALSE;
+ sm->eap_if.aaaEapReq = FALSE;
+ sm->eap_if.aaaEapNoReq = FALSE;
+ sm->eap_if.aaaEapResp = TRUE;
+}
+
+
+SM_STATE(EAP, TIMEOUT_FAILURE2)
+{
+ SM_ENTRY(EAP, TIMEOUT_FAILURE2);
+
+ sm->eap_if.eapTimeout = TRUE;
+}
+
+
+SM_STATE(EAP, FAILURE2)
+{
+ SM_ENTRY(EAP, FAILURE2);
+
+ eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
+ sm->eap_if.eapFail = TRUE;
+}
+
+
+SM_STATE(EAP, SUCCESS2)
+{
+ SM_ENTRY(EAP, SUCCESS2);
+
+ eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
+
+ sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable;
+ if (sm->eap_if.aaaEapKeyAvailable) {
+ EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData);
+ } else {
+ os_free(sm->eap_if.eapKeyData);
+ sm->eap_if.eapKeyData = NULL;
+ sm->eap_if.eapKeyDataLen = 0;
+ }
+
+ sm->eap_if.eapSuccess = TRUE;
+
+ /*
+ * Start reauthentication with identity request even though we know the
+ * previously used identity. This is needed to get reauthentication
+ * started properly.
+ */
+ sm->start_reauth = TRUE;
+}
+
+
+SM_STEP(EAP)
+{
+ if (sm->eap_if.eapRestart && sm->eap_if.portEnabled)
+ SM_ENTER_GLOBAL(EAP, INITIALIZE);
+ else if (!sm->eap_if.portEnabled)
+ SM_ENTER_GLOBAL(EAP, DISABLED);
+ else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
+ if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
+ wpa_printf(MSG_DEBUG, "EAP: more than %d "
+ "authentication rounds - abort",
+ EAP_MAX_AUTH_ROUNDS);
+ sm->num_rounds++;
+ SM_ENTER_GLOBAL(EAP, FAILURE);
+ }
+ } else switch (sm->EAP_state) {
+ case EAP_INITIALIZE:
+ if (sm->backend_auth) {
+ if (!sm->rxResp)
+ SM_ENTER(EAP, SELECT_ACTION);
+ else if (sm->rxResp &&
+ (sm->respMethod == EAP_TYPE_NAK ||
+ (sm->respMethod == EAP_TYPE_EXPANDED &&
+ sm->respVendor == EAP_VENDOR_IETF &&
+ sm->respVendorMethod == EAP_TYPE_NAK)))
+ SM_ENTER(EAP, NAK);
+ else
+ SM_ENTER(EAP, PICK_UP_METHOD);
+ } else {
+ SM_ENTER(EAP, SELECT_ACTION);
+ }
+ break;
+ case EAP_PICK_UP_METHOD:
+ if (sm->currentMethod == EAP_TYPE_NONE) {
+ SM_ENTER(EAP, SELECT_ACTION);
+ } else {
+ SM_ENTER(EAP, METHOD_RESPONSE);
+ }
+ break;
+ case EAP_DISABLED:
+ if (sm->eap_if.portEnabled)
+ SM_ENTER(EAP, INITIALIZE);
+ break;
+ case EAP_IDLE:
+ if (sm->eap_if.retransWhile == 0)
+ SM_ENTER(EAP, RETRANSMIT);
+ else if (sm->eap_if.eapResp)
+ SM_ENTER(EAP, RECEIVED);
+ break;
+ case EAP_RETRANSMIT:
+ if (sm->retransCount > sm->MaxRetrans)
+ SM_ENTER(EAP, TIMEOUT_FAILURE);
+ else
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_RECEIVED:
+ if (sm->rxResp && (sm->respId == sm->currentId) &&
+ (sm->respMethod == EAP_TYPE_NAK ||
+ (sm->respMethod == EAP_TYPE_EXPANDED &&
+ sm->respVendor == EAP_VENDOR_IETF &&
+ sm->respVendorMethod == EAP_TYPE_NAK))
+ && (sm->methodState == METHOD_PROPOSED))
+ SM_ENTER(EAP, NAK);
+ else if (sm->rxResp && (sm->respId == sm->currentId) &&
+ ((sm->respMethod == sm->currentMethod) ||
+ (sm->respMethod == EAP_TYPE_EXPANDED &&
+ sm->respVendor == EAP_VENDOR_IETF &&
+ sm->respVendorMethod == sm->currentMethod)))
+ SM_ENTER(EAP, INTEGRITY_CHECK);
+ else {
+ wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: "
+ "rxResp=%d respId=%d currentId=%d "
+ "respMethod=%d currentMethod=%d",
+ sm->rxResp, sm->respId, sm->currentId,
+ sm->respMethod, sm->currentMethod);
+ SM_ENTER(EAP, DISCARD);
+ }
+ break;
+ case EAP_DISCARD:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_SEND_REQUEST:
+ SM_ENTER(EAP, IDLE);
+ break;
+ case EAP_INTEGRITY_CHECK:
+ if (sm->ignore)
+ SM_ENTER(EAP, DISCARD);
+ else
+ SM_ENTER(EAP, METHOD_RESPONSE);
+ break;
+ case EAP_METHOD_REQUEST:
+ SM_ENTER(EAP, SEND_REQUEST);
+ break;
+ case EAP_METHOD_RESPONSE:
+ /*
+ * Note: Mechanism to allow EAP methods to wait while going
+ * through pending processing is an extension to RFC 4137
+ * which only defines the transits to SELECT_ACTION and
+ * METHOD_REQUEST from this METHOD_RESPONSE state.
+ */
+ if (sm->methodState == METHOD_END)
+ SM_ENTER(EAP, SELECT_ACTION);
+ else if (sm->method_pending == METHOD_PENDING_WAIT) {
+ wpa_printf(MSG_DEBUG, "EAP: Method has pending "
+ "processing - wait before proceeding to "
+ "METHOD_REQUEST state");
+ } else if (sm->method_pending == METHOD_PENDING_CONT) {
+ wpa_printf(MSG_DEBUG, "EAP: Method has completed "
+ "pending processing - reprocess pending "
+ "EAP message");
+ sm->method_pending = METHOD_PENDING_NONE;
+ SM_ENTER(EAP, METHOD_RESPONSE);
+ } else
+ SM_ENTER(EAP, METHOD_REQUEST);
+ break;
+ case EAP_PROPOSE_METHOD:
+ /*
+ * Note: Mechanism to allow EAP methods to wait while going
+ * through pending processing is an extension to RFC 4137
+ * which only defines the transit to METHOD_REQUEST from this
+ * PROPOSE_METHOD state.
+ */
+ if (sm->method_pending == METHOD_PENDING_WAIT) {
+ wpa_printf(MSG_DEBUG, "EAP: Method has pending "
+ "processing - wait before proceeding to "
+ "METHOD_REQUEST state");
+ if (sm->user_eap_method_index > 0)
+ sm->user_eap_method_index--;
+ } else if (sm->method_pending == METHOD_PENDING_CONT) {
+ wpa_printf(MSG_DEBUG, "EAP: Method has completed "
+ "pending processing - reprocess pending "
+ "EAP message");
+ sm->method_pending = METHOD_PENDING_NONE;
+ SM_ENTER(EAP, PROPOSE_METHOD);
+ } else
+ SM_ENTER(EAP, METHOD_REQUEST);
+ break;
+ case EAP_NAK:
+ SM_ENTER(EAP, SELECT_ACTION);
+ break;
+ case EAP_SELECT_ACTION:
+ if (sm->decision == DECISION_FAILURE)
+ SM_ENTER(EAP, FAILURE);
+ else if (sm->decision == DECISION_SUCCESS)
+ SM_ENTER(EAP, SUCCESS);
+ else if (sm->decision == DECISION_PASSTHROUGH)
+ SM_ENTER(EAP, INITIALIZE_PASSTHROUGH);
+ else
+ SM_ENTER(EAP, PROPOSE_METHOD);
+ break;
+ case EAP_TIMEOUT_FAILURE:
+ break;
+ case EAP_FAILURE:
+ break;
+ case EAP_SUCCESS:
+ break;
+
+ case EAP_INITIALIZE_PASSTHROUGH:
+ if (sm->currentId == -1)
+ SM_ENTER(EAP, AAA_IDLE);
+ else
+ SM_ENTER(EAP, AAA_REQUEST);
+ break;
+ case EAP_IDLE2:
+ if (sm->eap_if.eapResp)
+ SM_ENTER(EAP, RECEIVED2);
+ else if (sm->eap_if.retransWhile == 0)
+ SM_ENTER(EAP, RETRANSMIT2);
+ break;
+ case EAP_RETRANSMIT2:
+ if (sm->retransCount > sm->MaxRetrans)
+ SM_ENTER(EAP, TIMEOUT_FAILURE2);
+ else
+ SM_ENTER(EAP, IDLE2);
+ break;
+ case EAP_RECEIVED2:
+ if (sm->rxResp && (sm->respId == sm->currentId))
+ SM_ENTER(EAP, AAA_REQUEST);
+ else
+ SM_ENTER(EAP, DISCARD2);
+ break;
+ case EAP_DISCARD2:
+ SM_ENTER(EAP, IDLE2);
+ break;
+ case EAP_SEND_REQUEST2:
+ SM_ENTER(EAP, IDLE2);
+ break;
+ case EAP_AAA_REQUEST:
+ SM_ENTER(EAP, AAA_IDLE);
+ break;
+ case EAP_AAA_RESPONSE:
+ SM_ENTER(EAP, SEND_REQUEST2);
+ break;
+ case EAP_AAA_IDLE:
+ if (sm->eap_if.aaaFail)
+ SM_ENTER(EAP, FAILURE2);
+ else if (sm->eap_if.aaaSuccess)
+ SM_ENTER(EAP, SUCCESS2);
+ else if (sm->eap_if.aaaEapReq)
+ SM_ENTER(EAP, AAA_RESPONSE);
+ else if (sm->eap_if.aaaTimeout)
+ SM_ENTER(EAP, TIMEOUT_FAILURE2);
+ break;
+ case EAP_TIMEOUT_FAILURE2:
+ break;
+ case EAP_FAILURE2:
+ break;
+ case EAP_SUCCESS2:
+ break;
+ }
+}
+
+
+static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
+ int eapSRTT, int eapRTTVAR,
+ int methodTimeout)
+{
+ int rto, i;
+
+ if (methodTimeout) {
+ /*
+ * EAP method (either internal or through AAA server, provided
+ * timeout hint. Use that as-is as a timeout for retransmitting
+ * the EAP request if no response is received.
+ */
+ wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
+ "(from EAP method hint)", methodTimeout);
+ return methodTimeout;
+ }
+
+ /*
+ * RFC 3748 recommends algorithms described in RFC 2988 for estimation
+ * of the retransmission timeout. This should be implemented once
+ * round-trip time measurements are available. For nowm a simple
+ * backoff mechanism is used instead if there are no EAP method
+ * specific hints.
+ *
+ * SRTT = smoothed round-trip time
+ * RTTVAR = round-trip time variation
+ * RTO = retransmission timeout
+ */
+
+ /*
+ * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for
+ * initial retransmission and then double the RTO to provide back off
+ * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3
+ * modified RTOmax.
+ */
+ rto = 3;
+ for (i = 0; i < retransCount; i++) {
+ rto *= 2;
+ if (rto >= 20) {
+ rto = 20;
+ break;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
+ "(from dynamic back off; retransCount=%d)",
+ rto, retransCount);
+
+ return rto;
+}
+
+
+static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
+{
+ const struct eap_hdr *hdr;
+ size_t plen;
+
+ /* parse rxResp, respId, respMethod */
+ sm->rxResp = FALSE;
+ sm->respId = -1;
+ sm->respMethod = EAP_TYPE_NONE;
+ sm->respVendor = EAP_VENDOR_IETF;
+ sm->respVendorMethod = EAP_TYPE_NONE;
+
+ if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) {
+ wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p "
+ "len=%lu", resp,
+ resp ? (unsigned long) wpabuf_len(resp) : 0);
+ return;
+ }
+
+ hdr = wpabuf_head(resp);
+ plen = be_to_host16(hdr->length);
+ if (plen > wpabuf_len(resp)) {
+ wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
+ "(len=%lu plen=%lu)",
+ (unsigned long) wpabuf_len(resp),
+ (unsigned long) plen);
+ return;
+ }
+
+ sm->respId = hdr->identifier;
+
+ if (hdr->code == EAP_CODE_RESPONSE)
+ sm->rxResp = TRUE;
+
+ if (plen > sizeof(*hdr)) {
+ u8 *pos = (u8 *) (hdr + 1);
+ sm->respMethod = *pos++;
+ if (sm->respMethod == EAP_TYPE_EXPANDED) {
+ if (plen < sizeof(*hdr) + 8) {
+ wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
+ "expanded EAP-Packet (plen=%lu)",
+ (unsigned long) plen);
+ return;
+ }
+ sm->respVendor = WPA_GET_BE24(pos);
+ pos += 3;
+ sm->respVendorMethod = WPA_GET_BE32(pos);
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
+ "respMethod=%u respVendor=%u respVendorMethod=%u",
+ sm->rxResp, sm->respId, sm->respMethod, sm->respVendor,
+ sm->respVendorMethod);
+}
+
+
+static int eap_sm_getId(const struct wpabuf *data)
+{
+ const struct eap_hdr *hdr;
+
+ if (data == NULL || wpabuf_len(data) < sizeof(*hdr))
+ return -1;
+
+ hdr = wpabuf_head(data);
+ wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier);
+ return hdr->identifier;
+}
+
+
+static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id)
+{
+ struct wpabuf *msg;
+ struct eap_hdr *resp;
+ wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id);
+
+ msg = wpabuf_alloc(sizeof(*resp));
+ if (msg == NULL)
+ return NULL;
+ resp = wpabuf_put(msg, sizeof(*resp));
+ resp->code = EAP_CODE_SUCCESS;
+ resp->identifier = id;
+ resp->length = host_to_be16(sizeof(*resp));
+
+ return msg;
+}
+
+
+static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id)
+{
+ struct wpabuf *msg;
+ struct eap_hdr *resp;
+ wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id);
+
+ msg = wpabuf_alloc(sizeof(*resp));
+ if (msg == NULL)
+ return NULL;
+ resp = wpabuf_put(msg, sizeof(*resp));
+ resp->code = EAP_CODE_FAILURE;
+ resp->identifier = id;
+ resp->length = host_to_be16(sizeof(*resp));
+
+ return msg;
+}
+
+
+static int eap_sm_nextId(struct eap_sm *sm, int id)
+{
+ if (id < 0) {
+ /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a
+ * random number */
+ id = rand() & 0xff;
+ if (id != sm->lastId)
+ return id;
+ }
+ return (id + 1) & 0xff;
+}
+
+
+/**
+ * eap_sm_process_nak - Process EAP-Response/Nak
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * @nak_list: Nak list (allowed methods) from the supplicant
+ * @len: Length of nak_list in bytes
+ *
+ * This function is called when EAP-Response/Nak is received from the
+ * supplicant. This can happen for both phase 1 and phase 2 authentications.
+ */
+void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len)
+{
+ int i;
+ size_t j;
+
+ if (sm->user == NULL)
+ return;
+
+ wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method "
+ "index %d)", sm->user_eap_method_index);
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods",
+ (u8 *) sm->user->methods,
+ EAP_MAX_METHODS * sizeof(sm->user->methods[0]));
+ wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer",
+ nak_list, len);
+
+ i = sm->user_eap_method_index;
+ while (i < EAP_MAX_METHODS &&
+ (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+ sm->user->methods[i].method != EAP_TYPE_NONE)) {
+ if (sm->user->methods[i].vendor != EAP_VENDOR_IETF)
+ goto not_found;
+ for (j = 0; j < len; j++) {
+ if (nak_list[j] == sm->user->methods[i].method) {
+ break;
+ }
+ }
+
+ if (j < len) {
+ /* found */
+ i++;
+ continue;
+ }
+
+ not_found:
+ /* not found - remove from the list */
+ os_memmove(&sm->user->methods[i], &sm->user->methods[i + 1],
+ (EAP_MAX_METHODS - i - 1) *
+ sizeof(sm->user->methods[0]));
+ sm->user->methods[EAP_MAX_METHODS - 1].vendor =
+ EAP_VENDOR_IETF;
+ sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods",
+ (u8 *) sm->user->methods, EAP_MAX_METHODS *
+ sizeof(sm->user->methods[0]));
+}
+
+
+static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
+ size_t len)
+{
+ if (nak_list == NULL || sm == NULL || sm->user == NULL)
+ return;
+
+ if (sm->user->phase2) {
+ wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user"
+ " info was selected - reject");
+ sm->decision = DECISION_FAILURE;
+ return;
+ }
+
+ eap_sm_process_nak(sm, nak_list, len);
+}
+
+
+static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor)
+{
+ EapType next;
+ int idx = sm->user_eap_method_index;
+
+ /* In theory, there should be no problems with starting
+ * re-authentication with something else than EAP-Request/Identity and
+ * this does indeed work with wpa_supplicant. However, at least Funk
+ * Supplicant seemed to ignore re-auth if it skipped
+ * EAP-Request/Identity.
+ * Re-auth sets currentId == -1, so that can be used here to select
+ * whether Identity needs to be requested again. */
+ if (sm->identity == NULL || sm->currentId == -1) {
+ *vendor = EAP_VENDOR_IETF;
+ next = EAP_TYPE_IDENTITY;
+ sm->update_user = TRUE;
+ } else if (sm->user && idx < EAP_MAX_METHODS &&
+ (sm->user->methods[idx].vendor != EAP_VENDOR_IETF ||
+ sm->user->methods[idx].method != EAP_TYPE_NONE)) {
+ *vendor = sm->user->methods[idx].vendor;
+ next = sm->user->methods[idx].method;
+ sm->user_eap_method_index++;
+ } else {
+ *vendor = EAP_VENDOR_IETF;
+ next = EAP_TYPE_NONE;
+ }
+ wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d",
+ *vendor, next);
+ return next;
+}
+
+
+static int eap_sm_Policy_getDecision(struct eap_sm *sm)
+{
+ if (!sm->eap_server && sm->identity && !sm->start_reauth) {
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH");
+ return DECISION_PASSTHROUGH;
+ }
+
+ if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY &&
+ sm->m->isSuccess(sm, sm->eap_method_priv)) {
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> "
+ "SUCCESS");
+ sm->update_user = TRUE;
+ return DECISION_SUCCESS;
+ }
+
+ if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) &&
+ !sm->m->isSuccess(sm, sm->eap_method_priv)) {
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> "
+ "FAILURE");
+ sm->update_user = TRUE;
+ return DECISION_FAILURE;
+ }
+
+ if ((sm->user == NULL || sm->update_user) && sm->identity &&
+ !sm->start_reauth) {
+ /*
+ * Allow Identity method to be started once to allow identity
+ * selection hint to be sent from the authentication server,
+ * but prevent a loop of Identity requests by only allowing
+ * this to happen once.
+ */
+ int id_req = 0;
+ if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY &&
+ sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
+ sm->user->methods[0].method == EAP_TYPE_IDENTITY)
+ id_req = 1;
+ if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: user not "
+ "found from database -> FAILURE");
+ return DECISION_FAILURE;
+ }
+ if (id_req && sm->user &&
+ sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
+ sm->user->methods[0].method == EAP_TYPE_IDENTITY) {
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: stop "
+ "identity request loop -> FAILURE");
+ sm->update_user = TRUE;
+ return DECISION_FAILURE;
+ }
+ sm->update_user = FALSE;
+ }
+ sm->start_reauth = FALSE;
+
+ if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+ (sm->user->methods[sm->user_eap_method_index].vendor !=
+ EAP_VENDOR_IETF ||
+ sm->user->methods[sm->user_eap_method_index].method !=
+ EAP_TYPE_NONE)) {
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: another method "
+ "available -> CONTINUE");
+ return DECISION_CONTINUE;
+ }
+
+ if (sm->identity == NULL || sm->currentId == -1) {
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
+ "yet -> CONTINUE");
+ return DECISION_CONTINUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> "
+ "FAILURE");
+ return DECISION_FAILURE;
+}
+
+
+static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method)
+{
+ return method == EAP_TYPE_IDENTITY ? TRUE : FALSE;
+}
+
+
+/**
+ * eap_server_sm_step - Step EAP server state machine
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * Returns: 1 if EAP state was changed or 0 if not
+ *
+ * This function advances EAP state machine to a new state to match with the
+ * current variables. This should be called whenever variables used by the EAP
+ * state machine have changed.
+ */
+int eap_server_sm_step(struct eap_sm *sm)
+{
+ int res = 0;
+ do {
+ sm->changed = FALSE;
+ SM_STEP_RUN(EAP);
+ if (sm->changed)
+ res = 1;
+ } while (sm->changed);
+ return res;
+}
+
+
+static void eap_user_free(struct eap_user *user)
+{
+ if (user == NULL)
+ return;
+ os_free(user->password);
+ user->password = NULL;
+ os_free(user);
+}
+
+
+/**
+ * eap_server_sm_init - Allocate and initialize EAP server state machine
+ * @eapol_ctx: Context data to be used with eapol_cb calls
+ * @eapol_cb: Pointer to EAPOL callback functions
+ * @conf: EAP configuration
+ * Returns: Pointer to the allocated EAP state machine or %NULL on failure
+ *
+ * This function allocates and initializes an EAP state machine.
+ */
+struct eap_sm * eap_server_sm_init(void *eapol_ctx,
+ struct eapol_callbacks *eapol_cb,
+ struct eap_config *conf)
+{
+ struct eap_sm *sm;
+
+ sm = os_zalloc(sizeof(*sm));
+ if (sm == NULL)
+ return NULL;
+ sm->eapol_ctx = eapol_ctx;
+ sm->eapol_cb = eapol_cb;
+ sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */
+ sm->ssl_ctx = conf->ssl_ctx;
+ sm->msg_ctx = conf->msg_ctx;
+ sm->eap_sim_db_priv = conf->eap_sim_db_priv;
+ sm->backend_auth = conf->backend_auth;
+ sm->eap_server = conf->eap_server;
+ if (conf->pac_opaque_encr_key) {
+ sm->pac_opaque_encr_key = os_malloc(16);
+ if (sm->pac_opaque_encr_key) {
+ os_memcpy(sm->pac_opaque_encr_key,
+ conf->pac_opaque_encr_key, 16);
+ }
+ }
+ if (conf->eap_fast_a_id) {
+ sm->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
+ if (sm->eap_fast_a_id) {
+ os_memcpy(sm->eap_fast_a_id, conf->eap_fast_a_id,
+ conf->eap_fast_a_id_len);
+ sm->eap_fast_a_id_len = conf->eap_fast_a_id_len;
+ }
+ }
+ if (conf->eap_fast_a_id_info)
+ sm->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
+ sm->eap_fast_prov = conf->eap_fast_prov;
+ sm->pac_key_lifetime = conf->pac_key_lifetime;
+ sm->pac_key_refresh_time = conf->pac_key_refresh_time;
+ sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
+ sm->tnc = conf->tnc;
+ sm->wps = conf->wps;
+ if (conf->assoc_wps_ie)
+ sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie);
+ if (conf->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;
+
+ 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/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
new file mode 100644
index 0000000..42cbdce
--- /dev/null
+++ b/src/eap_server/eap_server_aka.c
@@ -0,0 +1,1278 @@
+/*
+ * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf)
+ * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
+#include "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;
+};
+
+
+static void eap_aka_determine_identity(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ int before_identity, int after_reauth);
+
+
+static const char * eap_aka_state_txt(int state)
+{
+ switch (state) {
+ case IDENTITY:
+ return "IDENTITY";
+ case CHALLENGE:
+ return "CHALLENGE";
+ case REAUTH:
+ return "REAUTH";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ case NOTIFICATION:
+ return "NOTIFICATION";
+ default:
+ return "Unknown?!";
+ }
+}
+
+
+static void eap_aka_state(struct eap_aka_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s",
+ eap_aka_state_txt(data->state),
+ eap_aka_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_aka_init(struct eap_sm *sm)
+{
+ struct eap_aka_data *data;
+
+ if (sm->eap_sim_db_priv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->eap_method = EAP_TYPE_AKA;
+
+ data->state = IDENTITY;
+ eap_aka_determine_identity(sm, data, 1, 0);
+ data->pending_id = -1;
+
+ return data;
+}
+
+
+#ifdef EAP_SERVER_AKA_PRIME
+static void * eap_aka_prime_init(struct eap_sm *sm)
+{
+ struct eap_aka_data *data;
+ /* TODO: make ANID configurable; see 3GPP TS 24.302 */
+ char *network_name = "WLAN";
+
+ if (sm->eap_sim_db_priv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->eap_method = EAP_TYPE_AKA_PRIME;
+ data->network_name = os_malloc(os_strlen(network_name));
+ if (data->network_name == NULL) {
+ os_free(data);
+ return NULL;
+ }
+
+ data->network_name_len = os_strlen(network_name);
+ os_memcpy(data->network_name, network_name, data->network_name_len);
+
+ data->state = IDENTITY;
+ eap_aka_determine_identity(sm, data, 1, 0);
+ data->pending_id = -1;
+
+ return data;
+}
+#endif /* EAP_SERVER_AKA_PRIME */
+
+
+static void eap_aka_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ os_free(data->next_pseudonym);
+ os_free(data->next_reauth_id);
+ wpabuf_free(data->id_msgs);
+ os_free(data->network_name);
+ os_free(data);
+}
+
+
+static int eap_aka_add_id_msg(struct eap_aka_data *data,
+ const struct wpabuf *msg)
+{
+ if (msg == NULL)
+ return -1;
+
+ if (data->id_msgs == NULL) {
+ data->id_msgs = wpabuf_dup(msg);
+ return data->id_msgs == NULL ? -1 : 0;
+ }
+
+ if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0)
+ return -1;
+ wpabuf_put_buf(data->id_msgs, msg);
+
+ return 0;
+}
+
+
+static void eap_aka_add_checkcode(struct eap_aka_data *data,
+ struct eap_sim_msg *msg)
+{
+ const u8 *addr;
+ size_t len;
+ u8 hash[SHA256_MAC_LEN];
+
+ wpa_printf(MSG_DEBUG, " AT_CHECKCODE");
+
+ if (data->id_msgs == NULL) {
+ /*
+ * No EAP-AKA/Identity packets were exchanged - send empty
+ * checkcode.
+ */
+ eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0);
+ return;
+ }
+
+ /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */
+ addr = wpabuf_head(data->id_msgs);
+ len = wpabuf_len(data->id_msgs);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len);
+ if (data->eap_method == EAP_TYPE_AKA_PRIME)
+ sha256_vector(1, &addr, &len, hash);
+ else
+ sha1_vector(1, &addr, &len, hash);
+
+ eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash,
+ data->eap_method == EAP_TYPE_AKA_PRIME ?
+ EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN);
+}
+
+
+static int eap_aka_verify_checkcode(struct eap_aka_data *data,
+ const u8 *checkcode, size_t checkcode_len)
+{
+ const u8 *addr;
+ size_t len;
+ u8 hash[SHA256_MAC_LEN];
+ size_t hash_len;
+
+ if (checkcode == NULL)
+ return -1;
+
+ if (data->id_msgs == NULL) {
+ if (checkcode_len != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer "
+ "indicates that AKA/Identity messages were "
+ "used, but they were not");
+ return -1;
+ }
+ return 0;
+ }
+
+ hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ?
+ EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN;
+
+ if (checkcode_len != hash_len) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates "
+ "that AKA/Identity message were not used, but they "
+ "were");
+ return -1;
+ }
+
+ /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */
+ addr = wpabuf_head(data->id_msgs);
+ len = wpabuf_len(data->id_msgs);
+ if (data->eap_method == EAP_TYPE_AKA_PRIME)
+ sha256_vector(1, &addr, &len, hash);
+ else
+ sha1_vector(1, &addr, &len, hash);
+
+ if (os_memcmp(hash, checkcode, hash_len) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm,
+ struct eap_aka_data *data, u8 id)
+{
+ struct eap_sim_msg *msg;
+ struct wpabuf *buf;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity");
+ msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
+ EAP_AKA_SUBTYPE_IDENTITY);
+ if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
+ sm->identity_len)) {
+ wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ");
+ eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
+ } else {
+ /*
+ * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is
+ * ignored and the AKA/Identity is used to request the
+ * identity.
+ */
+ wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ");
+ eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
+ }
+ buf = eap_sim_msg_finish(msg, NULL, NULL, 0);
+ if (eap_aka_add_id_msg(data, buf) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ data->pending_id = id;
+ return buf;
+}
+
+
+static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data,
+ struct eap_sim_msg *msg, u16 counter,
+ const u8 *nonce_s)
+{
+ os_free(data->next_pseudonym);
+ data->next_pseudonym =
+ eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1);
+ os_free(data->next_reauth_id);
+ if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) {
+ data->next_reauth_id =
+ eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 1);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication "
+ "count exceeded - force full authentication");
+ data->next_reauth_id = NULL;
+ }
+
+ if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
+ counter == 0 && nonce_s == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+ if (counter > 0) {
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+ }
+
+ if (nonce_s) {
+ wpa_printf(MSG_DEBUG, " *AT_NONCE_S");
+ eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
+ EAP_SIM_NONCE_S_LEN);
+ }
+
+ if (data->next_pseudonym) {
+ wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)",
+ data->next_pseudonym);
+ eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
+ os_strlen(data->next_pseudonym),
+ (u8 *) data->next_pseudonym,
+ os_strlen(data->next_pseudonym));
+ }
+
+ if (data->next_reauth_id) {
+ wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)",
+ data->next_reauth_id);
+ eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
+ os_strlen(data->next_reauth_id),
+ (u8 *) data->next_reauth_id,
+ os_strlen(data->next_reauth_id));
+ }
+
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+ "AT_ENCR_DATA");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ u8 id)
+{
+ struct eap_sim_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge");
+ msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
+ EAP_AKA_SUBTYPE_CHALLENGE);
+ wpa_printf(MSG_DEBUG, " AT_RAND");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN);
+ wpa_printf(MSG_DEBUG, " AT_AUTN");
+ eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN);
+ if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+ if (data->kdf) {
+ /* Add the selected KDF into the beginning */
+ wpa_printf(MSG_DEBUG, " AT_KDF");
+ eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf,
+ NULL, 0);
+ }
+ wpa_printf(MSG_DEBUG, " AT_KDF");
+ eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF,
+ NULL, 0);
+ wpa_printf(MSG_DEBUG, " AT_KDF_INPUT");
+ eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT,
+ data->network_name_len,
+ data->network_name, data->network_name_len);
+ }
+
+ if (eap_aka_build_encr(sm, data, msg, 0, NULL)) {
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+
+ eap_aka_add_checkcode(data, msg);
+
+ if (sm->eap_sim_aka_result_ind) {
+ wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+ }
+
+#ifdef EAP_SERVER_AKA_PRIME
+ if (data->eap_method == EAP_TYPE_AKA) {
+ u16 flags = 0;
+ int i;
+ int aka_prime_preferred = 0;
+
+ i = 0;
+ while (sm->user && i < EAP_MAX_METHODS &&
+ (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+ sm->user->methods[i].method != EAP_TYPE_NONE)) {
+ if (sm->user->methods[i].vendor == EAP_VENDOR_IETF) {
+ if (sm->user->methods[i].method ==
+ EAP_TYPE_AKA)
+ break;
+ if (sm->user->methods[i].method ==
+ EAP_TYPE_AKA_PRIME) {
+ aka_prime_preferred = 1;
+ break;
+ }
+ }
+ i++;
+ }
+
+ if (aka_prime_preferred)
+ flags |= EAP_AKA_BIDDING_FLAG_D;
+ eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0);
+ }
+#endif /* EAP_SERVER_AKA_PRIME */
+
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm,
+ struct eap_aka_data *data, u8 id)
+{
+ struct eap_sim_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication");
+
+ if (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,
+ int before_identity, int after_reauth)
+{
+ const u8 *identity;
+ size_t identity_len;
+ int res;
+
+ identity = NULL;
+ identity_len = 0;
+
+ if (after_reauth && data->reauth) {
+ identity = data->reauth->identity;
+ identity_len = data->reauth->identity_len;
+ } else if (sm->identity && sm->identity_len > 0 &&
+ sm->identity[0] == EAP_AKA_PERMANENT_PREFIX) {
+ identity = sm->identity;
+ identity_len = sm->identity_len;
+ } else {
+ identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
+ sm->identity,
+ sm->identity_len,
+ &identity_len);
+ if (identity == NULL) {
+ data->reauth = eap_sim_db_get_reauth_entry(
+ sm->eap_sim_db_priv, sm->identity,
+ sm->identity_len);
+ if (data->reauth &&
+ data->reauth->aka_prime !=
+ (data->eap_method == EAP_TYPE_AKA_PRIME)) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data "
+ "was for different AKA version");
+ data->reauth = NULL;
+ }
+ if (data->reauth) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast "
+ "re-authentication");
+ identity = data->reauth->identity;
+ identity_len = data->reauth->identity_len;
+ data->counter = data->reauth->counter;
+ if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+ os_memcpy(data->k_encr,
+ data->reauth->k_encr,
+ EAP_SIM_K_ENCR_LEN);
+ os_memcpy(data->k_aut,
+ data->reauth->k_aut,
+ EAP_AKA_PRIME_K_AUT_LEN);
+ os_memcpy(data->k_re,
+ data->reauth->k_re,
+ EAP_AKA_PRIME_K_RE_LEN);
+ } else {
+ os_memcpy(data->mk, data->reauth->mk,
+ EAP_SIM_MK_LEN);
+ }
+ }
+ }
+ }
+
+ if (identity == NULL ||
+ eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
+ sm->identity_len) < 0) {
+ if (before_identity) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent user name "
+ "not known - send AKA-Identity request");
+ eap_aka_state(data, IDENTITY);
+ return;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown whether the "
+ "permanent user name is known; try to use "
+ "it");
+ /* eap_sim_db_get_aka_auth() will report failure, if
+ * this identity is not known. */
+ }
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity",
+ identity, identity_len);
+
+ if (!after_reauth && data->reauth) {
+ eap_aka_state(data, REAUTH);
+ return;
+ }
+
+ res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, identity,
+ identity_len, data->rand, data->autn,
+ data->ik, data->ck, data->res,
+ &data->res_len, sm);
+ if (res == EAP_SIM_DB_PENDING) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
+ "not yet available - pending request");
+ sm->method_pending = METHOD_PENDING_WAIT;
+ return;
+ }
+
+#ifdef EAP_SERVER_AKA_PRIME
+ if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+ /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
+ * needed 6-octet SQN ^AK for CK',IK' derivation */
+ eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik,
+ data->autn,
+ data->network_name,
+ data->network_name_len);
+ }
+#endif /* EAP_SERVER_AKA_PRIME */
+
+ data->reauth = NULL;
+ data->counter = 0; /* reset re-auth counter since this is full auth */
+
+ if (res != 0) {
+ wpa_printf(MSG_INFO, "EAP-AKA: Failed to get AKA "
+ "authentication data for the peer");
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
+ }
+ if (sm->method_pending == METHOD_PENDING_WAIT) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
+ "available - abort pending wait");
+ sm->method_pending = METHOD_PENDING_NONE;
+ }
+
+ identity_len = sm->identity_len;
+ while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Workaround - drop last null "
+ "character from identity");
+ identity_len--;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation",
+ sm->identity, identity_len);
+
+ if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+ eap_aka_prime_derive_keys(identity, identity_len, data->ik,
+ data->ck, data->k_encr, data->k_aut,
+ data->k_re, data->msk, data->emsk);
+ } else {
+ eap_aka_derive_mk(sm->identity, identity_len, data->ik,
+ data->ck, data->mk);
+ eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
+ data->msk, data->emsk);
+ }
+
+ eap_aka_state(data, CHALLENGE);
+}
+
+
+static void eap_aka_process_identity(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct wpabuf *respData,
+ struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity");
+
+ if (attr->mac || attr->iv || attr->encr_data) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Unexpected attribute "
+ "received in EAP-Response/AKA-Identity");
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
+ }
+
+ if (attr->identity) {
+ os_free(sm->identity);
+ sm->identity = os_malloc(attr->identity_len);
+ if (sm->identity) {
+ os_memcpy(sm->identity, attr->identity,
+ attr->identity_len);
+ sm->identity_len = attr->identity_len;
+ }
+ }
+
+ eap_aka_determine_identity(sm, data, 0, 0);
+ if (eap_get_id(respData) == data->pending_id) {
+ data->pending_id = -1;
+ eap_aka_add_id_msg(data, respData);
+ }
+}
+
+
+static int eap_aka_verify_mac(struct eap_aka_data *data,
+ const struct wpabuf *req,
+ const u8 *mac, const u8 *extra,
+ size_t extra_len)
+{
+ if (data->eap_method == EAP_TYPE_AKA_PRIME)
+ return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra,
+ extra_len);
+ return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len);
+}
+
+
+static void eap_aka_process_challenge(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct wpabuf *respData,
+ struct eap_sim_attrs *attr)
+{
+ const u8 *identity;
+ size_t identity_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge");
+
+#ifdef EAP_SERVER_AKA_PRIME
+#if 0
+ /* KDF negotiation; to be enabled only after more than one KDF is
+ * supported */
+ if (data->eap_method == EAP_TYPE_AKA_PRIME &&
+ attr->kdf_count == 1 && attr->mac == NULL) {
+ if (attr->kdf[0] != EAP_AKA_PRIME_KDF) {
+ wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected "
+ "unknown KDF");
+ data->notification =
+ EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
+ }
+
+ data->kdf = attr->kdf[0];
+
+ /* Allow negotiation to continue with the selected KDF by
+ * sending another Challenge message */
+ wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
+ return;
+ }
+#endif
+#endif /* EAP_SERVER_AKA_PRIME */
+
+ if (attr->checkcode &&
+ eap_aka_verify_checkcode(data, attr->checkcode,
+ attr->checkcode_len)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
+ "message");
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
+ }
+ if (attr->mac == NULL ||
+ eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+ "did not include valid AT_MAC");
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
+ }
+
+ /*
+ * AT_RES is padded, so verify that there is enough room for RES and
+ * that the RES length in bits matches with the expected RES.
+ */
+ if (attr->res == NULL || attr->res_len < data->res_len ||
+ attr->res_len_bits != data->res_len * 8 ||
+ os_memcmp(attr->res, data->res, data->res_len) != 0) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not "
+ "include valid AT_RES (attr len=%lu, res len=%lu "
+ "bits, expected %lu bits)",
+ (unsigned long) attr->res_len,
+ (unsigned long) attr->res_len_bits,
+ (unsigned long) data->res_len * 8);
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the "
+ "correct AT_MAC");
+ if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+ data->use_result_ind = 1;
+ data->notification = EAP_SIM_SUCCESS;
+ eap_aka_state(data, NOTIFICATION);
+ } else
+ eap_aka_state(data, SUCCESS);
+
+ identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
+ sm->identity_len, &identity_len);
+ if (identity == NULL) {
+ identity = sm->identity;
+ identity_len = sm->identity_len;
+ }
+
+ if (data->next_pseudonym) {
+ eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
+ identity_len,
+ data->next_pseudonym);
+ data->next_pseudonym = NULL;
+ }
+ if (data->next_reauth_id) {
+ if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+#ifdef EAP_SERVER_AKA_PRIME
+ eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
+ identity,
+ identity_len,
+ data->next_reauth_id,
+ data->counter + 1,
+ data->k_encr, data->k_aut,
+ data->k_re);
+#endif /* EAP_SERVER_AKA_PRIME */
+ } else {
+ eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
+ identity_len,
+ data->next_reauth_id,
+ data->counter + 1,
+ data->mk);
+ }
+ data->next_reauth_id = NULL;
+ }
+}
+
+
+static void eap_aka_process_sync_failure(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct wpabuf *respData,
+ struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure");
+
+ if (attr->auts == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Synchronization-Failure "
+ "message did not include valid AT_AUTS");
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
+ }
+
+ /* Avoid re-reporting AUTS when processing pending EAP packet by
+ * maintaining a local flag stating whether this AUTS has already been
+ * reported. */
+ if (!data->auts_reported &&
+ eap_sim_db_resynchronize(sm->eap_sim_db_priv, sm->identity,
+ sm->identity_len, attr->auts,
+ data->rand)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed");
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
+ }
+ data->auts_reported = 1;
+
+ /* Try again after resynchronization */
+ eap_aka_determine_identity(sm, data, 0, 0);
+}
+
+
+static void eap_aka_process_reauth(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct wpabuf *respData,
+ struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+ u8 *decrypted = NULL;
+ const u8 *identity, *id2;
+ size_t identity_len, id2_len;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication");
+
+ if (attr->mac == NULL ||
+ eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s,
+ EAP_SIM_NONCE_S_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message "
+ "did not include valid AT_MAC");
+ goto fail;
+ }
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+ "message did not include encrypted data");
+ goto fail;
+ }
+
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr,
+ 0);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+ "data from reauthentication message");
+ goto fail;
+ }
+
+ if (eattr.counter != data->counter) {
+ wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message "
+ "used incorrect counter %u, expected %u",
+ eattr.counter, data->counter);
+ goto fail;
+ }
+ os_free(decrypted);
+ decrypted = NULL;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes "
+ "the correct AT_MAC");
+
+ if (eattr.counter_too_small) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
+ "included AT_COUNTER_TOO_SMALL - starting full "
+ "authentication");
+ eap_aka_determine_identity(sm, data, 0, 1);
+ return;
+ }
+
+ if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+ data->use_result_ind = 1;
+ data->notification = EAP_SIM_SUCCESS;
+ eap_aka_state(data, NOTIFICATION);
+ } else
+ eap_aka_state(data, SUCCESS);
+
+ if (data->reauth) {
+ identity = data->reauth->identity;
+ identity_len = data->reauth->identity_len;
+ } else {
+ identity = sm->identity;
+ identity_len = sm->identity_len;
+ }
+
+ id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
+ identity_len, &id2_len);
+ if (id2) {
+ identity = id2;
+ identity_len = id2_len;
+ }
+
+ if (data->next_pseudonym) {
+ eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
+ identity_len, data->next_pseudonym);
+ data->next_pseudonym = NULL;
+ }
+ if (data->next_reauth_id) {
+ if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+#ifdef EAP_SERVER_AKA_PRIME
+ eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
+ identity,
+ identity_len,
+ data->next_reauth_id,
+ data->counter + 1,
+ data->k_encr, data->k_aut,
+ data->k_re);
+#endif /* EAP_SERVER_AKA_PRIME */
+ } else {
+ eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
+ identity_len,
+ data->next_reauth_id,
+ data->counter + 1,
+ data->mk);
+ }
+ data->next_reauth_id = NULL;
+ } else {
+ eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+ data->reauth = NULL;
+ }
+
+ return;
+
+fail:
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+ data->reauth = NULL;
+ os_free(decrypted);
+}
+
+
+static void eap_aka_process_client_error(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct wpabuf *respData,
+ struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d",
+ attr->client_error_code);
+ if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
+ eap_aka_state(data, SUCCESS);
+ else
+ eap_aka_state(data, FAILURE);
+}
+
+
+static void eap_aka_process_authentication_reject(
+ struct eap_sm *sm, struct eap_aka_data *data,
+ struct wpabuf *respData, struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication");
+ eap_aka_state(data, FAILURE);
+}
+
+
+static void eap_aka_process_notification(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ struct wpabuf *respData,
+ struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification");
+ if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
+ eap_aka_state(data, SUCCESS);
+ else
+ eap_aka_state(data, FAILURE);
+}
+
+
+static void eap_aka_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_aka_data *data = priv;
+ const u8 *pos, *end;
+ u8 subtype;
+ size_t len;
+ struct eap_sim_attrs attr;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData,
+ &len);
+ if (pos == NULL || len < 3)
+ return;
+
+ end = pos + len;
+ subtype = *pos;
+ pos += 3;
+
+ if (eap_aka_subtype_ok(data, subtype)) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected "
+ "EAP-AKA Subtype in EAP Response");
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
+ }
+
+ if (eap_sim_parse_attr(pos, end, &attr,
+ data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
+ 0)) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes");
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
+ }
+
+ if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) {
+ eap_aka_process_client_error(sm, data, respData, &attr);
+ return;
+ }
+
+ if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) {
+ eap_aka_process_authentication_reject(sm, data, respData,
+ &attr);
+ return;
+ }
+
+ switch (data->state) {
+ case IDENTITY:
+ eap_aka_process_identity(sm, data, respData, &attr);
+ break;
+ case CHALLENGE:
+ if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) {
+ eap_aka_process_sync_failure(sm, data, respData,
+ &attr);
+ } else {
+ eap_aka_process_challenge(sm, data, respData, &attr);
+ }
+ break;
+ case REAUTH:
+ eap_aka_process_reauth(sm, data, respData, &attr);
+ break;
+ case NOTIFICATION:
+ eap_aka_process_notification(sm, data, respData, &attr);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in "
+ "process", data->state);
+ break;
+ }
+}
+
+
+static Boolean eap_aka_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_aka_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+ *len = EAP_SIM_KEYING_DATA_LEN;
+ return key;
+}
+
+
+static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_aka_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+ return key;
+}
+
+
+static Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_aka_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_aka_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_aka_init;
+ eap->reset = eap_aka_reset;
+ eap->buildReq = eap_aka_buildReq;
+ eap->check = eap_aka_check;
+ eap->process = eap_aka_process;
+ eap->isDone = eap_aka_isDone;
+ eap->getKey = eap_aka_getKey;
+ eap->isSuccess = eap_aka_isSuccess;
+ eap->get_emsk = eap_aka_get_emsk;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
+
+
+#ifdef EAP_SERVER_AKA_PRIME
+int eap_server_aka_prime_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME,
+ "AKA'");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_aka_prime_init;
+ eap->reset = eap_aka_reset;
+ eap->buildReq = eap_aka_buildReq;
+ eap->check = eap_aka_check;
+ eap->process = eap_aka_process;
+ eap->isDone = eap_aka_isDone;
+ eap->getKey = eap_aka_getKey;
+ eap->isSuccess = eap_aka_isSuccess;
+ eap->get_emsk = eap_aka_get_emsk;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+
+ return ret;
+}
+#endif /* EAP_SERVER_AKA_PRIME */
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
new file mode 100644
index 0000000..ba17e98
--- /dev/null
+++ b/src/eap_server/eap_server_fast.c
@@ -0,0 +1,1620 @@
+/*
+ * EAP-FAST server (RFC 4851)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "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/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c
new file mode 100644
index 0000000..a794806
--- /dev/null
+++ b/src/eap_server/eap_server_gpsk.c
@@ -0,0 +1,634 @@
+/*
+ * hostapd / EAP-GPSK (RFC 5433) server
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "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;
+ u8 *id_server;
+ size_t id_server_len;
+#define MAX_NUM_CSUITES 2
+ struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES];
+ size_t csuite_count;
+ int vendor; /* CSuite/Vendor */
+ int specifier; /* CSuite/Specifier */
+};
+
+
+static const char * eap_gpsk_state_txt(int state)
+{
+ switch (state) {
+ case GPSK_1:
+ return "GPSK-1";
+ case GPSK_3:
+ return "GPSK-3";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "?";
+ }
+}
+
+
+static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
+ eap_gpsk_state_txt(data->state),
+ eap_gpsk_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_gpsk_init(struct eap_sm *sm)
+{
+ struct eap_gpsk_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = GPSK_1;
+
+ /* TODO: add support for configuring ID_Server */
+ data->id_server = (u8 *) os_strdup("hostapd");
+ if (data->id_server)
+ data->id_server_len = os_strlen((char *) data->id_server);
+
+ data->csuite_count = 0;
+ if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF,
+ EAP_GPSK_CIPHER_AES)) {
+ WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor,
+ EAP_GPSK_VENDOR_IETF);
+ WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier,
+ EAP_GPSK_CIPHER_AES);
+ data->csuite_count++;
+ }
+ if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF,
+ EAP_GPSK_CIPHER_SHA256)) {
+ WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor,
+ EAP_GPSK_VENDOR_IETF);
+ WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier,
+ EAP_GPSK_CIPHER_SHA256);
+ data->csuite_count++;
+ }
+
+ return data;
+}
+
+
+static void eap_gpsk_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_gpsk_data *data = priv;
+ os_free(data->id_server);
+ os_free(data->id_peer);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm,
+ struct eap_gpsk_data *data, u8 id)
+{
+ size_t len;
+ struct wpabuf *req;
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1");
+
+ if (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 + data->id_server_len + EAP_GPSK_RAND_LEN + 2 +
+ data->csuite_count * sizeof(struct eap_gpsk_csuite);
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory "
+ "for request/GPSK-1");
+ eap_gpsk_state(data, FAILURE);
+ return NULL;
+ }
+
+ wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1);
+ wpabuf_put_be16(req, data->id_server_len);
+ wpabuf_put_data(req, data->id_server, data->id_server_len);
+ wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
+ wpabuf_put_be16(req,
+ data->csuite_count * sizeof(struct eap_gpsk_csuite));
+ wpabuf_put_data(req, data->csuite_list,
+ data->csuite_count * sizeof(struct eap_gpsk_csuite));
+
+ return req;
+}
+
+
+static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm,
+ struct eap_gpsk_data *data, u8 id)
+{
+ u8 *pos, *start;
+ size_t len, miclen;
+ struct eap_gpsk_csuite *csuite;
+ struct wpabuf *req;
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3");
+
+ miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+ len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + data->id_server_len +
+ sizeof(struct eap_gpsk_csuite) + 2 + miclen;
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory "
+ "for request/GPSK-3");
+ eap_gpsk_state(data, FAILURE);
+ return NULL;
+ }
+
+ wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_3);
+ start = wpabuf_put(req, 0);
+
+ wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN);
+ wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
+ wpabuf_put_be16(req, data->id_server_len);
+ wpabuf_put_data(req, data->id_server, data->id_server_len);
+ csuite = wpabuf_put(req, sizeof(*csuite));
+ WPA_PUT_BE32(csuite->vendor, data->vendor);
+ WPA_PUT_BE16(csuite->specifier, data->specifier);
+
+ /* no PD_Payload_2 */
+ wpabuf_put_be16(req, 0);
+
+ pos = wpabuf_put(req, miclen);
+ if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+ data->specifier, start, pos - start, pos) < 0)
+ {
+ os_free(req);
+ eap_gpsk_state(data, FAILURE);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+static struct wpabuf * eap_gpsk_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+ struct eap_gpsk_data *data = priv;
+
+ switch (data->state) {
+ case GPSK_1:
+ return eap_gpsk_build_gpsk_1(sm, data, id);
+ case GPSK_3:
+ return eap_gpsk_build_gpsk_3(sm, data, id);
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown state %d in buildReq",
+ data->state);
+ break;
+ }
+ return NULL;
+}
+
+
+static Boolean eap_gpsk_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_gpsk_data *data = priv;
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len);
+ if (pos == NULL || len < 1) {
+ wpa_printf(MSG_INFO, "EAP-GPSK: Invalid frame");
+ return TRUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode=%d", *pos);
+
+ if (data->state == GPSK_1 && *pos == EAP_GPSK_OPCODE_GPSK_2)
+ return FALSE;
+
+ if (data->state == GPSK_3 && *pos == EAP_GPSK_OPCODE_GPSK_4)
+ return FALSE;
+
+ wpa_printf(MSG_INFO, "EAP-GPSK: Unexpected opcode=%d in state=%d",
+ *pos, data->state);
+
+ return TRUE;
+}
+
+
+static void eap_gpsk_process_gpsk_2(struct eap_sm *sm,
+ struct eap_gpsk_data *data,
+ const u8 *payload, size_t payloadlen)
+{
+ const u8 *pos, *end;
+ u16 alen;
+ const struct eap_gpsk_csuite *csuite;
+ size_t i, miclen;
+ u8 mic[EAP_GPSK_MAX_MIC_LEN];
+
+ if (data->state != GPSK_1)
+ return;
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-2");
+
+ pos = payload;
+ end = payload + payloadlen;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "ID_Peer length");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ alen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < alen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "ID_Peer");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ os_free(data->id_peer);
+ data->id_peer = os_malloc(alen);
+ if (data->id_peer == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store "
+ "%d-octet ID_Peer", alen);
+ return;
+ }
+ os_memcpy(data->id_peer, pos, alen);
+ data->id_peer_len = alen;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
+ data->id_peer, data->id_peer_len);
+ pos += alen;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "ID_Server length");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ alen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < alen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "ID_Server");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ if (alen != data->id_server_len ||
+ os_memcmp(pos, data->id_server, alen) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and "
+ "GPSK-2 did not match");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ pos += alen;
+
+ if (end - pos < EAP_GPSK_RAND_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "RAND_Peer");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ os_memcpy(data->rand_peer, pos, EAP_GPSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer",
+ data->rand_peer, EAP_GPSK_RAND_LEN);
+ pos += EAP_GPSK_RAND_LEN;
+
+ if (end - pos < EAP_GPSK_RAND_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "RAND_Server");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ if (os_memcmp(data->rand_server, pos, EAP_GPSK_RAND_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
+ "GPSK-2 did not match");
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
+ data->rand_server, EAP_GPSK_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-2",
+ pos, EAP_GPSK_RAND_LEN);
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ pos += EAP_GPSK_RAND_LEN;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "CSuite_List length");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ alen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < alen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "CSuite_List");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ if (alen != data->csuite_count * sizeof(struct eap_gpsk_csuite) ||
+ os_memcmp(pos, data->csuite_list, alen) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List in GPSK-1 and "
+ "GPSK-2 did not match");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ pos += alen;
+
+ if (end - pos < (int) sizeof(*csuite)) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "CSuite_Sel");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ csuite = (const struct eap_gpsk_csuite *) pos;
+ for (i = 0; i < data->csuite_count; i++) {
+ if (os_memcmp(csuite, &data->csuite_list[i], sizeof(*csuite))
+ == 0)
+ break;
+ }
+ if (i == data->csuite_count) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Peer selected unsupported "
+ "ciphersuite %d:%d",
+ WPA_GET_BE32(csuite->vendor),
+ WPA_GET_BE16(csuite->specifier));
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ data->vendor = WPA_GET_BE32(csuite->vendor);
+ data->specifier = WPA_GET_BE16(csuite->specifier);
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d",
+ data->vendor, data->specifier);
+ pos += sizeof(*csuite);
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "PD_Payload_1 length");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ alen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < alen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "PD_Payload_1");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen);
+ pos += alen;
+
+ if (sm->user == NULL || sm->user->password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-GPSK: No PSK/password configured "
+ "for the user");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+
+ if (eap_gpsk_derive_keys(sm->user->password, sm->user->password_len,
+ data->vendor, data->specifier,
+ data->rand_peer, data->rand_server,
+ data->id_peer, data->id_peer_len,
+ data->id_server, data->id_server_len,
+ data->msk, data->emsk,
+ data->sk, &data->sk_len,
+ data->pk, &data->pk_len) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+
+ miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+ if (end - pos < (int) miclen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
+ "(left=%lu miclen=%lu)",
+ (unsigned long) (end - pos),
+ (unsigned long) miclen);
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+ data->specifier, payload, pos - payload, mic)
+ < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ if (os_memcmp(mic, pos, miclen) != 0) {
+ wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-2");
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ pos += miclen;
+
+ if (pos != end) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
+ "data in the end of GPSK-2",
+ (unsigned long) (end - pos));
+ }
+
+ eap_gpsk_state(data, GPSK_3);
+}
+
+
+static void eap_gpsk_process_gpsk_4(struct eap_sm *sm,
+ struct eap_gpsk_data *data,
+ const u8 *payload, size_t payloadlen)
+{
+ const u8 *pos, *end;
+ u16 alen;
+ size_t miclen;
+ u8 mic[EAP_GPSK_MAX_MIC_LEN];
+
+ if (data->state != GPSK_3)
+ return;
+
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-4");
+
+ pos = payload;
+ end = payload + payloadlen;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "PD_Payload_1 length");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ alen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < alen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+ "PD_Payload_1");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen);
+ pos += alen;
+
+ miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+ if (end - pos < (int) miclen) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
+ "(left=%lu miclen=%lu)",
+ (unsigned long) (end - pos),
+ (unsigned long) miclen);
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+ data->specifier, payload, pos - payload, mic)
+ < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ if (os_memcmp(mic, pos, miclen) != 0) {
+ wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-4");
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
+ wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
+ eap_gpsk_state(data, FAILURE);
+ return;
+ }
+ pos += miclen;
+
+ if (pos != end) {
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
+ "data in the end of GPSK-4",
+ (unsigned long) (end - pos));
+ }
+
+ eap_gpsk_state(data, SUCCESS);
+}
+
+
+static void eap_gpsk_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_gpsk_data *data = priv;
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len);
+ if (pos == NULL || len < 1)
+ return;
+
+ switch (*pos) {
+ case EAP_GPSK_OPCODE_GPSK_2:
+ eap_gpsk_process_gpsk_2(sm, data, pos + 1, len - 1);
+ break;
+ case EAP_GPSK_OPCODE_GPSK_4:
+ eap_gpsk_process_gpsk_4(sm, data, pos + 1, len - 1);
+ break;
+ }
+}
+
+
+static Boolean eap_gpsk_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_gpsk_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_gpsk_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->msk, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+
+ return key;
+}
+
+
+static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_gpsk_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
+static Boolean eap_gpsk_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_gpsk_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_gpsk_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_gpsk_init;
+ eap->reset = eap_gpsk_reset;
+ eap->buildReq = eap_gpsk_buildReq;
+ eap->check = eap_gpsk_check;
+ eap->process = eap_gpsk_process;
+ eap->isDone = eap_gpsk_isDone;
+ eap->getKey = eap_gpsk_getKey;
+ eap->isSuccess = eap_gpsk_isSuccess;
+ eap->get_emsk = eap_gpsk_get_emsk;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c
new file mode 100644
index 0000000..79b9696
--- /dev/null
+++ b/src/eap_server/eap_server_gtc.c
@@ -0,0 +1,230 @@
+/*
+ * hostapd / EAP-GTC (RFC 3748)
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_gtc_data {
+ enum { CONTINUE, SUCCESS, FAILURE } state;
+ int prefix;
+};
+
+
+static void * eap_gtc_init(struct eap_sm *sm)
+{
+ struct eap_gtc_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = CONTINUE;
+
+#ifdef EAP_SERVER_FAST
+ if (sm->m && sm->m->vendor == EAP_VENDOR_IETF &&
+ sm->m->method == EAP_TYPE_FAST) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix "
+ "with challenge/response");
+ data->prefix = 1;
+ }
+#endif /* EAP_SERVER_FAST */
+
+ return data;
+}
+
+
+static void eap_gtc_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_gtc_data *data = priv;
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_gtc_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+ struct eap_gtc_data *data = priv;
+ struct wpabuf *req;
+ char *msg;
+ size_t msg_len;
+
+ msg = data->prefix ? "CHALLENGE=Password" : "Password";
+
+ msg_len = os_strlen(msg);
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, msg_len,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-GTC: Failed to allocate memory for "
+ "request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ wpabuf_put_data(req, msg, msg_len);
+
+ data->state = CONTINUE;
+
+ return req;
+}
+
+
+static Boolean eap_gtc_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &len);
+ if (pos == NULL || len < 1) {
+ wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_gtc_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_gtc_data *data = priv;
+ const u8 *pos;
+ size_t rlen;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &rlen);
+ if (pos == NULL || rlen < 1)
+ return; /* Should not happen - frame already validated */
+
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen);
+
+#ifdef EAP_SERVER_FAST
+ if (data->prefix) {
+ const u8 *pos2, *end;
+ /* "RESPONSE=<user>\0<password>" */
+ if (rlen < 10) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Too short response "
+ "for EAP-FAST prefix");
+ data->state = FAILURE;
+ return;
+ }
+
+ end = pos + rlen;
+ pos += 9;
+ pos2 = pos;
+ while (pos2 < end && *pos2)
+ pos2++;
+ if (pos2 == end) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: No password in "
+ "response to EAP-FAST prefix");
+ data->state = FAILURE;
+ return;
+ }
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Response user",
+ pos, pos2 - pos);
+ if (sm->identity && sm->require_identity_match &&
+ (pos2 - pos != (int) sm->identity_len ||
+ os_memcmp(pos, sm->identity, sm->identity_len))) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Phase 2 Identity did "
+ "not match with required Identity");
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Expected "
+ "identity",
+ sm->identity, sm->identity_len);
+ data->state = FAILURE;
+ return;
+ } else {
+ os_free(sm->identity);
+ sm->identity_len = pos2 - pos;
+ sm->identity = os_malloc(sm->identity_len);
+ if (sm->identity == NULL) {
+ data->state = FAILURE;
+ return;
+ }
+ os_memcpy(sm->identity, pos, sm->identity_len);
+ }
+
+ if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-GTC: Phase2 "
+ "Identity not found in the user "
+ "database",
+ sm->identity, sm->identity_len);
+ data->state = FAILURE;
+ return;
+ }
+
+ pos = pos2 + 1;
+ rlen = end - pos;
+ wpa_hexdump_ascii_key(MSG_MSGDUMP,
+ "EAP-GTC: Response password",
+ pos, rlen);
+ }
+#endif /* EAP_SERVER_FAST */
+
+ if (sm->user == NULL || sm->user->password == NULL ||
+ sm->user->password_hash) {
+ wpa_printf(MSG_INFO, "EAP-GTC: Plaintext password not "
+ "configured");
+ data->state = FAILURE;
+ return;
+ }
+
+ if (rlen != sm->user->password_len ||
+ os_memcmp(pos, sm->user->password, rlen) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure");
+ data->state = FAILURE;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Success");
+ data->state = SUCCESS;
+ }
+}
+
+
+static Boolean eap_gtc_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_gtc_data *data = priv;
+ return data->state != CONTINUE;
+}
+
+
+static Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_gtc_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_gtc_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_gtc_init;
+ eap->reset = eap_gtc_reset;
+ eap->buildReq = eap_gtc_buildReq;
+ eap->check = eap_gtc_check;
+ eap->process = eap_gtc_process;
+ eap->isDone = eap_gtc_isDone;
+ eap->isSuccess = eap_gtc_isSuccess;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_identity.c b/src/eap_server/eap_server_identity.c
new file mode 100644
index 0000000..cd8da2a
--- /dev/null
+++ b/src/eap_server/eap_server_identity.c
@@ -0,0 +1,180 @@
+/*
+ * hostapd / EAP-Identity
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_identity_data {
+ enum { CONTINUE, SUCCESS, FAILURE } state;
+ int pick_up;
+};
+
+
+static void * eap_identity_init(struct eap_sm *sm)
+{
+ struct eap_identity_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = CONTINUE;
+
+ return data;
+}
+
+
+static void * eap_identity_initPickUp(struct eap_sm *sm)
+{
+ struct eap_identity_data *data;
+ data = eap_identity_init(sm);
+ if (data) {
+ data->pick_up = 1;
+ }
+ return data;
+}
+
+
+static void eap_identity_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_identity_data *data = priv;
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_identity_buildReq(struct eap_sm *sm, void *priv,
+ u8 id)
+{
+ struct eap_identity_data *data = priv;
+ struct wpabuf *req;
+ const char *req_data;
+ size_t req_data_len;
+
+ if (sm->eapol_cb->get_eap_req_id_text) {
+ req_data = sm->eapol_cb->get_eap_req_id_text(sm->eapol_ctx,
+ &req_data_len);
+ } else {
+ req_data = NULL;
+ req_data_len = 0;
+ }
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req_data_len,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate "
+ "memory for request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ wpabuf_put_data(req, req_data, req_data_len);
+
+ return req;
+}
+
+
+static Boolean eap_identity_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
+ respData, &len);
+ if (pos == NULL) {
+ wpa_printf(MSG_INFO, "EAP-Identity: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_identity_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_identity_data *data = priv;
+ const u8 *pos;
+ size_t len;
+
+ if (data->pick_up) {
+ if (eap_identity_check(sm, data, respData)) {
+ wpa_printf(MSG_DEBUG, "EAP-Identity: failed to pick "
+ "up already started negotiation");
+ data->state = FAILURE;
+ return;
+ }
+ data->pick_up = 0;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
+ respData, &len);
+ if (pos == NULL)
+ return; /* Should not happen - frame already validated */
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len);
+ if (sm->identity)
+ sm->update_user = TRUE;
+ os_free(sm->identity);
+ sm->identity = os_malloc(len ? len : 1);
+ if (sm->identity == NULL) {
+ data->state = FAILURE;
+ } else {
+ os_memcpy(sm->identity, pos, len);
+ sm->identity_len = len;
+ data->state = SUCCESS;
+ }
+}
+
+
+static Boolean eap_identity_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_identity_data *data = priv;
+ return data->state != CONTINUE;
+}
+
+
+static Boolean eap_identity_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_identity_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_identity_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
+ "Identity");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_identity_init;
+ eap->initPickUp = eap_identity_initPickUp;
+ eap->reset = eap_identity_reset;
+ eap->buildReq = eap_identity_buildReq;
+ eap->check = eap_identity_check;
+ eap->process = eap_identity_process;
+ eap->isDone = eap_identity_isDone;
+ eap->isSuccess = eap_identity_isSuccess;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c
new file mode 100644
index 0000000..ec4fa87
--- /dev/null
+++ b/src/eap_server/eap_server_ikev2.c
@@ -0,0 +1,539 @@
+/*
+ * EAP-IKEv2 server (RFC 5106)
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/eap_ikev2_common.h"
+#include "ikev2.h"
+
+
+struct eap_ikev2_data {
+ struct ikev2_initiator_data ikev2;
+ enum { MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
+ struct wpabuf *in_buf;
+ struct wpabuf *out_buf;
+ size_t out_used;
+ size_t fragment_size;
+ int keys_ready;
+ u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN];
+ int keymat_ok;
+};
+
+
+static const u8 * eap_ikev2_get_shared_secret(void *ctx, const u8 *IDr,
+ size_t IDr_len,
+ size_t *secret_len)
+{
+ struct eap_sm *sm = ctx;
+
+ if (IDr == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: No IDr received - default "
+ "to user identity from EAP-Identity");
+ IDr = sm->identity;
+ IDr_len = sm->identity_len;
+ }
+
+ if (eap_user_get(sm, IDr, IDr_len, 0) < 0 || sm->user == NULL ||
+ sm->user->password == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: No user entry found");
+ return NULL;
+ }
+
+ *secret_len = sm->user->password_len;
+ return sm->user->password;
+}
+
+
+static const char * eap_ikev2_state_txt(int state)
+{
+ switch (state) {
+ case MSG:
+ return "MSG";
+ case FRAG_ACK:
+ return "FRAG_ACK";
+ case WAIT_FRAG_ACK:
+ return "WAIT_FRAG_ACK";
+ case DONE:
+ return "DONE";
+ case FAIL:
+ return "FAIL";
+ default:
+ return "?";
+ }
+}
+
+
+static void eap_ikev2_state(struct eap_ikev2_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s",
+ eap_ikev2_state_txt(data->state),
+ eap_ikev2_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_ikev2_init(struct eap_sm *sm)
+{
+ struct eap_ikev2_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = MSG;
+ data->fragment_size = 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 = (u8 *) os_strdup("hostapd");
+ data->ikev2.IDi_len = 7;
+
+ data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret;
+ data->ikev2.cb_ctx = sm;
+
+ return data;
+
+failed:
+ ikev2_initiator_deinit(&data->ikev2);
+ os_free(data);
+ return NULL;
+}
+
+
+static void eap_ikev2_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_ikev2_data *data = priv;
+ wpabuf_free(data->in_buf);
+ wpabuf_free(data->out_buf);
+ ikev2_initiator_deinit(&data->ikev2);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, u8 id)
+{
+ struct wpabuf *req;
+ u8 flags;
+ size_t send_len, plen, icv_len = 0;
+
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Request");
+
+ flags = 0;
+ send_len = wpabuf_len(data->out_buf) - data->out_used;
+ if (1 + send_len > data->fragment_size) {
+ send_len = data->fragment_size - 1;
+ flags |= IKEV2_FLAGS_MORE_FRAGMENTS;
+ if (data->out_used == 0) {
+ flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
+ send_len -= 4;
+ }
+ }
+
+ plen = 1 + send_len;
+ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+ plen += 4;
+ if (data->keys_ready) {
+ const struct ikev2_integ_alg *integ;
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum "
+ "Data");
+ flags |= IKEV2_FLAGS_ICV_INCLUDED;
+ integ = ikev2_get_integ(data->ikev2.proposal.integ);
+ if (integ == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
+ "transform / cannot generate ICV");
+ return NULL;
+ }
+ icv_len = integ->hash_len;
+
+ plen += icv_len;
+ }
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL)
+ return NULL;
+
+ wpabuf_put_u8(req, flags); /* Flags */
+ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+ wpabuf_put_be32(req, wpabuf_len(data->out_buf));
+
+ wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
+ send_len);
+ data->out_used += send_len;
+
+ if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+ const u8 *msg = wpabuf_head(req);
+ size_t len = wpabuf_len(req);
+ ikev2_integ_hash(data->ikev2.proposal.integ,
+ data->ikev2.keys.SK_ai,
+ data->ikev2.keys.SK_integ_len,
+ msg, len, wpabuf_put(req, icv_len));
+ }
+
+ if (data->out_used == wpabuf_len(data->out_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+ "(message sent completely)",
+ (unsigned long) send_len);
+ wpabuf_free(data->out_buf);
+ data->out_buf = NULL;
+ data->out_used = 0;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+ "(%lu more to send)", (unsigned long) send_len,
+ (unsigned long) wpabuf_len(data->out_buf) -
+ data->out_used);
+ eap_ikev2_state(data, WAIT_FRAG_ACK);
+ }
+
+ return req;
+}
+
+
+static struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+ struct eap_ikev2_data *data = priv;
+
+ switch (data->state) {
+ case MSG:
+ if (data->out_buf == NULL) {
+ data->out_buf = ikev2_initiator_build(&data->ikev2);
+ if (data->out_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to "
+ "generate IKEv2 message");
+ return NULL;
+ }
+ data->out_used = 0;
+ }
+ /* pass through */
+ case WAIT_FRAG_ACK:
+ return eap_ikev2_build_msg(data, id);
+ case FRAG_ACK:
+ return eap_ikev2_build_frag_ack(id, EAP_CODE_REQUEST);
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected state %d in "
+ "buildReq", data->state);
+ return NULL;
+ }
+}
+
+
+static Boolean eap_ikev2_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
+ &len);
+ if (pos == NULL) {
+ wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
+ const struct wpabuf *respData,
+ u8 flags, const u8 *pos, const u8 **end)
+{
+ if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+ int icv_len = eap_ikev2_validate_icv(
+ data->ikev2.proposal.integ, &data->ikev2.keys, 0,
+ respData, pos, *end);
+ if (icv_len < 0)
+ return -1;
+ /* Hide Integrity Checksum Data from further processing */
+ *end -= icv_len;
+ } else if (data->keys_ready) {
+ wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
+ "included integrity checksum");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_ikev2_process_cont(struct eap_ikev2_data *data,
+ const u8 *buf, size_t len)
+{
+ /* Process continuation of a pending message */
+ if (len > wpabuf_tailroom(data->in_buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
+ eap_ikev2_state(data, FAIL);
+ return -1;
+ }
+
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting for %lu "
+ "bytes more", (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+
+ return 0;
+}
+
+
+static int eap_ikev2_process_fragment(struct eap_ikev2_data *data,
+ u8 flags, u32 message_length,
+ const u8 *buf, size_t len)
+{
+ /* Process a fragment that is not the last one of the message */
+ if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in "
+ "a fragmented packet");
+ return -1;
+ }
+
+ if (data->in_buf == NULL) {
+ /* First fragment of the message */
+ data->in_buf = wpabuf_alloc(message_length);
+ if (data->in_buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
+ "message");
+ return -1;
+ }
+ wpabuf_put_data(data->in_buf, buf, len);
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first "
+ "fragment, waiting for %lu bytes more",
+ (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->in_buf));
+ }
+
+ return 0;
+}
+
+
+static int eap_ikev2_server_keymat(struct eap_ikev2_data *data)
+{
+ if (eap_ikev2_derive_keymat(
+ data->ikev2.proposal.prf, &data->ikev2.keys,
+ data->ikev2.i_nonce, data->ikev2.i_nonce_len,
+ data->ikev2.r_nonce, data->ikev2.r_nonce_len,
+ data->keymat) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to derive "
+ "key material");
+ return -1;
+ }
+ data->keymat_ok = 1;
+ return 0;
+}
+
+
+static void eap_ikev2_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_ikev2_data *data = priv;
+ const u8 *start, *pos, *end;
+ size_t len;
+ u8 flags;
+ u32 message_length = 0;
+ struct wpabuf tmpbuf;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
+ &len);
+ if (pos == NULL)
+ return; /* Should not happen; message already verified */
+
+ start = pos;
+ end = start + len;
+
+ if (len == 0) {
+ /* fragment ack */
+ flags = 0;
+ } else
+ flags = *pos++;
+
+ if (eap_ikev2_process_icv(data, respData, flags, pos, &end) < 0) {
+ eap_ikev2_state(data, FAIL);
+ return;
+ }
+
+ if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
+ eap_ikev2_state(data, FAIL);
+ return;
+ }
+ message_length = WPA_GET_BE32(pos);
+ pos += 4;
+
+ if (message_length < (u32) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message "
+ "Length (%d; %ld remaining in this msg)",
+ message_length, (long) (end - pos));
+ eap_ikev2_state(data, FAIL);
+ return;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
+ "Message Length %u", flags, message_length);
+
+ if (data->state == WAIT_FRAG_ACK) {
+ if (len != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
+ "in WAIT_FRAG_ACK state");
+ eap_ikev2_state(data, FAIL);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
+ eap_ikev2_state(data, MSG);
+ return;
+ }
+
+ if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
+ eap_ikev2_state(data, FAIL);
+ return;
+ }
+
+ if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
+ if (eap_ikev2_process_fragment(data, flags, message_length,
+ pos, end - pos) < 0)
+ eap_ikev2_state(data, FAIL);
+ else
+ eap_ikev2_state(data, FRAG_ACK);
+ return;
+ } else if (data->state == FRAG_ACK) {
+ wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
+ data->state = MSG;
+ }
+
+ if (data->in_buf == NULL) {
+ /* Wrap unfragmented messages as wpabuf without extra copy */
+ wpabuf_set(&tmpbuf, pos, end - pos);
+ data->in_buf = &tmpbuf;
+ }
+
+ if (ikev2_initiator_process(&data->ikev2, data->in_buf) < 0) {
+ if (data->in_buf == &tmpbuf)
+ data->in_buf = NULL;
+ eap_ikev2_state(data, FAIL);
+ return;
+ }
+
+ switch (data->ikev2.state) {
+ case SA_AUTH:
+ /* SA_INIT was sent out, so message have to be
+ * integrity protected from now on. */
+ data->keys_ready = 1;
+ break;
+ case IKEV2_DONE:
+ if (data->state == FAIL)
+ break;
+ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication completed "
+ "successfully");
+ if (eap_ikev2_server_keymat(data))
+ break;
+ eap_ikev2_state(data, DONE);
+ break;
+ default:
+ break;
+ }
+
+ if (data->in_buf != &tmpbuf)
+ wpabuf_free(data->in_buf);
+ data->in_buf = NULL;
+}
+
+
+static Boolean eap_ikev2_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_ikev2_data *data = priv;
+ return data->state == DONE || data->state == FAIL;
+}
+
+
+static Boolean eap_ikev2_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_ikev2_data *data = priv;
+ return data->state == DONE && data->ikev2.state == IKEV2_DONE &&
+ data->keymat_ok;
+}
+
+
+static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ikev2_data *data = priv;
+ u8 *key;
+
+ if (data->state != DONE || !data->keymat_ok)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key) {
+ os_memcpy(key, data->keymat, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+ }
+
+ return key;
+}
+
+
+static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ikev2_data *data = priv;
+ u8 *key;
+
+ if (data->state != DONE || !data->keymat_ok)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key) {
+ os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+ }
+
+ return key;
+}
+
+
+int eap_server_ikev2_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
+ "IKEV2");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_ikev2_init;
+ eap->reset = eap_ikev2_reset;
+ eap->buildReq = eap_ikev2_buildReq;
+ eap->check = eap_ikev2_check;
+ eap->process = eap_ikev2_process;
+ eap->isDone = eap_ikev2_isDone;
+ eap->getKey = eap_ikev2_getKey;
+ eap->isSuccess = eap_ikev2_isSuccess;
+ eap->get_emsk = eap_ikev2_get_emsk;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_md5.c b/src/eap_server/eap_server_md5.c
new file mode 100644
index 0000000..d03ec53
--- /dev/null
+++ b/src/eap_server/eap_server_md5.c
@@ -0,0 +1,177 @@
+/*
+ * hostapd / EAP-MD5 server
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "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);
+ chap_md5(id, sm->user->password, sm->user->password_len,
+ data->challenge, CHALLENGE_LEN, hash);
+
+ if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success");
+ data->state = SUCCESS;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Failure");
+ data->state = FAILURE;
+ }
+}
+
+
+static Boolean eap_md5_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_md5_data *data = priv;
+ return data->state != CONTINUE;
+}
+
+
+static Boolean eap_md5_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_md5_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_md5_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_md5_init;
+ eap->reset = eap_md5_reset;
+ eap->buildReq = eap_md5_buildReq;
+ eap->check = eap_md5_check;
+ eap->process = eap_md5_process;
+ eap->isDone = eap_md5_isDone;
+ eap->isSuccess = eap_md5_isSuccess;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_methods.c b/src/eap_server/eap_server_methods.c
new file mode 100644
index 0000000..900a5dd
--- /dev/null
+++ b/src/eap_server/eap_server_methods.c
@@ -0,0 +1,175 @@
+/*
+ * EAP server method registration
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_methods.h"
+
+
+static struct eap_method *eap_methods;
+
+
+/**
+ * eap_server_get_eap_method - Get EAP method based on type number
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @method: EAP type number
+ * Returns: Pointer to EAP method or %NULL if not found
+ */
+const struct eap_method * eap_server_get_eap_method(int vendor, EapType method)
+{
+ struct eap_method *m;
+ for (m = eap_methods; m; m = m->next) {
+ if (m->vendor == vendor && m->method == method)
+ return m;
+ }
+ return NULL;
+}
+
+
+/**
+ * eap_server_get_type - Get EAP type for the given EAP method name
+ * @name: EAP method name, e.g., TLS
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers based on the list of
+ * EAP methods included in the build.
+ */
+EapType eap_server_get_type(const char *name, int *vendor)
+{
+ struct eap_method *m;
+ for (m = eap_methods; m; m = m->next) {
+ if (os_strcmp(m->name, name) == 0) {
+ *vendor = m->vendor;
+ return m->method;
+ }
+ }
+ *vendor = EAP_VENDOR_IETF;
+ return EAP_TYPE_NONE;
+}
+
+
+/**
+ * eap_server_method_alloc - Allocate EAP server method structure
+ * @version: Version of the EAP server method interface (set to
+ * EAP_SERVER_METHOD_INTERFACE_VERSION)
+ * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+ * @method: EAP type number (EAP_TYPE_*)
+ * @name: Name of the method (e.g., "TLS")
+ * Returns: Allocated EAP method structure or %NULL on failure
+ *
+ * The returned structure should be freed with eap_server_method_free() when it
+ * is not needed anymore.
+ */
+struct eap_method * eap_server_method_alloc(int version, int vendor,
+ EapType method, const char *name)
+{
+ struct eap_method *eap;
+ eap = os_zalloc(sizeof(*eap));
+ if (eap == NULL)
+ return NULL;
+ eap->version = version;
+ eap->vendor = vendor;
+ eap->method = method;
+ eap->name = name;
+ return eap;
+}
+
+
+/**
+ * eap_server_method_free - Free EAP server method structure
+ * @method: Method structure allocated with eap_server_method_alloc()
+ */
+void eap_server_method_free(struct eap_method *method)
+{
+ os_free(method);
+}
+
+
+/**
+ * eap_server_method_register - Register an EAP server method
+ * @method: EAP method to register
+ * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method
+ * has already been registered
+ *
+ * Each EAP server method needs to call this function to register itself as a
+ * supported EAP method.
+ */
+int eap_server_method_register(struct eap_method *method)
+{
+ struct eap_method *m, *last = NULL;
+
+ if (method == NULL || method->name == NULL ||
+ method->version != EAP_SERVER_METHOD_INTERFACE_VERSION)
+ return -1;
+
+ for (m = eap_methods; m; m = m->next) {
+ if ((m->vendor == method->vendor &&
+ m->method == method->method) ||
+ os_strcmp(m->name, method->name) == 0)
+ return -2;
+ last = m;
+ }
+
+ if (last)
+ last->next = method;
+ else
+ eap_methods = method;
+
+ return 0;
+}
+
+
+/**
+ * eap_server_unregister_methods - Unregister EAP server methods
+ *
+ * This function is called at program termination to unregister all EAP server
+ * methods.
+ */
+void eap_server_unregister_methods(void)
+{
+ struct eap_method *m;
+
+ while (eap_methods) {
+ m = eap_methods;
+ eap_methods = eap_methods->next;
+
+ if (m->free)
+ m->free(m);
+ else
+ eap_server_method_free(m);
+ }
+}
+
+
+/**
+ * eap_server_get_name - Get EAP method name for the given EAP type
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @type: EAP method type
+ * Returns: EAP method name, e.g., TLS, or %NULL if not found
+ *
+ * This function maps EAP type numbers into EAP type names based on the list of
+ * EAP methods included in the build.
+ */
+const char * eap_server_get_name(int vendor, EapType type)
+{
+ struct eap_method *m;
+ for (m = eap_methods; m; m = m->next) {
+ if (m->vendor == vendor && m->method == type)
+ return m->name;
+ }
+ return NULL;
+}
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
new file mode 100644
index 0000000..64120a4
--- /dev/null
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -0,0 +1,575 @@
+/*
+ * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/ms_funcs.h"
+#include "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;
+ char *name = "hostapd"; /* TODO: make this configurable */
+ 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 + os_strlen(name);
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
+ " for request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ ms = wpabuf_put(req, sizeof(*ms));
+ ms->op_code = MSCHAPV2_OP_CHALLENGE;
+ ms->mschapv2_id = id;
+ WPA_PUT_BE16(ms->ms_length, ms_len);
+
+ wpabuf_put_u8(req, CHALLENGE_LEN);
+ if (!data->auth_challenge_from_tls)
+ wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
+ else
+ wpabuf_put(req, CHALLENGE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
+ data->auth_challenge, CHALLENGE_LEN);
+ wpabuf_put_data(req, name, os_strlen(name));
+
+ return req;
+}
+
+
+static struct wpabuf * eap_mschapv2_build_success_req(
+ struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
+{
+ struct wpabuf *req;
+ struct eap_mschapv2_hdr *ms;
+ u8 *msg;
+ char *message = "OK";
+ size_t ms_len;
+
+ ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
+ os_strlen(message);
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
+ " for request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ ms = wpabuf_put(req, sizeof(*ms));
+ ms->op_code = MSCHAPV2_OP_SUCCESS;
+ ms->mschapv2_id = data->resp_mschapv2_id;
+ WPA_PUT_BE16(ms->ms_length, ms_len);
+ msg = (u8 *) (ms + 1);
+
+ wpabuf_put_u8(req, 'S');
+ wpabuf_put_u8(req, '=');
+ wpa_snprintf_hex_uppercase(
+ wpabuf_put(req, sizeof(data->auth_response) * 2),
+ sizeof(data->auth_response) * 2 + 1,
+ data->auth_response, sizeof(data->auth_response));
+ wpabuf_put_u8(req, ' ');
+ wpabuf_put_u8(req, 'M');
+ wpabuf_put_u8(req, '=');
+ wpabuf_put_data(req, message, os_strlen(message));
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
+ msg, ms_len - sizeof(*ms));
+
+ return req;
+}
+
+
+static struct wpabuf * eap_mschapv2_build_failure_req(
+ struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
+{
+ struct wpabuf *req;
+ struct eap_mschapv2_hdr *ms;
+ char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
+ "M=FAILED";
+ size_t ms_len;
+
+ ms_len = sizeof(*ms) + os_strlen(message);
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
+ " for request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ ms = wpabuf_put(req, sizeof(*ms));
+ ms->op_code = MSCHAPV2_OP_FAILURE;
+ ms->mschapv2_id = data->resp_mschapv2_id;
+ WPA_PUT_BE16(ms->ms_length, ms_len);
+
+ wpabuf_put_data(req, message, os_strlen(message));
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
+ (u8 *) message, os_strlen(message));
+
+ return req;
+}
+
+
+static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
+ u8 id)
+{
+ struct eap_mschapv2_data *data = priv;
+
+ switch (data->state) {
+ case CHALLENGE:
+ return eap_mschapv2_build_challenge(sm, data, id);
+ case SUCCESS_REQ:
+ return eap_mschapv2_build_success_req(sm, data, id);
+ case FAILURE_REQ:
+ return eap_mschapv2_build_failure_req(sm, data, id);
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
+ "buildReq", data->state);
+ break;
+ }
+ return NULL;
+}
+
+
+static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_mschapv2_data *data = priv;
+ struct eap_mschapv2_hdr *resp;
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
+ &len);
+ if (pos == NULL || len < 1) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
+ return TRUE;
+ }
+
+ resp = (struct eap_mschapv2_hdr *) pos;
+ if (data->state == CHALLENGE &&
+ resp->op_code != MSCHAPV2_OP_RESPONSE) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
+ "ignore op %d", resp->op_code);
+ return TRUE;
+ }
+
+ if (data->state == SUCCESS_REQ &&
+ resp->op_code != MSCHAPV2_OP_SUCCESS &&
+ resp->op_code != MSCHAPV2_OP_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
+ "Failure - ignore op %d", resp->op_code);
+ return TRUE;
+ }
+
+ if (data->state == FAILURE_REQ &&
+ resp->op_code != MSCHAPV2_OP_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
+ "- ignore op %d", resp->op_code);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_mschapv2_process_response(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ struct wpabuf *respData)
+{
+ struct eap_mschapv2_hdr *resp;
+ const u8 *pos, *end, *peer_challenge, *nt_response, *name;
+ u8 flags;
+ size_t len, name_len, i;
+ u8 expected[24];
+ const u8 *username, *user;
+ size_t username_len, user_len;
+ int res;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
+ &len);
+ if (pos == NULL || len < 1)
+ return; /* Should not happen - frame already validated */
+
+ end = pos + len;
+ resp = (struct eap_mschapv2_hdr *) pos;
+ pos = (u8 *) (resp + 1);
+
+ if (len < sizeof(*resp) + 1 + 49 ||
+ resp->op_code != MSCHAPV2_OP_RESPONSE ||
+ pos[0] != 49) {
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
+ respData);
+ data->state = FAILURE;
+ return;
+ }
+ data->resp_mschapv2_id = resp->mschapv2_id;
+ pos++;
+ peer_challenge = pos;
+ pos += 16 + 8;
+ nt_response = pos;
+ pos += 24;
+ flags = *pos++;
+ name = pos;
+ name_len = end - name;
+
+ if (data->peer_challenge) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
+ "Peer-Challenge");
+ peer_challenge = data->peer_challenge;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
+ peer_challenge, 16);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
+ wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
+
+ /* MSCHAPv2 does not include optional domain name in the
+ * challenge-response calculation, so remove domain prefix
+ * (if present). */
+ username = sm->identity;
+ username_len = sm->identity_len;
+ for (i = 0; i < username_len; i++) {
+ if (username[i] == '\\') {
+ username_len -= i + 1;
+ username += i + 1;
+ break;
+ }
+ }
+
+ user = name;
+ user_len = name_len;
+ for (i = 0; i < user_len; i++) {
+ if (user[i] == '\\') {
+ user_len -= i + 1;
+ user += i + 1;
+ break;
+ }
+ }
+
+ if (username_len != user_len ||
+ os_memcmp(username, user, username_len) != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
+ "name", username, username_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
+ "name", user, user_len);
+ data->state = FAILURE;
+ return;
+ }
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
+ username, username_len);
+
+ if (sm->user->password_hash) {
+ res = generate_nt_response_pwhash(data->auth_challenge,
+ peer_challenge,
+ username, username_len,
+ sm->user->password,
+ expected);
+ } else {
+ res = generate_nt_response(data->auth_challenge,
+ peer_challenge,
+ username, username_len,
+ sm->user->password,
+ sm->user->password_len,
+ expected);
+ }
+ if (res) {
+ data->state = FAILURE;
+ return;
+ }
+
+ if (os_memcmp(nt_response, expected, 24) == 0) {
+ const u8 *pw_hash;
+ u8 pw_hash_buf[16], pw_hash_hash[16];
+
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
+ data->state = SUCCESS_REQ;
+
+ /* Authenticator response is not really needed yet, but
+ * calculate it here so that peer_challenge and username need
+ * not be saved. */
+ if (sm->user->password_hash) {
+ pw_hash = sm->user->password;
+ } else {
+ nt_password_hash(sm->user->password,
+ sm->user->password_len,
+ pw_hash_buf);
+ pw_hash = pw_hash_buf;
+ }
+ generate_authenticator_response_pwhash(
+ pw_hash, peer_challenge, data->auth_challenge,
+ username, username_len, nt_response,
+ data->auth_response);
+
+ hash_nt_password_hash(pw_hash, pw_hash_hash);
+ get_master_key(pw_hash_hash, nt_response, data->master_key);
+ data->master_key_valid = 1;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
+ data->master_key, MSCHAPV2_KEY_LEN);
+ } else {
+ wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
+ expected, 24);
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
+ data->state = FAILURE_REQ;
+ }
+}
+
+
+static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ struct wpabuf *respData)
+{
+ struct eap_mschapv2_hdr *resp;
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
+ &len);
+ if (pos == NULL || len < 1)
+ return; /* Should not happen - frame already validated */
+
+ resp = (struct eap_mschapv2_hdr *) pos;
+
+ if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
+ " - authentication completed successfully");
+ data->state = SUCCESS;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
+ "Response - peer rejected authentication");
+ data->state = FAILURE;
+ }
+}
+
+
+static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
+ struct eap_mschapv2_data *data,
+ struct wpabuf *respData)
+{
+ struct eap_mschapv2_hdr *resp;
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
+ &len);
+ if (pos == NULL || len < 1)
+ return; /* Should not happen - frame already validated */
+
+ resp = (struct eap_mschapv2_hdr *) pos;
+
+ if (resp->op_code == MSCHAPV2_OP_FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
+ " - authentication failed");
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
+ "Response - authentication failed");
+ }
+
+ data->state = FAILURE;
+}
+
+
+static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_mschapv2_data *data = priv;
+
+ if (sm->user == NULL || sm->user->password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
+ data->state = FAILURE;
+ return;
+ }
+
+ switch (data->state) {
+ case CHALLENGE:
+ eap_mschapv2_process_response(sm, data, respData);
+ break;
+ case SUCCESS_REQ:
+ eap_mschapv2_process_success_resp(sm, data, respData);
+ break;
+ case FAILURE_REQ:
+ eap_mschapv2_process_failure_resp(sm, data, respData);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
+ "process", data->state);
+ break;
+ }
+}
+
+
+static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_mschapv2_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_mschapv2_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS || !data->master_key_valid)
+ return NULL;
+
+ *len = 2 * MSCHAPV2_KEY_LEN;
+ key = os_malloc(*len);
+ if (key == NULL)
+ return NULL;
+ /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
+ get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
+ get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
+ MSCHAPV2_KEY_LEN, 1, 1);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
+
+ return key;
+}
+
+
+static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_mschapv2_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_mschapv2_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
+ "MSCHAPV2");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_mschapv2_init;
+ eap->reset = eap_mschapv2_reset;
+ eap->buildReq = eap_mschapv2_buildReq;
+ eap->check = eap_mschapv2_check;
+ eap->process = eap_mschapv2_process;
+ eap->isDone = eap_mschapv2_isDone;
+ eap->getKey = eap_mschapv2_getKey;
+ eap->isSuccess = eap_mschapv2_isSuccess;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c
new file mode 100644
index 0000000..4d64269
--- /dev/null
+++ b/src/eap_server/eap_server_pax.c
@@ -0,0 +1,570 @@
+/*
+ * hostapd / EAP-PAX (RFC 4746) server
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "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/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
new file mode 100644
index 0000000..8a7d626
--- /dev/null
+++ b/src/eap_server/eap_server_peap.c
@@ -0,0 +1,1387 @@
+/*
+ * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "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);
+ peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck));
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
+ imck, sizeof(imck));
+
+ os_free(tk);
+
+ /* TODO: fast-connect: IPMK|CMK = TK */
+ os_memcpy(data->ipmk, imck, 40);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
+ os_memcpy(data->cmk, imck + 40, 20);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
+ struct eap_peap_data *data,
+ u8 id)
+{
+ struct wpabuf *buf, *encr_req;
+ size_t mlen;
+
+ mlen = 6; /* Result TLV */
+ if (data->crypto_binding != NO_BINDING)
+ mlen += 60; /* Cryptobinding TLV */
+#ifdef EAP_SERVER_TNC
+ if (data->soh_response)
+ mlen += wpabuf_len(data->soh_response);
+#endif /* EAP_SERVER_TNC */
+
+ buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen,
+ EAP_CODE_REQUEST, id);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_u8(buf, 0x80); /* Mandatory */
+ wpabuf_put_u8(buf, EAP_TLV_RESULT_TLV);
+ /* Length */
+ wpabuf_put_be16(buf, 2);
+ /* Status */
+ wpabuf_put_be16(buf, data->tlv_request == TLV_REQ_SUCCESS ?
+ EAP_TLV_RESULT_SUCCESS : EAP_TLV_RESULT_FAILURE);
+
+ if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS &&
+ data->crypto_binding != NO_BINDING) {
+ u8 *mac;
+ u8 eap_type = EAP_TYPE_PEAP;
+ const u8 *addr[2];
+ size_t len[2];
+ u16 tlv_type;
+
+#ifdef EAP_SERVER_TNC
+ if (data->soh_response) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH "
+ "Response TLV");
+ wpabuf_put_buf(buf, data->soh_response);
+ wpabuf_free(data->soh_response);
+ data->soh_response = NULL;
+ }
+#endif /* EAP_SERVER_TNC */
+
+ if (eap_peap_derive_cmk(sm, data) < 0 ||
+ 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);
+
+ hdr = wpabuf_head(in_decrypted);
+
+ if (data->peap_version == 0 && data->state != PHASE2_TLV) {
+ const struct eap_hdr *resp;
+ struct eap_hdr *nhdr;
+ struct wpabuf *nbuf =
+ wpabuf_alloc(sizeof(struct eap_hdr) +
+ wpabuf_len(in_decrypted));
+ if (nbuf == NULL) {
+ wpabuf_free(in_decrypted);
+ return;
+ }
+
+ resp = wpabuf_head(respData);
+ nhdr = wpabuf_put(nbuf, sizeof(*nhdr));
+ nhdr->code = resp->code;
+ nhdr->identifier = resp->identifier;
+ nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
+ wpabuf_len(in_decrypted));
+ wpabuf_put_buf(nbuf, in_decrypted);
+ wpabuf_free(in_decrypted);
+
+ in_decrypted = nbuf;
+ } else if (data->peap_version >= 2) {
+ struct eap_tlv_hdr *tlv;
+ struct wpabuf *nmsg;
+
+ if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
+ "EAP TLV");
+ wpabuf_free(in_decrypted);
+ return;
+ }
+ tlv = wpabuf_mhead(in_decrypted);
+ if ((be_to_host16(tlv->tlv_type) & EAP_TLV_TYPE_MASK) !=
+ EAP_TLV_EAP_PAYLOAD_TLV) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
+ wpabuf_free(in_decrypted);
+ return;
+ }
+ if (sizeof(*tlv) + be_to_host16(tlv->length) >
+ wpabuf_len(in_decrypted)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
+ "length");
+ wpabuf_free(in_decrypted);
+ return;
+ }
+ hdr = (struct eap_hdr *) (tlv + 1);
+ if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
+ "EAP packet in EAP TLV");
+ wpabuf_free(in_decrypted);
+ return;
+ }
+
+ nmsg = wpabuf_alloc(be_to_host16(hdr->length));
+ if (nmsg == NULL) {
+ wpabuf_free(in_decrypted);
+ return;
+ }
+
+ wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
+ wpabuf_free(in_decrypted);
+ in_decrypted = nmsg;
+ }
+
+ hdr = wpabuf_head(in_decrypted);
+ if (wpabuf_len(in_decrypted) < (int) sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
+ "EAP frame (len=%lu)",
+ (unsigned long) wpabuf_len(in_decrypted));
+ wpabuf_free(in_decrypted);
+ eap_peap_req_failure(sm, data);
+ return;
+ }
+ len = be_to_host16(hdr->length);
+ if (len > wpabuf_len(in_decrypted)) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
+ "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+ (unsigned long) wpabuf_len(in_decrypted),
+ (unsigned long) len);
+ wpabuf_free(in_decrypted);
+ eap_peap_req_failure(sm, data);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
+ "identifier=%d length=%lu", hdr->code, hdr->identifier,
+ (unsigned long) len);
+ switch (hdr->code) {
+ case EAP_CODE_RESPONSE:
+ eap_peap_process_phase2_response(sm, data, in_decrypted);
+ break;
+ case EAP_CODE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
+ if (data->state == SUCCESS_REQ) {
+ eap_peap_state(data, SUCCESS);
+ }
+ break;
+ case EAP_CODE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
+ eap_peap_state(data, FAILURE);
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ break;
+ }
+
+ wpabuf_free(in_decrypted);
+}
+
+
+static int eap_peapv2_start_phase2(struct eap_sm *sm,
+ struct eap_peap_data *data)
+{
+ struct wpabuf *buf, *buf2;
+
+ wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Phase1 done, include first Phase2 "
+ "payload in the same message");
+ eap_peap_state(data, PHASE1_ID2);
+ if (eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY))
+ return -1;
+
+ /* TODO: which Id to use here? */
+ buf = data->phase2_method->buildReq(sm, data->phase2_priv, 6);
+ if (buf == NULL)
+ return -1;
+
+ buf2 = eap_peapv2_tlv_eap_payload(buf);
+ if (buf2 == NULL)
+ return -1;
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Identity Request", buf2);
+
+ buf = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
+ buf2);
+ wpabuf_free(buf2);
+
+ if (buf == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PEAPv2: Failed to encrypt Phase 2 "
+ "data");
+ return -1;
+ }
+
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Encrypted Identity Request",
+ buf);
+
+ /* Append TLS data into the pending buffer after the Server Finished */
+ if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(buf)) < 0) {
+ wpabuf_free(buf);
+ return -1;
+ }
+ wpabuf_put_buf(data->ssl.tls_out, buf);
+ wpabuf_free(buf);
+
+ return 0;
+}
+
+
+static int eap_peap_process_version(struct eap_sm *sm, void *priv,
+ int peer_version)
+{
+ struct eap_peap_data *data = priv;
+
+ data->recv_version = peer_version;
+ if (data->force_version >= 0 && peer_version != data->force_version) {
+ wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced"
+ " version (forced=%d peer=%d) - reject",
+ data->force_version, peer_version);
+ return -1;
+ }
+ if (peer_version < data->peap_version) {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: peer ver=%d, own ver=%d; "
+ "use version %d",
+ peer_version, data->peap_version, peer_version);
+ data->peap_version = peer_version;
+ }
+
+ return 0;
+}
+
+
+static void eap_peap_process_msg(struct eap_sm *sm, void *priv,
+ const struct wpabuf *respData)
+{
+ struct eap_peap_data *data = priv;
+
+ switch (data->state) {
+ case PHASE1:
+ if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
+ eap_peap_state(data, FAILURE);
+ break;
+ }
+
+ if (data->peap_version >= 2 &&
+ tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ if (eap_peapv2_start_phase2(sm, data)) {
+ eap_peap_state(data, FAILURE);
+ break;
+ }
+ }
+ break;
+ case PHASE2_START:
+ eap_peap_state(data, PHASE2_ID);
+ eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY);
+ break;
+ case PHASE1_ID2:
+ case PHASE2_ID:
+ case PHASE2_METHOD:
+ case PHASE2_SOH:
+ case PHASE2_TLV:
+ eap_peap_process_phase2(sm, data, respData, data->ssl.tls_in);
+ break;
+ case SUCCESS_REQ:
+ eap_peap_state(data, SUCCESS);
+ break;
+ case FAILURE_REQ:
+ eap_peap_state(data, FAILURE);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected state %d in %s",
+ data->state, __func__);
+ break;
+ }
+}
+
+
+static void eap_peap_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_peap_data *data = priv;
+ if (eap_server_tls_process(sm, &data->ssl, respData, data,
+ EAP_TYPE_PEAP, eap_peap_process_version,
+ eap_peap_process_msg) < 0)
+ eap_peap_state(data, FAILURE);
+}
+
+
+static Boolean eap_peap_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_peap_data *data = priv;
+ u8 *eapKeyData;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ if (data->crypto_binding_used) {
+ u8 csk[128];
+ /*
+ * Note: It looks like Microsoft implementation requires null
+ * termination for this label while the one used for deriving
+ * IPMK|CMK did not use null termination.
+ */
+ peap_prfplus(data->peap_version, data->ipmk, 40,
+ "Session Key Generating Function",
+ (u8 *) "\00", 1, csk, sizeof(csk));
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
+ eapKeyData = os_malloc(EAP_TLS_KEY_LEN);
+ if (eapKeyData) {
+ os_memcpy(eapKeyData, csk, EAP_TLS_KEY_LEN);
+ *len = EAP_TLS_KEY_LEN;
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
+ eapKeyData, EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive "
+ "key");
+ }
+
+ return eapKeyData;
+ }
+
+ /* TODO: PEAPv1 - different label in some cases */
+ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+ "client EAP encryption",
+ EAP_TLS_KEY_LEN);
+ if (eapKeyData) {
+ *len = EAP_TLS_KEY_LEN;
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
+ eapKeyData, EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive key");
+ }
+
+ return eapKeyData;
+}
+
+
+static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_peap_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_peap_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_peap_init;
+ eap->reset = eap_peap_reset;
+ eap->buildReq = eap_peap_buildReq;
+ eap->check = eap_peap_check;
+ eap->process = eap_peap_process;
+ eap->isDone = eap_peap_isDone;
+ eap->getKey = eap_peap_getKey;
+ eap->isSuccess = eap_peap_isSuccess;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c
new file mode 100644
index 0000000..efc7a82
--- /dev/null
+++ b/src/eap_server/eap_server_psk.c
@@ -0,0 +1,518 @@
+/*
+ * hostapd / EAP-PSK (RFC 4764) server
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * Note: EAP-PSK is an EAP authentication method and as such, completely
+ * different from WPA-PSK. This file is not needed for WPA-PSK functionality.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
+#include "eap_common/eap_psk_common.h"
+#include "eap_server/eap_i.h"
+
+
+struct eap_psk_data {
+ enum { PSK_1, PSK_3, SUCCESS, FAILURE } state;
+ u8 rand_s[EAP_PSK_RAND_LEN];
+ u8 rand_p[EAP_PSK_RAND_LEN];
+ u8 *id_p, *id_s;
+ size_t id_p_len, id_s_len;
+ u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN];
+ u8 msk[EAP_MSK_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+};
+
+
+static void * eap_psk_init(struct eap_sm *sm)
+{
+ struct eap_psk_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = PSK_1;
+ data->id_s = (u8 *) "hostapd";
+ data->id_s_len = 7;
+
+ return data;
+}
+
+
+static void eap_psk_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_psk_data *data = priv;
+ os_free(data->id_p);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_psk_build_1(struct eap_sm *sm,
+ struct eap_psk_data *data, u8 id)
+{
+ struct wpabuf *req;
+ struct eap_psk_hdr_1 *psk;
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)");
+
+ if (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) + data->id_s_len,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory "
+ "request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ psk = wpabuf_put(req, sizeof(*psk));
+ psk->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */
+ os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN);
+ wpabuf_put_data(req, data->id_s, data->id_s_len);
+
+ return req;
+}
+
+
+static struct wpabuf * eap_psk_build_3(struct eap_sm *sm,
+ struct eap_psk_data *data, u8 id)
+{
+ struct wpabuf *req;
+ struct eap_psk_hdr_3 *psk;
+ u8 *buf, *pchannel, nonce[16];
+ size_t buflen;
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-3 (sending)");
+
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+ sizeof(*psk) + 4 + 16 + 1, EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory "
+ "request");
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ psk = wpabuf_put(req, sizeof(*psk));
+ psk->flags = EAP_PSK_FLAGS_SET_T(2); /* T=2 */
+ os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN);
+
+ /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */
+ buflen = data->id_s_len + EAP_PSK_RAND_LEN;
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ goto fail;
+
+ os_memcpy(buf, data->id_s, data->id_s_len);
+ os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
+ if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s))
+ goto fail;
+ os_free(buf);
+
+ if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk,
+ data->emsk))
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN);
+
+ os_memset(nonce, 0, sizeof(nonce));
+ pchannel = wpabuf_put(req, 4 + 16 + 1);
+ os_memcpy(pchannel, nonce + 12, 4);
+ os_memset(pchannel + 4, 0, 16); /* Tag */
+ pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6;
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (plaintext)",
+ pchannel, 4 + 16 + 1);
+ if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce),
+ wpabuf_head(req), 22,
+ pchannel + 4 + 16, 1, pchannel + 4))
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (encrypted)",
+ pchannel, 4 + 16 + 1);
+
+ return req;
+
+fail:
+ wpabuf_free(req);
+ data->state = FAILURE;
+ return NULL;
+}
+
+
+static struct wpabuf * eap_psk_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+ struct eap_psk_data *data = priv;
+
+ switch (data->state) {
+ case PSK_1:
+ return eap_psk_build_1(sm, data, id);
+ case PSK_3:
+ return eap_psk_build_3(sm, data, id);
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Unknown state %d in buildReq",
+ data->state);
+ break;
+ }
+ return NULL;
+}
+
+
+static Boolean eap_psk_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_psk_data *data = priv;
+ size_t len;
+ u8 t;
+ const u8 *pos;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len);
+ if (pos == NULL || len < 1) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
+ return TRUE;
+ }
+ t = EAP_PSK_FLAGS_GET_T(*pos);
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: received frame: T=%d", t);
+
+ if (data->state == PSK_1 && t != 1) {
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-2 - "
+ "ignore T=%d", t);
+ return TRUE;
+ }
+
+ if (data->state == PSK_3 && t != 3) {
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-4 - "
+ "ignore T=%d", t);
+ return TRUE;
+ }
+
+ if ((t == 1 && len < sizeof(struct eap_psk_hdr_2)) ||
+ (t == 3 && len < sizeof(struct eap_psk_hdr_4))) {
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Too short frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_psk_process_2(struct eap_sm *sm,
+ struct eap_psk_data *data,
+ struct wpabuf *respData)
+{
+ const struct eap_psk_hdr_2 *resp;
+ u8 *pos, mac[EAP_PSK_MAC_LEN], *buf;
+ size_t left, buflen;
+ int i;
+ const u8 *cpos;
+
+ if (data->state != PSK_1)
+ return;
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-2");
+
+ cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData,
+ &left);
+ if (cpos == NULL || left < sizeof(*resp)) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
+ return;
+ }
+ resp = (const struct eap_psk_hdr_2 *) cpos;
+ cpos = (const u8 *) (resp + 1);
+ left -= sizeof(*resp);
+
+ os_free(data->id_p);
+ data->id_p = os_malloc(left);
+ if (data->id_p == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for "
+ "ID_P");
+ return;
+ }
+ os_memcpy(data->id_p, cpos, left);
+ data->id_p_len = left;
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P",
+ data->id_p, data->id_p_len);
+
+ if (eap_user_get(sm, data->id_p, data->id_p_len, 0) < 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: unknown ID_P",
+ data->id_p, data->id_p_len);
+ data->state = FAILURE;
+ return;
+ }
+
+ for (i = 0;
+ i < EAP_MAX_METHODS &&
+ (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+ sm->user->methods[i].method != EAP_TYPE_NONE);
+ i++) {
+ if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
+ sm->user->methods[i].method == EAP_TYPE_PSK)
+ break;
+ }
+
+ if (i >= EAP_MAX_METHODS ||
+ sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+ sm->user->methods[i].method != EAP_TYPE_PSK) {
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-PSK: EAP-PSK not enabled for ID_P",
+ data->id_p, data->id_p_len);
+ data->state = FAILURE;
+ return;
+ }
+
+ if (sm->user->password == NULL ||
+ sm->user->password_len != EAP_PSK_PSK_LEN) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: invalid password in "
+ "user database for ID_P",
+ data->id_p, data->id_p_len);
+ data->state = FAILURE;
+ return;
+ }
+ if (eap_psk_key_setup(sm->user->password, data->ak, data->kdk)) {
+ data->state = FAILURE;
+ return;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN);
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_P (client rand)",
+ resp->rand_p, EAP_PSK_RAND_LEN);
+ os_memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN);
+
+ /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */
+ buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN;
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ data->state = FAILURE;
+ return;
+ }
+ os_memcpy(buf, data->id_p, data->id_p_len);
+ pos = buf + data->id_p_len;
+ os_memcpy(pos, data->id_s, data->id_s_len);
+ pos += data->id_s_len;
+ os_memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN);
+ pos += EAP_PSK_RAND_LEN;
+ os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN);
+ if (omac1_aes_128(data->ak, buf, buflen, mac)) {
+ os_free(buf);
+ data->state = FAILURE;
+ return;
+ }
+ os_free(buf);
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN);
+ if (os_memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Invalid MAC_P");
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Expected MAC_P",
+ mac, EAP_PSK_MAC_LEN);
+ data->state = FAILURE;
+ return;
+ }
+
+ data->state = PSK_3;
+}
+
+
+static void eap_psk_process_4(struct eap_sm *sm,
+ struct eap_psk_data *data,
+ struct wpabuf *respData)
+{
+ const struct eap_psk_hdr_4 *resp;
+ u8 *decrypted, nonce[16];
+ size_t left;
+ const u8 *pos, *tag;
+
+ if (data->state != PSK_3)
+ return;
+
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-4");
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &left);
+ if (pos == NULL || left < sizeof(*resp)) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
+ return;
+ }
+ resp = (const struct eap_psk_hdr_4 *) pos;
+ pos = (const u8 *) (resp + 1);
+ left -= sizeof(*resp);
+
+ wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Encrypted PCHANNEL", pos, left);
+
+ if (left < 4 + 16 + 1) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in "
+ "PSK-4 (len=%lu, expected 21)",
+ (unsigned long) left);
+ return;
+ }
+
+ if (pos[0] == 0 && pos[1] == 0 && pos[2] == 0 && pos[3] == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-PSK: Nonce did not increase");
+ return;
+ }
+
+ os_memset(nonce, 0, 12);
+ os_memcpy(nonce + 12, pos, 4);
+ pos += 4;
+ left -= 4;
+ tag = pos;
+ pos += 16;
+ left -= 16;
+
+ decrypted = os_malloc(left);
+ if (decrypted == NULL)
+ return;
+ os_memcpy(decrypted, pos, left);
+
+ if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
+ wpabuf_head(respData), 22, decrypted, left,
+ tag)) {
+ wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed");
+ os_free(decrypted);
+ data->state = FAILURE;
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message",
+ decrypted, left);
+
+ /* Verify R flag */
+ switch (decrypted[0] >> 6) {
+ case EAP_PSK_R_FLAG_CONT:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported");
+ data->state = FAILURE;
+ break;
+ case EAP_PSK_R_FLAG_DONE_SUCCESS:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS");
+ data->state = SUCCESS;
+ break;
+ case EAP_PSK_R_FLAG_DONE_FAILURE:
+ wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE");
+ data->state = FAILURE;
+ break;
+ }
+ os_free(decrypted);
+}
+
+
+static void eap_psk_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_psk_data *data = priv;
+ const u8 *pos;
+ size_t len;
+
+ if (sm->user == NULL || sm->user->password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PSK: Plaintext password not "
+ "configured");
+ data->state = FAILURE;
+ return;
+ }
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len);
+ if (pos == NULL || len < 1)
+ return;
+
+ switch (EAP_PSK_FLAGS_GET_T(*pos)) {
+ case 1:
+ eap_psk_process_2(sm, data, respData);
+ break;
+ case 3:
+ eap_psk_process_4(sm, data, respData);
+ break;
+ }
+}
+
+
+static Boolean eap_psk_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_psk_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_psk_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->msk, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+
+ return key;
+}
+
+
+static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_psk_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
+static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_psk_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_psk_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_psk_init;
+ eap->reset = eap_psk_reset;
+ eap->buildReq = eap_psk_buildReq;
+ eap->check = eap_psk_check;
+ eap->process = eap_psk_process;
+ eap->isDone = eap_psk_isDone;
+ eap->getKey = eap_psk_getKey;
+ eap->isSuccess = eap_psk_isSuccess;
+ eap->get_emsk = eap_psk_get_emsk;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
new file mode 100644
index 0000000..dd2557a
--- /dev/null
+++ b/src/eap_server/eap_server_pwd.c
@@ -0,0 +1,844 @@
+/*
+ * hostapd / EAP-pwd (RFC 5931) server
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.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;
+
+ BIGNUM *k;
+ BIGNUM *private_value;
+ BIGNUM *peer_scalar;
+ BIGNUM *my_scalar;
+ EC_POINT *my_element;
+ EC_POINT *peer_element;
+
+ u8 my_confirm[SHA256_DIGEST_LENGTH];
+
+ 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;
+ }
+
+ 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 struct wpabuf *
+eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
+{
+ struct wpabuf *req;
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request");
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ sizeof(struct eap_pwd_hdr) +
+ sizeof(struct eap_pwd_id) + data->id_server_len,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ eap_pwd_state(data, FAILURE);
+ return NULL;
+ }
+
+ /* an lfsr is good enough to generate unpredictable tokens */
+ data->token = os_random();
+ wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH);
+ wpabuf_put_be16(req, data->group_num);
+ wpabuf_put_u8(req, EAP_PWD_DEFAULT_RAND_FUNC);
+ wpabuf_put_u8(req, EAP_PWD_DEFAULT_PRF);
+ wpabuf_put_data(req, &data->token, sizeof(data->token));
+ wpabuf_put_u8(req, EAP_PWD_PREP_NONE);
+ wpabuf_put_data(req, data->id_server, data->id_server_len);
+
+ return req;
+}
+
+
+static struct wpabuf *
+eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
+{
+ struct wpabuf *req = NULL;
+ BIGNUM *mask = NULL, *x = NULL, *y = NULL;
+ u8 *scalar = NULL, *element = NULL;
+ u16 offset;
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
+
+ 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);
+
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ sizeof(struct eap_pwd_hdr) +
+ (2 * BN_num_bytes(data->grp->prime)) +
+ BN_num_bytes(data->grp->order),
+ EAP_CODE_REQUEST, id);
+ if (req == NULL)
+ goto fin;
+ wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH);
+
+ /* We send the element as (x,y) followed by the scalar */
+ wpabuf_put_data(req, element, (2 * BN_num_bytes(data->grp->prime)));
+ wpabuf_put_data(req, scalar, BN_num_bytes(data->grp->order));
+
+fin:
+ os_free(scalar);
+ os_free(element);
+ BN_free(x);
+ BN_free(y);
+ if (req == NULL)
+ eap_pwd_state(data, FAILURE);
+
+ return req;
+}
+
+
+static struct wpabuf *
+eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
+{
+ struct wpabuf *req = NULL;
+ BIGNUM *x = NULL, *y = NULL;
+ HMAC_CTX ctx;
+ u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+ u16 grp;
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request");
+
+ /* 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)
+ */
+ H_Init(&ctx);
+
+ /*
+ * 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));
+ BN_bn2bin(data->k, cruft);
+ H_Update(&ctx, 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));
+ BN_bn2bin(x, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(y, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(data->my_scalar, cruft);
+ H_Update(&ctx, 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));
+ BN_bn2bin(x, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(y, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* peer scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(data->peer_scalar, cruft);
+ H_Update(&ctx, 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);
+ H_Update(&ctx, cruft, ptr-cruft);
+
+ /* all done with the random function */
+ H_Final(&ctx, conf);
+ os_memcpy(data->my_confirm, conf, SHA256_DIGEST_LENGTH);
+
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL)
+ goto fin;
+
+ wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH);
+ wpabuf_put_data(req, conf, SHA256_DIGEST_LENGTH);
+
+fin:
+ os_free(cruft);
+ BN_free(x);
+ BN_free(y);
+ if (req == NULL)
+ eap_pwd_state(data, FAILURE);
+
+ return req;
+}
+
+
+static struct wpabuf *
+eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id)
+{
+ struct eap_pwd_data *data = priv;
+
+ switch (data->state) {
+ case PWD_ID_Req:
+ return eap_pwd_build_id_req(sm, data, id);
+ case PWD_Commit_Req:
+ return eap_pwd_build_commit_req(sm, data, id);
+ case PWD_Confirm_Req:
+ return eap_pwd_build_confirm_req(sm, data, id);
+ default:
+ wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req",
+ data->state);
+ break;
+ }
+
+ return NULL;
+}
+
+
+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: opcode=%d", *pos);
+
+ if (data->state == PWD_ID_Req && *pos == EAP_PWD_OPCODE_ID_EXCH)
+ return FALSE;
+
+ if (data->state == PWD_Commit_Req &&
+ *pos == EAP_PWD_OPCODE_COMMIT_EXCH)
+ return FALSE;
+
+ if (data->state == PWD_Confirm_Req &&
+ *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;
+ HMAC_CTX ctx;
+ u32 cs;
+ u16 grp;
+ u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+
+ /* 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)
+ */
+ H_Init(&ctx);
+
+ /* k */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(data->k, cruft);
+ H_Update(&ctx, 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));
+ BN_bn2bin(x, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(y, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* peer scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(data->peer_scalar, cruft);
+ H_Update(&ctx, 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));
+ BN_bn2bin(x, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(y, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ BN_bn2bin(data->my_scalar, cruft);
+ H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+ /* ciphersuite */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ H_Update(&ctx, (u8 *)&cs, sizeof(u32));
+
+ /* all done */
+ H_Final(&ctx, conf);
+
+ ptr = (u8 *) payload;
+ if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
+ 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 exch;
+
+ 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;
+ }
+
+ exch = *pos & 0x3f;
+ switch (exch) {
+ case EAP_PWD_OPCODE_ID_EXCH:
+ eap_pwd_process_id_resp(sm, data, pos + 1, len - 1);
+ break;
+ case EAP_PWD_OPCODE_COMMIT_EXCH:
+ eap_pwd_process_commit_resp(sm, data, pos + 1, len - 1);
+ break;
+ case EAP_PWD_OPCODE_CONFIRM_EXCH:
+ eap_pwd_process_confirm_resp(sm, data, pos + 1, len - 1);
+ break;
+ }
+}
+
+
+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/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c
new file mode 100644
index 0000000..a9b515f
--- /dev/null
+++ b/src/eap_server/eap_server_sake.c
@@ -0,0 +1,543 @@
+/*
+ * hostapd / EAP-SAKE (RFC 4763) server
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "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;
+ u8 *serverid;
+ size_t serverid_len;
+};
+
+
+static const char * eap_sake_state_txt(int state)
+{
+ switch (state) {
+ case IDENTITY:
+ return "IDENTITY";
+ case CHALLENGE:
+ return "CHALLENGE";
+ case CONFIRM:
+ return "CONFIRM";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "?";
+ }
+}
+
+
+static void eap_sake_state(struct eap_sake_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
+ eap_sake_state_txt(data->state),
+ eap_sake_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_sake_init(struct eap_sm *sm)
+{
+ struct eap_sake_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = CHALLENGE;
+
+ if (os_get_random(&data->session_id, 1)) {
+ wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
+ os_free(data);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d",
+ data->session_id);
+
+ /* TODO: add support for configuring SERVERID */
+ data->serverid = (u8 *) os_strdup("hostapd");
+ if (data->serverid)
+ data->serverid_len = os_strlen((char *) data->serverid);
+
+ return data;
+}
+
+
+static void eap_sake_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_sake_data *data = priv;
+ os_free(data->serverid);
+ os_free(data->peerid);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
+ u8 id, size_t length, u8 subtype)
+{
+ struct eap_sake_hdr *sake;
+ struct wpabuf *msg;
+ size_t plen;
+
+ plen = sizeof(struct eap_sake_hdr) + length;
+
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
+ EAP_CODE_REQUEST, id);
+ if (msg == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
+ "request");
+ return NULL;
+ }
+
+ sake = wpabuf_put(msg, sizeof(*sake));
+ sake->version = EAP_SAKE_VERSION;
+ sake->session_id = data->session_id;
+ sake->subtype = subtype;
+
+ return msg;
+}
+
+
+static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm,
+ struct eap_sake_data *data,
+ u8 id)
+{
+ struct wpabuf *msg;
+ size_t plen;
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity");
+
+ plen = 4;
+ if (data->serverid)
+ plen += 2 + data->serverid_len;
+ msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY);
+ if (msg == NULL) {
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ");
+ eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2);
+
+ if (data->serverid) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
+ eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
+ data->serverid, data->serverid_len);
+ }
+
+ return msg;
+}
+
+
+static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm,
+ struct eap_sake_data *data,
+ u8 id)
+{
+ struct wpabuf *msg;
+ size_t plen;
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge");
+
+ if (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;
+ if (data->serverid)
+ plen += 2 + data->serverid_len;
+ msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE);
+ if (msg == NULL) {
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_S");
+ eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S,
+ data->rand_s, EAP_SAKE_RAND_LEN);
+
+ if (data->serverid) {
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
+ eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
+ data->serverid, data->serverid_len);
+ }
+
+ return msg;
+}
+
+
+static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm,
+ struct eap_sake_data *data,
+ u8 id)
+{
+ struct wpabuf *msg;
+ u8 *mic;
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Confirm");
+
+ msg = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN,
+ EAP_SAKE_SUBTYPE_CONFIRM);
+ if (msg == NULL) {
+ data->state = FAILURE;
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_S");
+ wpabuf_put_u8(msg, EAP_SAKE_AT_MIC_S);
+ wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN);
+ mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN);
+ if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+ data->serverid, data->serverid_len,
+ data->peerid, data->peerid_len, 0,
+ wpabuf_head(msg), wpabuf_len(msg), mic, mic))
+ {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+ data->state = FAILURE;
+ os_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+static struct wpabuf * eap_sake_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+ struct eap_sake_data *data = priv;
+
+ switch (data->state) {
+ case IDENTITY:
+ return eap_sake_build_identity(sm, data, id);
+ case CHALLENGE:
+ return eap_sake_build_challenge(sm, data, id);
+ case CONFIRM:
+ return eap_sake_build_confirm(sm, data, id);
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown state %d in buildReq",
+ data->state);
+ break;
+ }
+ return NULL;
+}
+
+
+static Boolean eap_sake_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_sake_data *data = priv;
+ struct eap_sake_hdr *resp;
+ size_t len;
+ u8 version, session_id, subtype;
+ const u8 *pos;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
+ if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Invalid frame");
+ return TRUE;
+ }
+
+ resp = (struct eap_sake_hdr *) pos;
+ version = resp->version;
+ session_id = resp->session_id;
+ subtype = resp->subtype;
+
+ if (version != EAP_SAKE_VERSION) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Unknown version %d", version);
+ return TRUE;
+ }
+
+ if (session_id != data->session_id) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
+ session_id, data->session_id);
+ return TRUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype=%d", subtype);
+
+ if (data->state == IDENTITY && subtype == EAP_SAKE_SUBTYPE_IDENTITY)
+ return FALSE;
+
+ if (data->state == CHALLENGE && subtype == EAP_SAKE_SUBTYPE_CHALLENGE)
+ return FALSE;
+
+ if (data->state == CONFIRM && subtype == EAP_SAKE_SUBTYPE_CONFIRM)
+ return FALSE;
+
+ if (subtype == EAP_SAKE_SUBTYPE_AUTH_REJECT)
+ return FALSE;
+
+ wpa_printf(MSG_INFO, "EAP-SAKE: Unexpected subtype=%d in state=%d",
+ subtype, data->state);
+
+ return TRUE;
+}
+
+
+static void eap_sake_process_identity(struct eap_sm *sm,
+ struct eap_sake_data *data,
+ const struct wpabuf *respData,
+ const u8 *payload, size_t payloadlen)
+{
+ if (data->state != IDENTITY)
+ return;
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Identity");
+ /* TODO: update identity and select new user data */
+ eap_sake_state(data, CHALLENGE);
+}
+
+
+static void eap_sake_process_challenge(struct eap_sm *sm,
+ struct eap_sake_data *data,
+ const struct wpabuf *respData,
+ const u8 *payload, size_t payloadlen)
+{
+ struct eap_sake_parse_attr attr;
+ u8 mic_p[EAP_SAKE_MIC_LEN];
+
+ if (data->state != CHALLENGE)
+ return;
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Challenge");
+
+ if (eap_sake_parse_attributes(payload, payloadlen, &attr))
+ return;
+
+ if (!attr.rand_p || !attr.mic_p) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Response/Challenge did not "
+ "include AT_RAND_P or AT_MIC_P");
+ return;
+ }
+
+ os_memcpy(data->rand_p, attr.rand_p, EAP_SAKE_RAND_LEN);
+
+ os_free(data->peerid);
+ data->peerid = NULL;
+ data->peerid_len = 0;
+ if (attr.peerid) {
+ data->peerid = os_malloc(attr.peerid_len);
+ if (data->peerid == NULL)
+ return;
+ os_memcpy(data->peerid, attr.peerid, attr.peerid_len);
+ data->peerid_len = attr.peerid_len;
+ }
+
+ if (sm->user == NULL || sm->user->password == NULL ||
+ sm->user->password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Plaintext password with "
+ "%d-byte key not configured",
+ 2 * EAP_SAKE_ROOT_SECRET_LEN);
+ data->state = FAILURE;
+ return;
+ }
+ eap_sake_derive_keys(sm->user->password,
+ sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
+ data->rand_s, data->rand_p,
+ (u8 *) &data->tek, data->msk, data->emsk);
+
+ eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+ data->serverid, data->serverid_len,
+ data->peerid, data->peerid_len, 1,
+ wpabuf_head(respData), wpabuf_len(respData),
+ attr.mic_p, mic_p);
+ if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
+ eap_sake_state(data, FAILURE);
+ return;
+ }
+
+ eap_sake_state(data, CONFIRM);
+}
+
+
+static void eap_sake_process_confirm(struct eap_sm *sm,
+ struct eap_sake_data *data,
+ const struct wpabuf *respData,
+ const u8 *payload, size_t payloadlen)
+{
+ struct eap_sake_parse_attr attr;
+ u8 mic_p[EAP_SAKE_MIC_LEN];
+
+ if (data->state != CONFIRM)
+ return;
+
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm");
+
+ if (eap_sake_parse_attributes(payload, payloadlen, &attr))
+ return;
+
+ if (!attr.mic_p) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not "
+ "include AT_MIC_P");
+ return;
+ }
+
+ eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+ data->serverid, data->serverid_len,
+ data->peerid, data->peerid_len, 1,
+ wpabuf_head(respData), wpabuf_len(respData),
+ attr.mic_p, mic_p);
+ if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
+ eap_sake_state(data, FAILURE);
+ } else
+ eap_sake_state(data, SUCCESS);
+}
+
+
+static void eap_sake_process_auth_reject(struct eap_sm *sm,
+ struct eap_sake_data *data,
+ const struct wpabuf *respData,
+ const u8 *payload, size_t payloadlen)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject");
+ eap_sake_state(data, FAILURE);
+}
+
+
+static void eap_sake_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_sake_data *data = priv;
+ struct eap_sake_hdr *resp;
+ u8 subtype;
+ size_t len;
+ const u8 *pos, *end;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
+ if (pos == NULL || len < sizeof(struct eap_sake_hdr))
+ return;
+
+ resp = (struct eap_sake_hdr *) pos;
+ end = pos + len;
+ subtype = resp->subtype;
+ pos = (u8 *) (resp + 1);
+
+ wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
+ pos, end - pos);
+
+ switch (subtype) {
+ case EAP_SAKE_SUBTYPE_IDENTITY:
+ eap_sake_process_identity(sm, data, respData, pos, end - pos);
+ break;
+ case EAP_SAKE_SUBTYPE_CHALLENGE:
+ eap_sake_process_challenge(sm, data, respData, pos, end - pos);
+ break;
+ case EAP_SAKE_SUBTYPE_CONFIRM:
+ eap_sake_process_confirm(sm, data, respData, pos, end - pos);
+ break;
+ case EAP_SAKE_SUBTYPE_AUTH_REJECT:
+ eap_sake_process_auth_reject(sm, data, respData, pos,
+ end - pos);
+ break;
+ }
+}
+
+
+static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_sake_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sake_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->msk, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+
+ return key;
+}
+
+
+static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sake_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
+static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_sake_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_sake_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_sake_init;
+ eap->reset = eap_sake_reset;
+ eap->buildReq = eap_sake_buildReq;
+ eap->check = eap_sake_check;
+ eap->process = eap_sake_process;
+ eap->isDone = eap_sake_isDone;
+ eap->getKey = eap_sake_getKey;
+ eap->isSuccess = eap_sake_isSuccess;
+ eap->get_emsk = eap_sake_get_emsk;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
new file mode 100644
index 0000000..29df2ff
--- /dev/null
+++ b/src/eap_server/eap_server_sim.c
@@ -0,0 +1,798 @@
+/*
+ * hostapd / EAP-SIM (RFC 4186)
+ * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "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;
+};
+
+
+static const char * eap_sim_state_txt(int state)
+{
+ switch (state) {
+ case START:
+ return "START";
+ case CHALLENGE:
+ return "CHALLENGE";
+ case REAUTH:
+ return "REAUTH";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ case NOTIFICATION:
+ return "NOTIFICATION";
+ default:
+ return "Unknown?!";
+ }
+}
+
+
+static void eap_sim_state(struct eap_sim_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s",
+ eap_sim_state_txt(data->state),
+ eap_sim_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_sim_init(struct eap_sm *sm)
+{
+ struct eap_sim_data *data;
+
+ if (sm->eap_sim_db_priv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = START;
+
+ return data;
+}
+
+
+static void eap_sim_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ os_free(data->next_pseudonym);
+ os_free(data->next_reauth_id);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_sim_build_start(struct eap_sm *sm,
+ struct eap_sim_data *data, u8 id)
+{
+ struct eap_sim_msg *msg;
+ u8 ver[2];
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start");
+ msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
+ EAP_SIM_SUBTYPE_START);
+ if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
+ sm->identity_len)) {
+ wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ");
+ eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
+ } else {
+ /*
+ * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is
+ * ignored and the SIM/Start is used to request the identity.
+ */
+ wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ");
+ eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
+ }
+ wpa_printf(MSG_DEBUG, " AT_VERSION_LIST");
+ ver[0] = 0;
+ ver[1] = EAP_SIM_VERSION;
+ eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver),
+ ver, sizeof(ver));
+ return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data,
+ struct eap_sim_msg *msg, u16 counter,
+ const u8 *nonce_s)
+{
+ os_free(data->next_pseudonym);
+ data->next_pseudonym =
+ eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0);
+ os_free(data->next_reauth_id);
+ if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
+ data->next_reauth_id =
+ eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 0);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication "
+ "count exceeded - force full authentication");
+ data->next_reauth_id = NULL;
+ }
+
+ if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
+ counter == 0 && nonce_s == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, " AT_IV");
+ wpa_printf(MSG_DEBUG, " AT_ENCR_DATA");
+ eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+ if (counter > 0) {
+ wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter);
+ eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+ }
+
+ if (nonce_s) {
+ wpa_printf(MSG_DEBUG, " *AT_NONCE_S");
+ eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
+ EAP_SIM_NONCE_S_LEN);
+ }
+
+ if (data->next_pseudonym) {
+ wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)",
+ data->next_pseudonym);
+ eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
+ os_strlen(data->next_pseudonym),
+ (u8 *) data->next_pseudonym,
+ os_strlen(data->next_pseudonym));
+ }
+
+ if (data->next_reauth_id) {
+ wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)",
+ data->next_reauth_id);
+ eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
+ os_strlen(data->next_reauth_id),
+ (u8 *) data->next_reauth_id,
+ os_strlen(data->next_reauth_id));
+ }
+
+ if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+ "AT_ENCR_DATA");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ u8 id)
+{
+ struct eap_sim_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge");
+ msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
+ EAP_SIM_SUBTYPE_CHALLENGE);
+ wpa_printf(MSG_DEBUG, " AT_RAND");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand,
+ data->num_chal * GSM_RAND_LEN);
+
+ if (eap_sim_build_encr(sm, data, msg, 0, NULL)) {
+ eap_sim_msg_free(msg);
+ return NULL;
+ }
+
+ if (sm->eap_sim_aka_result_ind) {
+ wpa_printf(MSG_DEBUG, " AT_RESULT_IND");
+ eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+ }
+
+ wpa_printf(MSG_DEBUG, " AT_MAC");
+ eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+ return eap_sim_msg_finish(msg, data->k_aut, data->nonce_mt,
+ EAP_SIM_NONCE_MT_LEN);
+}
+
+
+static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm,
+ struct eap_sim_data *data, u8 id)
+{
+ struct eap_sim_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
+
+ if (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)
+{
+ struct eap_sim_data *data = priv;
+ const u8 *pos;
+ size_t len;
+ u8 subtype;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
+ if (pos == NULL || len < 3) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
+ return TRUE;
+ }
+ subtype = *pos;
+
+ if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
+ return FALSE;
+
+ switch (data->state) {
+ case START:
+ if (subtype != EAP_SIM_SUBTYPE_START) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+ "subtype %d", subtype);
+ return TRUE;
+ }
+ break;
+ case CHALLENGE:
+ if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+ "subtype %d", subtype);
+ return TRUE;
+ }
+ break;
+ case REAUTH:
+ if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+ "subtype %d", subtype);
+ return TRUE;
+ }
+ break;
+ case NOTIFICATION:
+ if (subtype != EAP_SIM_SUBTYPE_NOTIFICATION) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+ "subtype %d", subtype);
+ return TRUE;
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for "
+ "processing a response", data->state);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int eap_sim_supported_ver(struct eap_sim_data *data, int version)
+{
+ return version == EAP_SIM_VERSION;
+}
+
+
+static void eap_sim_process_start(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ struct wpabuf *respData,
+ struct eap_sim_attrs *attr)
+{
+ const u8 *identity;
+ size_t identity_len;
+ u8 ver_list[2];
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
+
+ if (attr->identity) {
+ os_free(sm->identity);
+ sm->identity = os_malloc(attr->identity_len);
+ if (sm->identity) {
+ os_memcpy(sm->identity, attr->identity,
+ attr->identity_len);
+ sm->identity_len = attr->identity_len;
+ }
+ }
+
+ identity = NULL;
+ identity_len = 0;
+
+ if (sm->identity && sm->identity_len > 0 &&
+ sm->identity[0] == EAP_SIM_PERMANENT_PREFIX) {
+ identity = sm->identity;
+ identity_len = sm->identity_len;
+ } else {
+ identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
+ sm->identity,
+ sm->identity_len,
+ &identity_len);
+ if (identity == NULL) {
+ data->reauth = eap_sim_db_get_reauth_entry(
+ sm->eap_sim_db_priv, sm->identity,
+ sm->identity_len);
+ if (data->reauth) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast "
+ "re-authentication");
+ identity = data->reauth->identity;
+ identity_len = data->reauth->identity_len;
+ data->counter = data->reauth->counter;
+ os_memcpy(data->mk, data->reauth->mk,
+ EAP_SIM_MK_LEN);
+ }
+ }
+ }
+
+ if (identity == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent"
+ " user name");
+ eap_sim_state(data, FAILURE);
+ return;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
+ identity, identity_len);
+
+ if (data->reauth) {
+ eap_sim_state(data, REAUTH);
+ return;
+ }
+
+ if (attr->nonce_mt == NULL || attr->selected_version < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
+ "required attributes");
+ eap_sim_state(data, FAILURE);
+ return;
+ }
+
+ if (!eap_sim_supported_ver(data, attr->selected_version)) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
+ "version %d", attr->selected_version);
+ eap_sim_state(data, FAILURE);
+ return;
+ }
+
+ data->counter = 0; /* reset re-auth counter since this is full auth */
+ data->reauth = NULL;
+
+ data->num_chal = eap_sim_db_get_gsm_triplets(
+ sm->eap_sim_db_priv, identity, identity_len,
+ EAP_SIM_MAX_CHAL,
+ (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm);
+ if (data->num_chal == EAP_SIM_DB_PENDING) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets "
+ "not yet available - pending request");
+ sm->method_pending = METHOD_PENDING_WAIT;
+ return;
+ }
+ if (data->num_chal < 2) {
+ wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
+ "authentication triplets for the peer");
+ eap_sim_state(data, FAILURE);
+ return;
+ }
+
+ identity_len = sm->identity_len;
+ while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null "
+ "character from identity");
+ identity_len--;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation",
+ sm->identity, identity_len);
+
+ os_memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+ WPA_PUT_BE16(ver_list, EAP_SIM_VERSION);
+ eap_sim_derive_mk(sm->identity, identity_len, attr->nonce_mt,
+ attr->selected_version, ver_list, sizeof(ver_list),
+ data->num_chal, (const u8 *) data->kc, data->mk);
+ eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
+ data->emsk);
+
+ eap_sim_state(data, CHALLENGE);
+}
+
+
+static void eap_sim_process_challenge(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ struct wpabuf *respData,
+ struct eap_sim_attrs *attr)
+{
+ const u8 *identity;
+ size_t identity_len;
+
+ if (attr->mac == NULL ||
+ eap_sim_verify_mac(data->k_aut, respData, attr->mac,
+ (u8 *) data->sres,
+ data->num_chal * EAP_SIM_SRES_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+ "did not include valid AT_MAC");
+ eap_sim_state(data, FAILURE);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the "
+ "correct AT_MAC");
+ if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+ data->use_result_ind = 1;
+ data->notification = EAP_SIM_SUCCESS;
+ eap_sim_state(data, NOTIFICATION);
+ } else
+ eap_sim_state(data, SUCCESS);
+
+ identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
+ sm->identity_len, &identity_len);
+ if (identity == NULL) {
+ identity = sm->identity;
+ identity_len = sm->identity_len;
+ }
+
+ if (data->next_pseudonym) {
+ eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
+ identity_len,
+ data->next_pseudonym);
+ data->next_pseudonym = NULL;
+ }
+ if (data->next_reauth_id) {
+ eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
+ identity_len,
+ data->next_reauth_id, data->counter + 1,
+ data->mk);
+ data->next_reauth_id = NULL;
+ }
+}
+
+
+static void eap_sim_process_reauth(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ struct wpabuf *respData,
+ struct eap_sim_attrs *attr)
+{
+ struct eap_sim_attrs eattr;
+ u8 *decrypted = NULL;
+ const u8 *identity, *id2;
+ size_t identity_len, id2_len;
+
+ if (attr->mac == NULL ||
+ eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s,
+ EAP_SIM_NONCE_S_LEN)) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
+ "did not include valid AT_MAC");
+ goto fail;
+ }
+
+ if (attr->encr_data == NULL || attr->iv == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+ "message did not include encrypted data");
+ goto fail;
+ }
+
+ decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+ attr->encr_data_len, attr->iv, &eattr,
+ 0);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+ "data from reauthentication message");
+ goto fail;
+ }
+
+ if (eattr.counter != data->counter) {
+ wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
+ "used incorrect counter %u, expected %u",
+ eattr.counter, data->counter);
+ goto fail;
+ }
+ os_free(decrypted);
+ decrypted = NULL;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes "
+ "the correct AT_MAC");
+ if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+ data->use_result_ind = 1;
+ data->notification = EAP_SIM_SUCCESS;
+ eap_sim_state(data, NOTIFICATION);
+ } else
+ eap_sim_state(data, SUCCESS);
+
+ if (data->reauth) {
+ identity = data->reauth->identity;
+ identity_len = data->reauth->identity_len;
+ } else {
+ identity = sm->identity;
+ identity_len = sm->identity_len;
+ }
+
+ id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
+ identity_len, &id2_len);
+ if (id2) {
+ identity = id2;
+ identity_len = id2_len;
+ }
+
+ if (data->next_pseudonym) {
+ eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
+ identity_len, data->next_pseudonym);
+ data->next_pseudonym = NULL;
+ }
+ if (data->next_reauth_id) {
+ eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
+ identity_len, data->next_reauth_id,
+ data->counter + 1, data->mk);
+ data->next_reauth_id = NULL;
+ } else {
+ eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+ data->reauth = NULL;
+ }
+
+ return;
+
+fail:
+ eap_sim_state(data, FAILURE);
+ eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+ data->reauth = NULL;
+ os_free(decrypted);
+}
+
+
+static void eap_sim_process_client_error(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ struct wpabuf *respData,
+ struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d",
+ attr->client_error_code);
+ if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
+ eap_sim_state(data, SUCCESS);
+ else
+ eap_sim_state(data, FAILURE);
+}
+
+
+static void eap_sim_process_notification(struct eap_sm *sm,
+ struct eap_sim_data *data,
+ struct wpabuf *respData,
+ struct eap_sim_attrs *attr)
+{
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Client replied to notification");
+ if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
+ eap_sim_state(data, SUCCESS);
+ else
+ eap_sim_state(data, FAILURE);
+}
+
+
+static void eap_sim_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_sim_data *data = priv;
+ const u8 *pos, *end;
+ u8 subtype;
+ size_t len;
+ struct eap_sim_attrs attr;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
+ if (pos == NULL || len < 3)
+ return;
+
+ end = pos + len;
+ subtype = *pos;
+ pos += 3;
+
+ if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
+ eap_sim_state(data, FAILURE);
+ return;
+ }
+
+ if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) {
+ eap_sim_process_client_error(sm, data, respData, &attr);
+ return;
+ }
+
+ switch (data->state) {
+ case START:
+ eap_sim_process_start(sm, data, respData, &attr);
+ break;
+ case CHALLENGE:
+ eap_sim_process_challenge(sm, data, respData, &attr);
+ break;
+ case REAUTH:
+ eap_sim_process_reauth(sm, data, respData, &attr);
+ break;
+ case NOTIFICATION:
+ eap_sim_process_notification(sm, data, respData, &attr);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
+ "process", data->state);
+ break;
+ }
+}
+
+
+static Boolean eap_sim_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sim_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+ *len = EAP_SIM_KEYING_DATA_LEN;
+ return key;
+}
+
+
+static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_sim_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+ return key;
+}
+
+
+static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_sim_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_sim_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_sim_init;
+ eap->reset = eap_sim_reset;
+ eap->buildReq = eap_sim_buildReq;
+ eap->check = eap_sim_check;
+ eap->process = eap_sim_process;
+ eap->isDone = eap_sim_isDone;
+ eap->getKey = eap_sim_getKey;
+ eap->isSuccess = eap_sim_isSuccess;
+ eap->get_emsk = eap_sim_get_emsk;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
new file mode 100644
index 0000000..c98fa18
--- /dev/null
+++ b/src/eap_server/eap_server_tls.c
@@ -0,0 +1,286 @@
+/*
+ * hostapd / EAP-TLS (RFC 2716)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "crypto/tls.h"
+
+
+static void eap_tls_reset(struct eap_sm *sm, void *priv);
+
+
+struct eap_tls_data {
+ struct eap_ssl_data ssl;
+ enum { START, CONTINUE, SUCCESS, FAILURE } state;
+ int established;
+};
+
+
+static const char * eap_tls_state_txt(int state)
+{
+ switch (state) {
+ case START:
+ return "START";
+ case CONTINUE:
+ return "CONTINUE";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "Unknown?!";
+ }
+}
+
+
+static void eap_tls_state(struct eap_tls_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s",
+ eap_tls_state_txt(data->state),
+ eap_tls_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_tls_init(struct eap_sm *sm)
+{
+ struct eap_tls_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = START;
+
+ if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+ eap_tls_reset(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_tls_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ if (data == NULL)
+ return;
+ eap_server_tls_ssl_deinit(sm, &data->ssl);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
+ struct eap_tls_data *data, u8 id)
+{
+ struct wpabuf *req;
+
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST,
+ id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
+ "request");
+ eap_tls_state(data, FAILURE);
+ return NULL;
+ }
+
+ wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
+
+ eap_tls_state(data, CONTINUE);
+
+ return req;
+}
+
+
+static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+ struct eap_tls_data *data = priv;
+ struct wpabuf *res;
+
+ if (data->ssl.state == FRAG_ACK) {
+ return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0);
+ }
+
+ if (data->ssl.state == WAIT_FRAG_ACK) {
+ res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0,
+ id);
+ goto check_established;
+ }
+
+ switch (data->state) {
+ case START:
+ return eap_tls_build_start(sm, data, id);
+ case CONTINUE:
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
+ data->established = 1;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
+ __func__, data->state);
+ return NULL;
+ }
+
+ res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id);
+
+check_established:
+ if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
+ /* TLS handshake has been completed and there are no more
+ * fragments waiting to be sent out. */
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
+ eap_tls_state(data, SUCCESS);
+ }
+
+ return res;
+}
+
+
+static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len);
+ if (pos == NULL || len < 1) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_tls_process_msg(struct eap_sm *sm, void *priv,
+ const struct wpabuf *respData)
+{
+ struct eap_tls_data *data = priv;
+ if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
+ "handshake message");
+ return;
+ }
+ if (eap_server_tls_phase1(sm, &data->ssl) < 0)
+ eap_tls_state(data, FAILURE);
+}
+
+
+static void eap_tls_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_tls_data *data = priv;
+ if (eap_server_tls_process(sm, &data->ssl, respData, data,
+ EAP_TYPE_TLS, NULL, eap_tls_process_msg) <
+ 0)
+ eap_tls_state(data, FAILURE);
+}
+
+
+static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_tls_data *data = priv;
+ u8 *eapKeyData;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+ "client EAP encryption",
+ EAP_TLS_KEY_LEN);
+ if (eapKeyData) {
+ *len = EAP_TLS_KEY_LEN;
+ wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
+ eapKeyData, EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
+ }
+
+ return eapKeyData;
+}
+
+
+static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_tls_data *data = priv;
+ u8 *eapKeyData, *emsk;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+ "client EAP encryption",
+ EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+ if (eapKeyData) {
+ emsk = os_malloc(EAP_EMSK_LEN);
+ if (emsk)
+ os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
+ EAP_EMSK_LEN);
+ os_free(eapKeyData);
+ } else
+ emsk = NULL;
+
+ if (emsk) {
+ *len = EAP_EMSK_LEN;
+ wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
+ emsk, EAP_EMSK_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
+ }
+
+ return emsk;
+}
+
+
+static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_tls_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_tls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_tls_init;
+ eap->reset = eap_tls_reset;
+ eap->buildReq = eap_tls_buildReq;
+ eap->check = eap_tls_check;
+ eap->process = eap_tls_process;
+ eap->isDone = eap_tls_isDone;
+ eap->getKey = eap_tls_getKey;
+ eap->isSuccess = eap_tls_isSuccess;
+ eap->get_emsk = eap_tls_get_emsk;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
new file mode 100644
index 0000000..e149ee3
--- /dev/null
+++ b/src/eap_server/eap_server_tls_common.c
@@ -0,0 +1,400 @@
+/*
+ * EAP-TLS/PEAP/TTLS/FAST server common functions
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+
+
+static void eap_server_tls_free_in_buf(struct eap_ssl_data *data);
+
+
+int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+ int verify_peer)
+{
+ data->eap = sm;
+ data->phase2 = sm->init_phase2;
+
+ data->conn = tls_connection_init(sm->ssl_ctx);
+ if (data->conn == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
+ "connection");
+ return -1;
+ }
+
+ if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) {
+ wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
+ "of TLS peer certificate");
+ tls_connection_deinit(sm->ssl_ctx, data->conn);
+ data->conn = NULL;
+ return -1;
+ }
+
+ 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(keys.master_key, keys.master_key_len,
+ label, rnd, keys.client_random_len +
+ keys.server_random_len, out, len))
+ goto fail;
+
+ os_free(rnd);
+ return out;
+
+fail:
+ os_free(out);
+ os_free(rnd);
+ return NULL;
+}
+
+
+struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
+ int eap_type, int version, u8 id)
+{
+ struct wpabuf *req;
+ u8 flags;
+ size_t send_len, plen;
+
+ wpa_printf(MSG_DEBUG, "SSL: Generating Request");
+ if (data->tls_out == NULL) {
+ wpa_printf(MSG_ERROR, "SSL: tls_out NULL in %s", __func__);
+ return NULL;
+ }
+
+ flags = version;
+ send_len = wpabuf_len(data->tls_out) - data->tls_out_pos;
+ if (1 + send_len > data->tls_out_limit) {
+ send_len = data->tls_out_limit - 1;
+ flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
+ if (data->tls_out_pos == 0) {
+ flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
+ send_len -= 4;
+ }
+ }
+
+ plen = 1 + send_len;
+ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
+ plen += 4;
+
+ req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL)
+ return NULL;
+
+ wpabuf_put_u8(req, flags); /* Flags */
+ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
+ wpabuf_put_be32(req, wpabuf_len(data->tls_out));
+
+ wpabuf_put_data(req, wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
+ send_len);
+ data->tls_out_pos += send_len;
+
+ if (data->tls_out_pos == wpabuf_len(data->tls_out)) {
+ wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes "
+ "(message sent completely)",
+ (unsigned long) send_len);
+ wpabuf_free(data->tls_out);
+ data->tls_out = NULL;
+ data->tls_out_pos = 0;
+ data->state = MSG;
+ } else {
+ wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes "
+ "(%lu more to send)", (unsigned long) send_len,
+ (unsigned long) wpabuf_len(data->tls_out) -
+ data->tls_out_pos);
+ data->state = WAIT_FRAG_ACK;
+ }
+
+ return req;
+}
+
+
+struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version)
+{
+ struct wpabuf *req;
+
+ req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST,
+ id);
+ if (req == NULL)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "SSL: Building ACK");
+ wpabuf_put_u8(req, version); /* Flags */
+ return req;
+}
+
+
+static int eap_server_tls_process_cont(struct eap_ssl_data *data,
+ const u8 *buf, size_t len)
+{
+ /* Process continuation of a pending message */
+ if (len > wpabuf_tailroom(data->tls_in)) {
+ wpa_printf(MSG_DEBUG, "SSL: Fragment overflow");
+ return -1;
+ }
+
+ wpabuf_put_data(data->tls_in, buf, len);
+ wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes, waiting for %lu "
+ "bytes more", (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->tls_in));
+
+ return 0;
+}
+
+
+static int eap_server_tls_process_fragment(struct eap_ssl_data *data,
+ u8 flags, u32 message_length,
+ const u8 *buf, size_t len)
+{
+ /* Process a fragment that is not the last one of the message */
+ if (data->tls_in == NULL && !(flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)) {
+ wpa_printf(MSG_DEBUG, "SSL: No Message Length field in a "
+ "fragmented packet");
+ return -1;
+ }
+
+ if (data->tls_in == NULL) {
+ /* First fragment of the message */
+
+ /* Limit length to avoid rogue peers from causing large
+ * memory allocations. */
+ if (message_length > 65536) {
+ wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size"
+ " over 64 kB)");
+ return -1;
+ }
+
+ data->tls_in = wpabuf_alloc(message_length);
+ if (data->tls_in == NULL) {
+ wpa_printf(MSG_DEBUG, "SSL: No memory for message");
+ return -1;
+ }
+ wpabuf_put_data(data->tls_in, buf, len);
+ wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes in first "
+ "fragment, waiting for %lu bytes more",
+ (unsigned long) len,
+ (unsigned long) wpabuf_tailroom(data->tls_in));
+ }
+
+ return 0;
+}
+
+
+int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+ if (data->tls_out) {
+ /* This should not happen.. */
+ wpa_printf(MSG_INFO, "SSL: pending tls_out data when "
+ "processing new message");
+ wpabuf_free(data->tls_out);
+ WPA_ASSERT(data->tls_out == NULL);
+ }
+
+ data->tls_out = tls_connection_server_handshake(sm->ssl_ctx,
+ data->conn,
+ data->tls_in, NULL);
+ if (data->tls_out == NULL) {
+ wpa_printf(MSG_INFO, "SSL: TLS processing failed");
+ return -1;
+ }
+ if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+ /* TLS processing has failed - return error */
+ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
+ "report error");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags,
+ const u8 **pos, size_t *left)
+{
+ unsigned int tls_msg_len = 0;
+ const u8 *end = *pos + *left;
+
+ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+ if (*left < 4) {
+ wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
+ "length");
+ return -1;
+ }
+ tls_msg_len = WPA_GET_BE32(*pos);
+ wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
+ tls_msg_len);
+ *pos += 4;
+ *left -= 4;
+ }
+
+ wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x "
+ "Message Length %u", flags, tls_msg_len);
+
+ if (data->state == WAIT_FRAG_ACK) {
+ if (*left != 0) {
+ wpa_printf(MSG_DEBUG, "SSL: Unexpected payload in "
+ "WAIT_FRAG_ACK state");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "SSL: Fragment acknowledged");
+ return 1;
+ }
+
+ if (data->tls_in &&
+ eap_server_tls_process_cont(data, *pos, end - *pos) < 0)
+ return -1;
+
+ if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) {
+ if (eap_server_tls_process_fragment(data, flags, tls_msg_len,
+ *pos, end - *pos) < 0)
+ return -1;
+
+ data->state = FRAG_ACK;
+ return 1;
+ }
+
+ if (data->state == FRAG_ACK) {
+ wpa_printf(MSG_DEBUG, "SSL: All fragments received");
+ data->state = MSG;
+ }
+
+ if (data->tls_in == NULL) {
+ /* Wrap unfragmented messages as wpabuf without extra copy */
+ wpabuf_set(&data->tmpbuf, *pos, end - *pos);
+ data->tls_in = &data->tmpbuf;
+ }
+
+ return 0;
+}
+
+
+static void eap_server_tls_free_in_buf(struct eap_ssl_data *data)
+{
+ if (data->tls_in != &data->tmpbuf)
+ wpabuf_free(data->tls_in);
+ data->tls_in = NULL;
+}
+
+
+struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ const struct wpabuf *plain)
+{
+ struct wpabuf *buf;
+
+ buf = tls_connection_encrypt(sm->ssl_ctx, data->conn,
+ plain);
+ if (buf == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data");
+ return NULL;
+ }
+
+ return buf;
+}
+
+
+int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
+ struct wpabuf *respData, void *priv, int eap_type,
+ int (*proc_version)(struct eap_sm *sm, void *priv,
+ int peer_version),
+ void (*proc_msg)(struct eap_sm *sm, void *priv,
+ const struct wpabuf *respData))
+{
+ const u8 *pos;
+ u8 flags;
+ size_t left;
+ int ret, res = 0;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left);
+ if (pos == NULL || left < 1)
+ return 0; /* Should not happen - frame already validated */
+ flags = *pos++;
+ left--;
+ wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - Flags 0x%02x",
+ (unsigned long) wpabuf_len(respData), flags);
+
+ if (proc_version &&
+ proc_version(sm, priv, flags & EAP_TLS_VERSION_MASK) < 0)
+ return -1;
+
+ ret = eap_server_tls_reassemble(data, flags, &pos, &left);
+ if (ret < 0) {
+ res = -1;
+ goto done;
+ } else if (ret == 1)
+ return 0;
+
+ if (proc_msg)
+ proc_msg(sm, priv, respData);
+
+ if (tls_connection_get_write_alerts(sm->ssl_ctx, data->conn) > 1) {
+ wpa_printf(MSG_INFO, "SSL: Locally detected fatal error in "
+ "TLS processing");
+ res = -1;
+ }
+
+done:
+ eap_server_tls_free_in_buf(data);
+
+ return res;
+}
diff --git a/src/eap_server/eap_server_tnc.c b/src/eap_server/eap_server_tnc.c
new file mode 100644
index 0000000..a2d6f17
--- /dev/null
+++ b/src/eap_server/eap_server_tnc.c
@@ -0,0 +1,582 @@
+/*
+ * EAP server method: EAP-TNC (Trusted Network Connect)
+ * Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "eap_i.h"
+#include "tncs.h"
+
+
+struct eap_tnc_data {
+ enum eap_tnc_state {
+ START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE,
+ FAIL
+ } state;
+ enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation;
+ struct tncs_data *tncs;
+ struct wpabuf *in_buf;
+ struct wpabuf *out_buf;
+ size_t out_used;
+ size_t fragment_size;
+ unsigned int was_done:1;
+ unsigned int was_fail:1;
+};
+
+
+/* EAP-TNC Flags */
+#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TNC_FLAGS_START 0x20
+#define EAP_TNC_VERSION_MASK 0x07
+
+#define EAP_TNC_VERSION 1
+
+
+static const char * eap_tnc_state_txt(enum eap_tnc_state state)
+{
+ switch (state) {
+ case START:
+ return "START";
+ case CONTINUE:
+ return "CONTINUE";
+ case RECOMMENDATION:
+ return "RECOMMENDATION";
+ case FRAG_ACK:
+ return "FRAG_ACK";
+ case WAIT_FRAG_ACK:
+ return "WAIT_FRAG_ACK";
+ case DONE:
+ return "DONE";
+ case FAIL:
+ return "FAIL";
+ }
+ return "??";
+}
+
+
+static void eap_tnc_set_state(struct eap_tnc_data *data,
+ enum eap_tnc_state new_state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s",
+ eap_tnc_state_txt(data->state),
+ eap_tnc_state_txt(new_state));
+ data->state = new_state;
+}
+
+
+static void * eap_tnc_init(struct eap_sm *sm)
+{
+ struct eap_tnc_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ eap_tnc_set_state(data, START);
+ data->tncs = tncs_init();
+ if (data->tncs == NULL) {
+ os_free(data);
+ return NULL;
+ }
+
+ data->fragment_size = 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/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
new file mode 100644
index 0000000..702c50c
--- /dev/null
+++ b/src/eap_server/eap_server_ttls.c
@@ -0,0 +1,1430 @@
+/*
+ * hostapd / EAP-TTLS (RFC 5281)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/ms_funcs.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_server/eap_i.h"
+#include "eap_server/eap_tls_common.h"
+#include "eap_common/chap.h"
+#include "eap_common/eap_ttls.h"
+
+
+/* Maximum supported TTLS version
+ * 0 = RFC 5281
+ * 1 = draft-funk-eap-ttls-v1-00.txt
+ */
+#ifndef EAP_TTLS_VERSION
+#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
+#endif /* EAP_TTLS_VERSION */
+
+
+#define MSCHAPV2_KEY_LEN 16
+
+
+static void eap_ttls_reset(struct eap_sm *sm, void *priv);
+
+
+struct eap_ttls_data {
+ struct eap_ssl_data ssl;
+ enum {
+ START, PHASE1, PHASE2_START, PHASE2_METHOD,
+ PHASE2_MSCHAPV2_RESP, PHASE_FINISHED, SUCCESS, FAILURE
+ } state;
+
+ int ttls_version;
+ int force_version;
+ const struct eap_method *phase2_method;
+ void *phase2_priv;
+ int mschapv2_resp_ok;
+ u8 mschapv2_auth_response[20];
+ u8 mschapv2_ident;
+ int tls_ia_configured;
+ struct wpabuf *pending_phase2_eap_resp;
+ int tnc_started;
+};
+
+
+static const char * eap_ttls_state_txt(int state)
+{
+ switch (state) {
+ case START:
+ return "START";
+ case PHASE1:
+ return "PHASE1";
+ case PHASE2_START:
+ return "PHASE2_START";
+ case PHASE2_METHOD:
+ return "PHASE2_METHOD";
+ case PHASE2_MSCHAPV2_RESP:
+ return "PHASE2_MSCHAPV2_RESP";
+ case PHASE_FINISHED:
+ return "PHASE_FINISHED";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "Unknown?!";
+ }
+}
+
+
+static void eap_ttls_state(struct eap_ttls_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: %s -> %s",
+ eap_ttls_state_txt(data->state),
+ eap_ttls_state_txt(state));
+ data->state = state;
+}
+
+
+static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
+ int mandatory, size_t len)
+{
+ struct ttls_avp_vendor *avp;
+ u8 flags;
+ size_t hdrlen;
+
+ avp = (struct ttls_avp_vendor *) avphdr;
+ flags = mandatory ? AVP_FLAGS_MANDATORY : 0;
+ if (vendor_id) {
+ flags |= AVP_FLAGS_VENDOR;
+ hdrlen = sizeof(*avp);
+ avp->vendor_id = host_to_be32(vendor_id);
+ } else {
+ hdrlen = sizeof(struct ttls_avp);
+ }
+
+ avp->avp_code = host_to_be32(avp_code);
+ avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
+
+ return avphdr + hdrlen;
+}
+
+
+static struct wpabuf * eap_ttls_avp_encapsulate(struct wpabuf *resp,
+ u32 avp_code, int mandatory)
+{
+ struct wpabuf *avp;
+ u8 *pos;
+
+ avp = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(resp) + 4);
+ if (avp == NULL) {
+ wpabuf_free(resp);
+ return NULL;
+ }
+
+ pos = eap_ttls_avp_hdr(wpabuf_mhead(avp), avp_code, 0, mandatory,
+ wpabuf_len(resp));
+ os_memcpy(pos, wpabuf_head(resp), wpabuf_len(resp));
+ pos += wpabuf_len(resp);
+ AVP_PAD((const u8 *) wpabuf_head(avp), pos);
+ wpabuf_free(resp);
+ wpabuf_put(avp, pos - (u8 *) wpabuf_head(avp));
+ return avp;
+}
+
+
+struct eap_ttls_avp {
+ /* Note: eap is allocated memory; caller is responsible for freeing
+ * it. All the other pointers are pointing to the packet data, i.e.,
+ * they must not be freed separately. */
+ u8 *eap;
+ size_t eap_len;
+ u8 *user_name;
+ size_t user_name_len;
+ u8 *user_password;
+ size_t user_password_len;
+ u8 *chap_challenge;
+ size_t chap_challenge_len;
+ u8 *chap_password;
+ size_t chap_password_len;
+ u8 *mschap_challenge;
+ size_t mschap_challenge_len;
+ u8 *mschap_response;
+ size_t mschap_response_len;
+ u8 *mschap2_response;
+ size_t mschap2_response_len;
+};
+
+
+static int eap_ttls_avp_parse(struct wpabuf *buf, struct eap_ttls_avp *parse)
+{
+ struct ttls_avp *avp;
+ u8 *pos;
+ int left;
+
+ pos = wpabuf_mhead(buf);
+ left = wpabuf_len(buf);
+ os_memset(parse, 0, sizeof(*parse));
+
+ while (left > 0) {
+ u32 avp_code, avp_length, vendor_id = 0;
+ u8 avp_flags, *dpos;
+ size_t pad, dlen;
+ avp = (struct ttls_avp *) pos;
+ avp_code = be_to_host32(avp->avp_code);
+ avp_length = be_to_host32(avp->avp_length);
+ avp_flags = (avp_length >> 24) & 0xff;
+ avp_length &= 0xffffff;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "
+ "length=%d", (int) avp_code, avp_flags,
+ (int) avp_length);
+ if ((int) avp_length > left) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow "
+ "(len=%d, left=%d) - dropped",
+ (int) avp_length, left);
+ goto fail;
+ }
+ if (avp_length < sizeof(*avp)) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length "
+ "%d", avp_length);
+ goto fail;
+ }
+ dpos = (u8 *) (avp + 1);
+ dlen = avp_length - sizeof(*avp);
+ if (avp_flags & AVP_FLAGS_VENDOR) {
+ if (dlen < 4) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP "
+ "underflow");
+ goto fail;
+ }
+ vendor_id = be_to_host32(* (be32 *) dpos);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d",
+ (int) vendor_id);
+ dpos += 4;
+ dlen -= 4;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
+
+ if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
+ if (parse->eap == NULL) {
+ parse->eap = os_malloc(dlen);
+ if (parse->eap == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: "
+ "failed to allocate memory "
+ "for Phase 2 EAP data");
+ goto fail;
+ }
+ os_memcpy(parse->eap, dpos, dlen);
+ parse->eap_len = dlen;
+ } else {
+ u8 *neweap = os_realloc(parse->eap,
+ parse->eap_len + dlen);
+ if (neweap == NULL) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: "
+ "failed to allocate memory "
+ "for Phase 2 EAP data");
+ goto fail;
+ }
+ os_memcpy(neweap + parse->eap_len, dpos, dlen);
+ parse->eap = neweap;
+ parse->eap_len += dlen;
+ }
+ } else if (vendor_id == 0 &&
+ avp_code == RADIUS_ATTR_USER_NAME) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: User-Name",
+ dpos, dlen);
+ parse->user_name = dpos;
+ parse->user_name_len = dlen;
+ } else if (vendor_id == 0 &&
+ avp_code == RADIUS_ATTR_USER_PASSWORD) {
+ u8 *password = dpos;
+ size_t password_len = dlen;
+ while (password_len > 0 &&
+ password[password_len - 1] == '\0') {
+ password_len--;
+ }
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: "
+ "User-Password (PAP)",
+ password, password_len);
+ parse->user_password = password;
+ parse->user_password_len = password_len;
+ } else if (vendor_id == 0 &&
+ avp_code == RADIUS_ATTR_CHAP_CHALLENGE) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TTLS: CHAP-Challenge (CHAP)",
+ dpos, dlen);
+ parse->chap_challenge = dpos;
+ parse->chap_challenge_len = dlen;
+ } else if (vendor_id == 0 &&
+ avp_code == RADIUS_ATTR_CHAP_PASSWORD) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TTLS: CHAP-Password (CHAP)",
+ dpos, dlen);
+ parse->chap_password = dpos;
+ parse->chap_password_len = dlen;
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP_CHALLENGE) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TTLS: MS-CHAP-Challenge",
+ dpos, dlen);
+ parse->mschap_challenge = dpos;
+ parse->mschap_challenge_len = dlen;
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP_RESPONSE) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TTLS: MS-CHAP-Response (MSCHAP)",
+ dpos, dlen);
+ parse->mschap_response = dpos;
+ parse->mschap_response_len = dlen;
+ } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+ avp_code == RADIUS_ATTR_MS_CHAP2_RESPONSE) {
+ wpa_hexdump(MSG_DEBUG,
+ "EAP-TTLS: MS-CHAP2-Response (MSCHAPV2)",
+ dpos, dlen);
+ parse->mschap2_response = dpos;
+ parse->mschap2_response_len = dlen;
+ } else if (avp_flags & AVP_FLAGS_MANDATORY) {
+ wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported "
+ "mandatory AVP code %d vendor_id %d - "
+ "dropped", (int) avp_code, (int) vendor_id);
+ goto fail;
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported "
+ "AVP code %d vendor_id %d",
+ (int) avp_code, (int) vendor_id);
+ }
+
+ pad = (4 - (avp_length & 3)) & 3;
+ pos += avp_length + pad;
+ left -= avp_length + pad;
+ }
+
+ return 0;
+
+fail:
+ os_free(parse->eap);
+ parse->eap = NULL;
+ return -1;
+}
+
+
+static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
+ struct eap_ttls_data *data, size_t len)
+{
+ struct tls_keys keys;
+ u8 *challenge, *rnd;
+
+ if (data->ttls_version == 0) {
+ return eap_server_tls_derive_key(sm, &data->ssl,
+ "ttls challenge", len);
+ }
+
+ os_memset(&keys, 0, sizeof(keys));
+ if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
+ keys.client_random == NULL || keys.server_random == NULL ||
+ keys.inner_secret == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
+ "client random, or server random to derive "
+ "implicit challenge");
+ return NULL;
+ }
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ challenge = os_malloc(len);
+ if (rnd == NULL || challenge == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
+ "challenge derivation");
+ os_free(rnd);
+ os_free(challenge);
+ return NULL;
+ }
+ os_memcpy(rnd, keys.server_random, keys.server_random_len);
+ os_memcpy(rnd + keys.server_random_len, keys.client_random,
+ keys.client_random_len);
+
+ if (tls_prf(keys.inner_secret, keys.inner_secret_len,
+ "inner application challenge", rnd,
+ keys.client_random_len + keys.server_random_len,
+ challenge, len)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
+ "challenge");
+ os_free(rnd);
+ os_free(challenge);
+ return NULL;
+ }
+
+ os_free(rnd);
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
+ challenge, len);
+
+ return challenge;
+}
+
+
+static void * eap_ttls_init(struct eap_sm *sm)
+{
+ struct eap_ttls_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->ttls_version = EAP_TTLS_VERSION;
+ data->force_version = -1;
+ if (sm->user && sm->user->force_version >= 0) {
+ data->force_version = sm->user->force_version;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: forcing version %d",
+ data->force_version);
+ data->ttls_version = data->force_version;
+ }
+ data->state = START;
+
+ if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
+ data->ttls_version > 0) {
+ if (data->force_version > 0) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
+ "TLS library does not support TLS/IA.",
+ data->force_version);
+ eap_ttls_reset(sm, data);
+ return NULL;
+ }
+ data->ttls_version = 0;
+ }
+
+ if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+ eap_ttls_reset(sm, data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static void eap_ttls_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ if (data == NULL)
+ return;
+ if (data->phase2_priv && data->phase2_method)
+ data->phase2_method->reset(sm, data->phase2_priv);
+ eap_server_tls_ssl_deinit(sm, &data->ssl);
+ wpabuf_free(data->pending_phase2_eap_resp);
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm,
+ struct eap_ttls_data *data, u8 id)
+{
+ struct wpabuf *req;
+
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate memory for"
+ " request");
+ eap_ttls_state(data, FAILURE);
+ return NULL;
+ }
+
+ wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->ttls_version);
+
+ eap_ttls_state(data, PHASE1);
+
+ return req;
+}
+
+
+static struct wpabuf * eap_ttls_build_phase2_eap_req(
+ struct eap_sm *sm, struct eap_ttls_data *data, u8 id)
+{
+ struct wpabuf *buf, *encr_req;
+
+
+ buf = data->phase2_method->buildReq(sm, data->phase2_priv, id);
+ if (buf == NULL)
+ return NULL;
+
+ wpa_hexdump_buf_key(MSG_DEBUG,
+ "EAP-TTLS/EAP: Encapsulate Phase 2 data", buf);
+
+ buf = eap_ttls_avp_encapsulate(buf, RADIUS_ATTR_EAP_MESSAGE, 1);
+ if (buf == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Failed to encapsulate "
+ "packet");
+ return NULL;
+ }
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated "
+ "Phase 2 data", buf);
+
+ encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf);
+ wpabuf_free(buf);
+
+ return encr_req;
+}
+
+
+static struct wpabuf * eap_ttls_build_phase2_mschapv2(
+ struct eap_sm *sm, struct eap_ttls_data *data)
+{
+ struct wpabuf *encr_req, msgbuf;
+ u8 *req, *pos, *end;
+ int ret;
+
+ pos = req = os_malloc(100);
+ if (req == NULL)
+ return NULL;
+ end = req + 100;
+
+ if (data->mschapv2_resp_ok) {
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS,
+ RADIUS_VENDOR_ID_MICROSOFT, 1, 43);
+ *pos++ = data->mschapv2_ident;
+ ret = os_snprintf((char *) pos, end - pos, "S=");
+ if (ret >= 0 && ret < end - pos)
+ pos += ret;
+ pos += wpa_snprintf_hex_uppercase(
+ (char *) pos, end - pos, data->mschapv2_auth_response,
+ sizeof(data->mschapv2_auth_response));
+ } else {
+ pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR,
+ RADIUS_VENDOR_ID_MICROSOFT, 1, 6);
+ os_memcpy(pos, "Failed", 6);
+ pos += 6;
+ AVP_PAD(req, pos);
+ }
+
+ wpabuf_set(&msgbuf, req, pos - req);
+ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 "
+ "data", &msgbuf);
+
+ encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
+ os_free(req);
+
+ return encr_req;
+}
+
+
+static struct wpabuf * eap_ttls_build_phase_finished(
+ struct eap_sm *sm, struct eap_ttls_data *data, int final)
+{
+ return tls_connection_ia_send_phase_finished(sm->ssl_ctx,
+ data->ssl.conn, final);
+}
+
+
+static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+ struct eap_ttls_data *data = priv;
+
+ if (data->ssl.state == FRAG_ACK) {
+ return eap_server_tls_build_ack(id, EAP_TYPE_TTLS,
+ data->ttls_version);
+ }
+
+ if (data->ssl.state == WAIT_FRAG_ACK) {
+ return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, id);
+ }
+
+ switch (data->state) {
+ case START:
+ return eap_ttls_build_start(sm, data, id);
+ case PHASE1:
+ if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, "
+ "starting Phase2");
+ eap_ttls_state(data, PHASE2_START);
+ }
+ break;
+ case PHASE2_METHOD:
+ wpabuf_free(data->ssl.tls_out);
+ data->ssl.tls_out_pos = 0;
+ data->ssl.tls_out = eap_ttls_build_phase2_eap_req(sm, data,
+ id);
+ break;
+ case PHASE2_MSCHAPV2_RESP:
+ wpabuf_free(data->ssl.tls_out);
+ data->ssl.tls_out_pos = 0;
+ data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data);
+ break;
+ case PHASE_FINISHED:
+ wpabuf_free(data->ssl.tls_out);
+ data->ssl.tls_out_pos = 0;
+ data->ssl.tls_out = eap_ttls_build_phase_finished(sm, data, 1);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
+ __func__, data->state);
+ return NULL;
+ }
+
+ return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS,
+ data->ttls_version, id);
+}
+
+
+static Boolean eap_ttls_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TTLS, respData, &len);
+ if (pos == NULL || len < 1) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ const u8 *key, size_t key_len)
+{
+ u8 *buf;
+ size_t buf_len;
+ int ret;
+
+ if (key) {
+ buf_len = 2 + key_len;
+ buf = os_malloc(buf_len);
+ if (buf == NULL)
+ return -1;
+ WPA_PUT_BE16(buf, key_len);
+ os_memcpy(buf + 2, key, key_len);
+ } else {
+ buf = NULL;
+ buf_len = 0;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
+ "secret permutation", buf, buf_len);
+ ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
+ data->ssl.conn,
+ buf, buf_len);
+ os_free(buf);
+
+ return ret;
+}
+
+
+static void eap_ttls_process_phase2_pap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ const u8 *user_password,
+ size_t user_password_len)
+{
+ if (!sm->user || !sm->user->password || sm->user->password_hash ||
+ !(sm->user->ttls_auth & EAP_TTLS_AUTH_PAP)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No plaintext user "
+ "password configured");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (sm->user->password_len != user_password_len ||
+ os_memcmp(sm->user->password, user_password, user_password_len) !=
+ 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Invalid user password");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password");
+ eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
+ SUCCESS);
+}
+
+
+static void eap_ttls_process_phase2_chap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ const u8 *challenge,
+ size_t challenge_len,
+ const u8 *password,
+ size_t password_len)
+{
+ u8 *chal, hash[CHAP_MD5_LEN];
+
+ if (challenge == NULL || password == NULL ||
+ challenge_len != EAP_TTLS_CHAP_CHALLENGE_LEN ||
+ password_len != 1 + EAP_TTLS_CHAP_PASSWORD_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid CHAP attributes "
+ "(challenge len %lu password len %lu)",
+ (unsigned long) challenge_len,
+ (unsigned long) password_len);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (!sm->user || !sm->user->password || sm->user->password_hash ||
+ !(sm->user->ttls_auth & EAP_TTLS_AUTH_CHAP)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No plaintext user "
+ "password configured");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ chal = eap_ttls_implicit_challenge(sm, data,
+ EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
+ if (chal == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Failed to generate "
+ "challenge from TLS data");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (os_memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 ||
+ password[0] != chal[EAP_TTLS_CHAP_CHALLENGE_LEN]) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Challenge mismatch");
+ os_free(chal);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+ os_free(chal);
+
+ /* MD5(Ident + Password + Challenge) */
+ chap_md5(password[0], sm->user->password, sm->user->password_len,
+ challenge, challenge_len, hash);
+
+ if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password");
+ eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
+ SUCCESS);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password");
+ eap_ttls_state(data, FAILURE);
+ }
+}
+
+
+static void eap_ttls_process_phase2_mschap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ u8 *challenge, size_t challenge_len,
+ u8 *response, size_t response_len)
+{
+ u8 *chal, nt_response[24];
+
+ if (challenge == NULL || response == NULL ||
+ challenge_len != EAP_TTLS_MSCHAP_CHALLENGE_LEN ||
+ response_len != EAP_TTLS_MSCHAP_RESPONSE_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid MS-CHAP "
+ "attributes (challenge len %lu response len %lu)",
+ (unsigned long) challenge_len,
+ (unsigned long) response_len);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (!sm->user || !sm->user->password ||
+ !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAP)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: No user password "
+ "configured");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ chal = eap_ttls_implicit_challenge(sm, data,
+ EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
+ if (chal == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Failed to generate "
+ "challenge from TLS data");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 ||
+ response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Challenge mismatch");
+ os_free(chal);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+ os_free(chal);
+
+ if (sm->user->password_hash)
+ challenge_response(challenge, sm->user->password, nt_response);
+ else
+ nt_challenge_response(challenge, sm->user->password,
+ sm->user->password_len, nt_response);
+
+ if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
+ eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
+ SUCCESS);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response");
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received",
+ response + 2 + 24, 24);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Expected",
+ nt_response, 24);
+ eap_ttls_state(data, FAILURE);
+ }
+}
+
+
+static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ u8 *challenge,
+ size_t challenge_len,
+ u8 *response, size_t response_len)
+{
+ u8 *chal, *username, nt_response[24], *rx_resp, *peer_challenge,
+ *auth_challenge;
+ size_t username_len, i;
+
+ if (challenge == NULL || response == NULL ||
+ challenge_len != EAP_TTLS_MSCHAPV2_CHALLENGE_LEN ||
+ response_len != EAP_TTLS_MSCHAPV2_RESPONSE_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid MS-CHAP2 "
+ "attributes (challenge len %lu response len %lu)",
+ (unsigned long) challenge_len,
+ (unsigned long) response_len);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (!sm->user || !sm->user->password ||
+ !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAPV2)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user password "
+ "configured");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ /* MSCHAPv2 does not include optional domain name in the
+ * challenge-response calculation, so remove domain prefix
+ * (if present). */
+ username = sm->identity;
+ username_len = sm->identity_len;
+ for (i = 0; i < username_len; i++) {
+ if (username[i] == '\\') {
+ username_len -= i + 1;
+ username += i + 1;
+ break;
+ }
+ }
+
+ chal = eap_ttls_implicit_challenge(
+ sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);
+ if (chal == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Failed to generate "
+ "challenge from TLS data");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 ||
+ response[0] != chal[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Challenge mismatch");
+ os_free(chal);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+ os_free(chal);
+
+ auth_challenge = challenge;
+ peer_challenge = response + 2;
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: User",
+ username, username_len);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: auth_challenge",
+ auth_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: peer_challenge",
+ peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+
+ if (sm->user->password_hash) {
+ generate_nt_response_pwhash(auth_challenge, peer_challenge,
+ username, username_len,
+ sm->user->password,
+ nt_response);
+ } else {
+ generate_nt_response(auth_challenge, peer_challenge,
+ username, username_len,
+ sm->user->password,
+ sm->user->password_len,
+ nt_response);
+ }
+
+ rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8;
+ if (os_memcmp(nt_response, rx_resp, 24) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct "
+ "NT-Response");
+ data->mschapv2_resp_ok = 1;
+ if (data->ttls_version > 0) {
+ const u8 *pw_hash;
+ u8 pw_hash_buf[16], pw_hash_hash[16], master_key[16];
+ u8 session_key[2 * MSCHAPV2_KEY_LEN];
+
+ if (sm->user->password_hash)
+ pw_hash = sm->user->password;
+ else {
+ nt_password_hash(sm->user->password,
+ sm->user->password_len,
+ pw_hash_buf);
+ pw_hash = pw_hash_buf;
+ }
+ hash_nt_password_hash(pw_hash, pw_hash_hash);
+ get_master_key(pw_hash_hash, nt_response, master_key);
+ get_asymetric_start_key(master_key, session_key,
+ MSCHAPV2_KEY_LEN, 0, 0);
+ get_asymetric_start_key(master_key,
+ session_key + MSCHAPV2_KEY_LEN,
+ MSCHAPV2_KEY_LEN, 1, 0);
+ eap_ttls_ia_permute_inner_secret(sm, data,
+ session_key,
+ sizeof(session_key));
+ }
+
+ if (sm->user->password_hash) {
+ generate_authenticator_response_pwhash(
+ sm->user->password,
+ peer_challenge, auth_challenge,
+ username, username_len, nt_response,
+ data->mschapv2_auth_response);
+ } else {
+ generate_authenticator_response(
+ sm->user->password, sm->user->password_len,
+ peer_challenge, auth_challenge,
+ username, username_len, nt_response,
+ data->mschapv2_auth_response);
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid "
+ "NT-Response");
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Received",
+ rx_resp, 24);
+ wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Expected",
+ nt_response, 24);
+ data->mschapv2_resp_ok = 0;
+ }
+ eap_ttls_state(data, PHASE2_MSCHAPV2_RESP);
+ data->mschapv2_ident = response[0];
+}
+
+
+static int eap_ttls_phase2_eap_init(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ EapType eap_type)
+{
+ if (data->phase2_priv && data->phase2_method) {
+ data->phase2_method->reset(sm, data->phase2_priv);
+ data->phase2_method = NULL;
+ data->phase2_priv = NULL;
+ }
+ data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
+ eap_type);
+ if (!data->phase2_method)
+ return -1;
+
+ sm->init_phase2 = 1;
+ data->phase2_priv = data->phase2_method->init(sm);
+ sm->init_phase2 = 0;
+ return data->phase2_priv == NULL ? -1 : 0;
+}
+
+
+static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ u8 *in_data, size_t in_len)
+{
+ u8 next_type = EAP_TYPE_NONE;
+ struct eap_hdr *hdr;
+ u8 *pos;
+ size_t left;
+ struct wpabuf buf;
+ const struct eap_method *m = data->phase2_method;
+ void *priv = data->phase2_priv;
+
+ if (priv == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not "
+ "initialized?!", __func__);
+ return;
+ }
+
+ hdr = (struct eap_hdr *) in_data;
+ pos = (u8 *) (hdr + 1);
+
+ if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
+ left = in_len - sizeof(*hdr);
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 type Nak'ed; "
+ "allowed types", pos + 1, left - 1);
+ eap_sm_process_nak(sm, pos + 1, left - 1);
+ if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+ sm->user->methods[sm->user_eap_method_index].method !=
+ EAP_TYPE_NONE) {
+ next_type = sm->user->methods[
+ sm->user_eap_method_index++].method;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d",
+ next_type);
+ if (eap_ttls_phase2_eap_init(sm, data, next_type)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to "
+ "initialize EAP type %d",
+ next_type);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+ } else {
+ eap_ttls_state(data, FAILURE);
+ }
+ return;
+ }
+
+ wpabuf_set(&buf, in_data, in_len);
+
+ if (m->check(sm, priv, &buf)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to "
+ "ignore the packet");
+ return;
+ }
+
+ m->process(sm, priv, &buf);
+
+ if (sm->method_pending == METHOD_PENDING_WAIT) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method is in "
+ "pending wait state - save decrypted response");
+ wpabuf_free(data->pending_phase2_eap_resp);
+ data->pending_phase2_eap_resp = wpabuf_dup(&buf);
+ }
+
+ if (!m->isDone(sm, priv))
+ return;
+
+ if (!m->isSuccess(sm, priv)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method failed");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ switch (data->state) {
+ case PHASE2_START:
+ if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP_TTLS: Phase2 "
+ "Identity not found in the user "
+ "database",
+ sm->identity, sm->identity_len);
+ eap_ttls_state(data, FAILURE);
+ break;
+ }
+
+ eap_ttls_state(data, PHASE2_METHOD);
+ next_type = sm->user->methods[0].method;
+ sm->user_eap_method_index = 1;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type);
+ if (eap_ttls_phase2_eap_init(sm, data, next_type)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize "
+ "EAP type %d", next_type);
+ eap_ttls_state(data, FAILURE);
+ }
+ break;
+ case PHASE2_METHOD:
+ if (data->ttls_version > 0) {
+ if (m->getKey) {
+ u8 *key;
+ size_t key_len;
+ key = m->getKey(sm, priv, &key_len);
+ eap_ttls_ia_permute_inner_secret(sm, data,
+ key, key_len);
+ }
+ eap_ttls_state(data, PHASE_FINISHED);
+ } else
+ eap_ttls_state(data, SUCCESS);
+ break;
+ case FAILURE:
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
+ __func__, data->state);
+ break;
+ }
+}
+
+
+static void eap_ttls_process_phase2_eap(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ const u8 *eap, size_t eap_len)
+{
+ struct eap_hdr *hdr;
+ size_t len;
+
+ if (data->state == PHASE2_START) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: initializing Phase 2");
+ if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_IDENTITY) < 0)
+ {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: failed to "
+ "initialize EAP-Identity");
+ return;
+ }
+ }
+
+ if (eap_len < sizeof(*hdr)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: too short Phase 2 EAP "
+ "packet (len=%lu)", (unsigned long) eap_len);
+ return;
+ }
+
+ hdr = (struct eap_hdr *) eap;
+ len = be_to_host16(hdr->length);
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: received Phase 2 EAP: code=%d "
+ "identifier=%d length=%lu", hdr->code, hdr->identifier,
+ (unsigned long) len);
+ if (len > eap_len) {
+ wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Length mismatch in Phase 2"
+ " EAP frame (hdr len=%lu, data len in AVP=%lu)",
+ (unsigned long) len, (unsigned long) eap_len);
+ return;
+ }
+
+ switch (hdr->code) {
+ case EAP_CODE_RESPONSE:
+ eap_ttls_process_phase2_eap_response(sm, data, (u8 *) hdr,
+ len);
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Unexpected code=%d in "
+ "Phase 2 EAP header", hdr->code);
+ break;
+ }
+}
+
+
+static void eap_ttls_process_phase2(struct eap_sm *sm,
+ struct eap_ttls_data *data,
+ struct wpabuf *in_buf)
+{
+ struct wpabuf *in_decrypted;
+ struct eap_ttls_avp parse;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
+ " Phase 2", (unsigned long) wpabuf_len(in_buf));
+
+ if (data->pending_phase2_eap_resp) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 EAP response "
+ "- skip decryption and use old data");
+ eap_ttls_process_phase2_eap(
+ sm, data, wpabuf_head(data->pending_phase2_eap_resp),
+ wpabuf_len(data->pending_phase2_eap_resp));
+ wpabuf_free(data->pending_phase2_eap_resp);
+ data->pending_phase2_eap_resp = NULL;
+ return;
+ }
+
+ in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+ in_buf);
+ if (in_decrypted == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 "
+ "data");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (data->state == PHASE_FINISHED) {
+ if (wpabuf_len(in_decrypted) == 0 &&
+ tls_connection_ia_final_phase_finished(sm->ssl_ctx,
+ data->ssl.conn)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished "
+ "received");
+ eap_ttls_state(data, SUCCESS);
+ } else {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Did not receive valid "
+ "FinalPhaseFinished");
+ eap_ttls_state(data, FAILURE);
+ }
+
+ wpabuf_free(in_decrypted);
+ return;
+ }
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP",
+ in_decrypted);
+
+ if (eap_ttls_avp_parse(in_decrypted, &parse) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs");
+ wpabuf_free(in_decrypted);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (parse.user_name) {
+ os_free(sm->identity);
+ sm->identity = os_malloc(parse.user_name_len);
+ if (sm->identity) {
+ os_memcpy(sm->identity, parse.user_name,
+ parse.user_name_len);
+ sm->identity_len = parse.user_name_len;
+ }
+ if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1)
+ != 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not "
+ "found in the user database");
+ eap_ttls_state(data, FAILURE);
+ goto done;
+ }
+ }
+
+#ifdef EAP_SERVER_TNC
+ if (data->tnc_started && parse.eap == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: TNC started but no EAP "
+ "response from peer");
+ eap_ttls_state(data, FAILURE);
+ goto done;
+ }
+#endif /* EAP_SERVER_TNC */
+
+ if (parse.eap) {
+ eap_ttls_process_phase2_eap(sm, data, parse.eap,
+ parse.eap_len);
+ } else if (parse.user_password) {
+ eap_ttls_process_phase2_pap(sm, data, parse.user_password,
+ parse.user_password_len);
+ } else if (parse.chap_password) {
+ eap_ttls_process_phase2_chap(sm, data,
+ parse.chap_challenge,
+ parse.chap_challenge_len,
+ parse.chap_password,
+ parse.chap_password_len);
+ } else if (parse.mschap_response) {
+ eap_ttls_process_phase2_mschap(sm, data,
+ parse.mschap_challenge,
+ parse.mschap_challenge_len,
+ parse.mschap_response,
+ parse.mschap_response_len);
+ } else if (parse.mschap2_response) {
+ eap_ttls_process_phase2_mschapv2(sm, data,
+ parse.mschap_challenge,
+ parse.mschap_challenge_len,
+ parse.mschap2_response,
+ parse.mschap2_response_len);
+ }
+
+done:
+ wpabuf_free(in_decrypted);
+ os_free(parse.eap);
+}
+
+
+static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data)
+{
+#ifdef EAP_SERVER_TNC
+ if (!sm->tnc || data->state != SUCCESS || data->tnc_started)
+ return;
+
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Initialize TNC");
+ if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_TNC)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize TNC");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ data->tnc_started = 1;
+ eap_ttls_state(data, PHASE2_METHOD);
+#endif /* EAP_SERVER_TNC */
+}
+
+
+static int eap_ttls_process_version(struct eap_sm *sm, void *priv,
+ int peer_version)
+{
+ struct eap_ttls_data *data = priv;
+ if (peer_version < data->ttls_version) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; "
+ "use version %d",
+ peer_version, data->ttls_version, peer_version);
+ data->ttls_version = peer_version;
+ }
+
+ if (data->ttls_version > 0 && !data->tls_ia_configured) {
+ if (tls_connection_set_ia(sm->ssl_ctx, data->ssl.conn, 1)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to enable "
+ "TLS/IA");
+ return -1;
+ }
+ data->tls_ia_configured = 1;
+ }
+
+ return 0;
+}
+
+
+static void eap_ttls_process_msg(struct eap_sm *sm, void *priv,
+ const struct wpabuf *respData)
+{
+ struct eap_ttls_data *data = priv;
+
+ switch (data->state) {
+ case PHASE1:
+ if (eap_server_tls_phase1(sm, &data->ssl) < 0)
+ eap_ttls_state(data, FAILURE);
+ break;
+ case PHASE2_START:
+ case PHASE2_METHOD:
+ case PHASE_FINISHED:
+ eap_ttls_process_phase2(sm, data, data->ssl.tls_in);
+ eap_ttls_start_tnc(sm, data);
+ break;
+ case PHASE2_MSCHAPV2_RESP:
+ if (data->mschapv2_resp_ok && wpabuf_len(data->ssl.tls_in) ==
+ 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
+ "acknowledged response");
+ eap_ttls_state(data, data->ttls_version > 0 ?
+ PHASE_FINISHED : SUCCESS);
+ } else if (!data->mschapv2_resp_ok) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
+ "acknowledged error");
+ eap_ttls_state(data, FAILURE);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Unexpected "
+ "frame from peer (payload len %lu, "
+ "expected empty frame)",
+ (unsigned long)
+ wpabuf_len(data->ssl.tls_in));
+ eap_ttls_state(data, FAILURE);
+ }
+ eap_ttls_start_tnc(sm, data);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected state %d in %s",
+ data->state, __func__);
+ break;
+ }
+}
+
+
+static void eap_ttls_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_ttls_data *data = priv;
+ if (eap_server_tls_process(sm, &data->ssl, respData, data,
+ EAP_TYPE_TTLS, eap_ttls_process_version,
+ eap_ttls_process_msg) < 0)
+ eap_ttls_state(data, FAILURE);
+}
+
+
+static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_ttls_v1_derive_key(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+ struct tls_keys keys;
+ u8 *rnd, *key;
+
+ os_memset(&keys, 0, sizeof(keys));
+ if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
+ keys.client_random == NULL || keys.server_random == NULL ||
+ keys.inner_secret == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
+ "client random, or server random to derive keying "
+ "material");
+ return NULL;
+ }
+
+ rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+ key = os_malloc(EAP_TLS_KEY_LEN);
+ if (rnd == NULL || key == NULL) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
+ os_free(rnd);
+ os_free(key);
+ return NULL;
+ }
+ os_memcpy(rnd, keys.client_random, keys.client_random_len);
+ os_memcpy(rnd + keys.client_random_len, keys.server_random,
+ keys.server_random_len);
+
+ if (tls_prf(keys.inner_secret, keys.inner_secret_len,
+ "ttls v1 keying material", rnd, keys.client_random_len +
+ keys.server_random_len, key, EAP_TLS_KEY_LEN)) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
+ os_free(rnd);
+ os_free(key);
+ return NULL;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
+ rnd, keys.client_random_len + keys.server_random_len);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
+ keys.inner_secret, keys.inner_secret_len);
+
+ os_free(rnd);
+
+ return key;
+}
+
+
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_ttls_data *data = priv;
+ u8 *eapKeyData;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ if (data->ttls_version == 0) {
+ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+ "ttls keying material",
+ EAP_TLS_KEY_LEN);
+ } else {
+ eapKeyData = eap_ttls_v1_derive_key(sm, data);
+ }
+
+ if (eapKeyData) {
+ *len = EAP_TLS_KEY_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+ eapKeyData, EAP_TLS_KEY_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
+ }
+
+ return eapKeyData;
+}
+
+
+static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_ttls_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_ttls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_ttls_init;
+ eap->reset = eap_ttls_reset;
+ eap->buildReq = eap_ttls_buildReq;
+ eap->check = eap_ttls_check;
+ eap->process = eap_ttls_process;
+ eap->isDone = eap_ttls_isDone;
+ eap->getKey = eap_ttls_getKey;
+ eap->isSuccess = eap_ttls_isSuccess;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_vendor_test.c b/src/eap_server/eap_server_vendor_test.c
new file mode 100644
index 0000000..0dd0aca
--- /dev/null
+++ b/src/eap_server/eap_server_vendor_test.c
@@ -0,0 +1,198 @@
+/*
+ * hostapd / Test method for vendor specific (expanded) EAP type
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+#define EAP_VENDOR_ID 0xfffefd
+#define EAP_VENDOR_TYPE 0xfcfbfaf9
+
+
+struct eap_vendor_test_data {
+ enum { INIT, CONFIRM, SUCCESS, FAILURE } state;
+};
+
+
+static const char * eap_vendor_test_state_txt(int state)
+{
+ switch (state) {
+ case INIT:
+ return "INIT";
+ case CONFIRM:
+ return "CONFIRM";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "?";
+ }
+}
+
+
+static void eap_vendor_test_state(struct eap_vendor_test_data *data,
+ int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: %s -> %s",
+ eap_vendor_test_state_txt(data->state),
+ eap_vendor_test_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_vendor_test_init(struct eap_sm *sm)
+{
+ struct eap_vendor_test_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = INIT;
+
+ return data;
+}
+
+
+static void eap_vendor_test_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_vendor_test_data *data = priv;
+ os_free(data);
+}
+
+
+static struct wpabuf * eap_vendor_test_buildReq(struct eap_sm *sm, void *priv,
+ u8 id)
+{
+ struct eap_vendor_test_data *data = priv;
+ struct wpabuf *req;
+
+ req = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1,
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ wpa_printf(MSG_ERROR, "EAP-VENDOR-TEST: Failed to allocate "
+ "memory for request");
+ return NULL;
+ }
+
+ wpabuf_put_u8(req, data->state == INIT ? 1 : 3);
+
+ return req;
+}
+
+
+static Boolean eap_vendor_test_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len);
+ if (pos == NULL || len < 1) {
+ wpa_printf(MSG_INFO, "EAP-VENDOR-TEST: Invalid frame");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void eap_vendor_test_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_vendor_test_data *data = priv;
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len);
+ if (pos == NULL || len < 1)
+ return;
+
+ if (data->state == INIT) {
+ if (*pos == 2)
+ eap_vendor_test_state(data, CONFIRM);
+ else
+ eap_vendor_test_state(data, FAILURE);
+ } else if (data->state == CONFIRM) {
+ if (*pos == 4)
+ eap_vendor_test_state(data, SUCCESS);
+ else
+ eap_vendor_test_state(data, FAILURE);
+ } else
+ eap_vendor_test_state(data, FAILURE);
+}
+
+
+static Boolean eap_vendor_test_isDone(struct eap_sm *sm, void *priv)
+{
+ struct eap_vendor_test_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_vendor_test_data *data = priv;
+ u8 *key;
+ const int key_len = 64;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(key_len);
+ if (key == NULL)
+ return NULL;
+
+ os_memset(key, 0x11, key_len / 2);
+ os_memset(key + key_len / 2, 0x22, key_len / 2);
+ *len = key_len;
+
+ return key;
+}
+
+
+static Boolean eap_vendor_test_isSuccess(struct eap_sm *sm, void *priv)
+{
+ struct eap_vendor_test_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+int eap_server_vendor_test_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_ID, EAP_VENDOR_TYPE,
+ "VENDOR-TEST");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_vendor_test_init;
+ eap->reset = eap_vendor_test_reset;
+ eap->buildReq = eap_vendor_test_buildReq;
+ eap->check = eap_vendor_test_check;
+ eap->process = eap_vendor_test_process;
+ eap->isDone = eap_vendor_test_isDone;
+ eap->getKey = eap_vendor_test_getKey;
+ eap->isSuccess = eap_vendor_test_isSuccess;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c
new file mode 100644
index 0000000..e944a4d
--- /dev/null
+++ b/src/eap_server/eap_server_wsc.c
@@ -0,0 +1,517 @@
+/*
+ * EAP-WSC server for Wi-Fi Protected Setup
+ * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "eap_i.h"
+#include "eap_common/eap_wsc_common.h"
+#include "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 */
+ 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/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c
new file mode 100644
index 0000000..248b216
--- /dev/null
+++ b/src/eap_server/eap_sim_db.c
@@ -0,0 +1,1338 @@
+/*
+ * hostapd / EAP-SIM database/authenticator gateway
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This is an example implementation of the EAP-SIM/AKA database/authentication
+ * gateway interface that is using an external program as an SS7 gateway to
+ * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example
+ * implementation of such a gateway program. This eap_sim_db.c takes care of
+ * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different
+ * gateway implementations for HLR/AuC access. Alternatively, it can also be
+ * completely replaced if the in-memory database of pseudonyms/re-auth
+ * identities is not suitable for some cases.
+ */
+
+#include "includes.h"
+#include <sys/un.h>
+
+#include "common.h"
+#include "crypto/random.h"
+#include "eap_common/eap_sim_common.h"
+#include "eap_server/eap_sim_db.h"
+#include "eloop.h"
+
+struct eap_sim_pseudonym {
+ struct eap_sim_pseudonym *next;
+ u8 *identity;
+ size_t identity_len;
+ char *pseudonym;
+};
+
+struct eap_sim_db_pending {
+ struct eap_sim_db_pending *next;
+ u8 imsi[20];
+ size_t imsi_len;
+ enum { PENDING, SUCCESS, FAILURE } state;
+ void *cb_session_ctx;
+ struct os_time timestamp;
+ int aka;
+ union {
+ struct {
+ u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
+ u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
+ u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
+ int num_chal;
+ } sim;
+ struct {
+ u8 rand[EAP_AKA_RAND_LEN];
+ u8 autn[EAP_AKA_AUTN_LEN];
+ u8 ik[EAP_AKA_IK_LEN];
+ u8 ck[EAP_AKA_CK_LEN];
+ u8 res[EAP_AKA_RES_MAX_LEN];
+ size_t res_len;
+ } aka;
+ } u;
+};
+
+struct eap_sim_db_data {
+ int sock;
+ char *fname;
+ char *local_sock;
+ void (*get_complete_cb)(void *ctx, void *session_ctx);
+ void *ctx;
+ struct eap_sim_pseudonym *pseudonyms;
+ struct eap_sim_reauth *reauths;
+ struct eap_sim_db_pending *pending;
+};
+
+
+static struct eap_sim_db_pending *
+eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi,
+ size_t imsi_len, int aka)
+{
+ struct eap_sim_db_pending *entry, *prev = NULL;
+
+ entry = data->pending;
+ while (entry) {
+ if (entry->aka == aka && entry->imsi_len == imsi_len &&
+ os_memcmp(entry->imsi, imsi, imsi_len) == 0) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ data->pending = entry->next;
+ break;
+ }
+ prev = entry;
+ entry = entry->next;
+ }
+ return entry;
+}
+
+
+static void eap_sim_db_add_pending(struct eap_sim_db_data *data,
+ struct eap_sim_db_pending *entry)
+{
+ entry->next = data->pending;
+ data->pending = entry;
+}
+
+
+static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
+ const char *imsi, char *buf)
+{
+ char *start, *end, *pos;
+ struct eap_sim_db_pending *entry;
+ int num_chal;
+
+ /*
+ * SIM-RESP-AUTH <IMSI> Kc(i):SRES(i):RAND(i) ...
+ * SIM-RESP-AUTH <IMSI> FAILURE
+ * (IMSI = ASCII string, Kc/SRES/RAND = hex string)
+ */
+
+ entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0);
+ if (entry == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
+ "received message found");
+ return;
+ }
+
+ start = buf;
+ if (os_strncmp(start, "FAILURE", 7) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
+ "failure");
+ entry->state = FAILURE;
+ eap_sim_db_add_pending(data, entry);
+ data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+ return;
+ }
+
+ num_chal = 0;
+ while (num_chal < EAP_SIM_MAX_CHAL) {
+ end = os_strchr(start, ' ');
+ if (end)
+ *end = '\0';
+
+ pos = os_strchr(start, ':');
+ if (pos == NULL)
+ goto parse_fail;
+ *pos = '\0';
+ if (hexstr2bin(start, entry->u.sim.kc[num_chal],
+ EAP_SIM_KC_LEN))
+ goto parse_fail;
+
+ start = pos + 1;
+ pos = os_strchr(start, ':');
+ if (pos == NULL)
+ goto parse_fail;
+ *pos = '\0';
+ if (hexstr2bin(start, entry->u.sim.sres[num_chal],
+ EAP_SIM_SRES_LEN))
+ goto parse_fail;
+
+ start = pos + 1;
+ if (hexstr2bin(start, entry->u.sim.rand[num_chal],
+ GSM_RAND_LEN))
+ goto parse_fail;
+
+ num_chal++;
+ if (end == NULL)
+ break;
+ else
+ start = end + 1;
+ }
+ entry->u.sim.num_chal = num_chal;
+
+ entry->state = SUCCESS;
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
+ "successfully - callback");
+ eap_sim_db_add_pending(data, entry);
+ data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+ return;
+
+parse_fail:
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
+ os_free(entry);
+}
+
+
+static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
+ const char *imsi, char *buf)
+{
+ char *start, *end;
+ struct eap_sim_db_pending *entry;
+
+ /*
+ * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
+ * AKA-RESP-AUTH <IMSI> FAILURE
+ * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string)
+ */
+
+ entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1);
+ if (entry == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
+ "received message found");
+ return;
+ }
+
+ start = buf;
+ if (os_strncmp(start, "FAILURE", 7) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
+ "failure");
+ entry->state = FAILURE;
+ eap_sim_db_add_pending(data, entry);
+ data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+ return;
+ }
+
+ end = os_strchr(start, ' ');
+ if (end == NULL)
+ goto parse_fail;
+ *end = '\0';
+ if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN))
+ goto parse_fail;
+
+ start = end + 1;
+ end = os_strchr(start, ' ');
+ if (end == NULL)
+ goto parse_fail;
+ *end = '\0';
+ if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN))
+ goto parse_fail;
+
+ start = end + 1;
+ end = os_strchr(start, ' ');
+ if (end == NULL)
+ goto parse_fail;
+ *end = '\0';
+ if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN))
+ goto parse_fail;
+
+ start = end + 1;
+ end = os_strchr(start, ' ');
+ if (end == NULL)
+ goto parse_fail;
+ *end = '\0';
+ if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN))
+ goto parse_fail;
+
+ start = end + 1;
+ end = os_strchr(start, ' ');
+ if (end)
+ *end = '\0';
+ else {
+ end = start;
+ while (*end)
+ end++;
+ }
+ entry->u.aka.res_len = (end - start) / 2;
+ if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES");
+ entry->u.aka.res_len = 0;
+ goto parse_fail;
+ }
+ if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len))
+ goto parse_fail;
+
+ entry->state = SUCCESS;
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
+ "successfully - callback");
+ eap_sim_db_add_pending(data, entry);
+ data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+ return;
+
+parse_fail:
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
+ os_free(entry);
+}
+
+
+static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct eap_sim_db_data *data = eloop_ctx;
+ char buf[1000], *pos, *cmd, *imsi;
+ int res;
+
+ res = recv(sock, buf, sizeof(buf), 0);
+ if (res < 0)
+ return;
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
+ "external source", (u8 *) buf, res);
+ if (res == 0)
+ return;
+ if (res >= (int) sizeof(buf))
+ res = sizeof(buf) - 1;
+ buf[res] = '\0';
+
+ if (data->get_complete_cb == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
+ "registered");
+ return;
+ }
+
+ /* <cmd> <IMSI> ... */
+
+ cmd = buf;
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ goto parse_fail;
+ *pos = '\0';
+ imsi = pos + 1;
+ pos = os_strchr(imsi, ' ');
+ if (pos == NULL)
+ goto parse_fail;
+ *pos = '\0';
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s",
+ cmd, imsi);
+
+ if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0)
+ eap_sim_db_sim_resp_auth(data, imsi, pos + 1);
+ else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0)
+ eap_sim_db_aka_resp_auth(data, imsi, pos + 1);
+ else
+ wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response "
+ "'%s'", cmd);
+ return;
+
+parse_fail:
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
+}
+
+
+static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
+{
+ struct sockaddr_un addr;
+ static int counter = 0;
+
+ if (os_strncmp(data->fname, "unix:", 5) != 0)
+ return -1;
+
+ data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (data->sock < 0) {
+ perror("socket(eap_sim_db)");
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_snprintf(addr.sun_path, sizeof(addr.sun_path),
+ "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
+ data->local_sock = os_strdup(addr.sun_path);
+ if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind(eap_sim_db)");
+ close(data->sock);
+ data->sock = -1;
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path));
+ if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("connect(eap_sim_db)");
+ wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket",
+ (u8 *) addr.sun_path,
+ os_strlen(addr.sun_path));
+ close(data->sock);
+ data->sock = -1;
+ return -1;
+ }
+
+ eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
+
+ return 0;
+}
+
+
+static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
+{
+ if (data->sock >= 0) {
+ eloop_unregister_read_sock(data->sock);
+ close(data->sock);
+ data->sock = -1;
+ }
+ if (data->local_sock) {
+ unlink(data->local_sock);
+ os_free(data->local_sock);
+ data->local_sock = NULL;
+ }
+}
+
+
+/**
+ * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
+ * @config: Configuration data (e.g., file name)
+ * @get_complete_cb: Callback function for reporting availability of triplets
+ * @ctx: Context pointer for get_complete_cb
+ * Returns: Pointer to a private data structure or %NULL on failure
+ */
+void * eap_sim_db_init(const char *config,
+ void (*get_complete_cb)(void *ctx, void *session_ctx),
+ void *ctx)
+{
+ struct eap_sim_db_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->sock = -1;
+ data->get_complete_cb = get_complete_cb;
+ data->ctx = ctx;
+ data->fname = os_strdup(config);
+ if (data->fname == NULL)
+ goto fail;
+
+ if (os_strncmp(data->fname, "unix:", 5) == 0) {
+ if (eap_sim_db_open_socket(data))
+ goto fail;
+ }
+
+ return data;
+
+fail:
+ eap_sim_db_close_socket(data);
+ os_free(data->fname);
+ os_free(data);
+ return NULL;
+}
+
+
+static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
+{
+ os_free(p->identity);
+ os_free(p->pseudonym);
+ os_free(p);
+}
+
+
+static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
+{
+ os_free(r->identity);
+ os_free(r->reauth_id);
+ os_free(r);
+}
+
+
+/**
+ * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
+ * @priv: Private data pointer from eap_sim_db_init()
+ */
+void eap_sim_db_deinit(void *priv)
+{
+ struct eap_sim_db_data *data = priv;
+ struct eap_sim_pseudonym *p, *prev;
+ struct eap_sim_reauth *r, *prevr;
+ struct eap_sim_db_pending *pending, *prev_pending;
+
+ eap_sim_db_close_socket(data);
+ os_free(data->fname);
+
+ p = data->pseudonyms;
+ while (p) {
+ prev = p;
+ p = p->next;
+ eap_sim_db_free_pseudonym(prev);
+ }
+
+ r = data->reauths;
+ while (r) {
+ prevr = r;
+ r = r->next;
+ eap_sim_db_free_reauth(prevr);
+ }
+
+ pending = data->pending;
+ while (pending) {
+ prev_pending = pending;
+ pending = pending->next;
+ os_free(prev_pending);
+ }
+
+ os_free(data);
+}
+
+
+static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
+ size_t len)
+{
+ int _errno = 0;
+
+ if (send(data->sock, msg, len, 0) < 0) {
+ _errno = errno;
+ perror("send[EAP-SIM DB UNIX]");
+ }
+
+ if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
+ _errno == ECONNREFUSED) {
+ /* Try to reconnect */
+ eap_sim_db_close_socket(data);
+ if (eap_sim_db_open_socket(data) < 0)
+ return -1;
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
+ "external server");
+ if (send(data->sock, msg, len, 0) < 0) {
+ perror("send[EAP-SIM DB UNIX]");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
+{
+ /* TODO: add limit for maximum length for pending list; remove latest
+ * (i.e., last) entry from the list if the limit is reached; could also
+ * use timeout to expire pending entries */
+}
+
+
+/**
+ * eap_sim_db_get_gsm_triplets - Get GSM triplets
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: User name identity
+ * @identity_len: Length of identity in bytes
+ * @max_chal: Maximum number of triplets
+ * @_rand: Buffer for RAND values
+ * @kc: Buffer for Kc values
+ * @sres: Buffer for SRES values
+ * @cb_session_ctx: Session callback context for get_complete_cb()
+ * Returns: Number of triplets received (has to be less than or equal to
+ * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or
+ * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the
+ * callback function registered with eap_sim_db_init() will be called once the
+ * results become available.
+ *
+ * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
+ * ASCII format.
+ *
+ * When using an external server for GSM triplets, this function can always
+ * start a request and return EAP_SIM_DB_PENDING immediately if authentication
+ * triplets are not available. Once the triplets are received, callback
+ * function registered with eap_sim_db_init() is called to notify EAP state
+ * machine to reprocess the message. This eap_sim_db_get_gsm_triplets()
+ * function will then be called again and the newly received triplets will then
+ * be given to the caller.
+ */
+int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
+ size_t identity_len, int max_chal,
+ u8 *_rand, u8 *kc, u8 *sres,
+ void *cb_session_ctx)
+{
+ struct eap_sim_db_data *data = priv;
+ struct eap_sim_db_pending *entry;
+ int len, ret;
+ size_t i;
+ char msg[40];
+
+ if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+ identity, identity_len);
+ return EAP_SIM_DB_FAILURE;
+ }
+ identity++;
+ identity_len--;
+ for (i = 0; i < identity_len; i++) {
+ if (identity[i] == '@') {
+ identity_len = i;
+ break;
+ }
+ }
+ if (identity_len + 1 > sizeof(entry->imsi)) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+ identity, identity_len);
+ return EAP_SIM_DB_FAILURE;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
+ identity, identity_len);
+
+ entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
+ if (entry) {
+ int num_chal;
+ if (entry->state == FAILURE) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
+ "failure");
+ os_free(entry);
+ return EAP_SIM_DB_FAILURE;
+ }
+
+ if (entry->state == PENDING) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
+ "still pending");
+ eap_sim_db_add_pending(data, entry);
+ return EAP_SIM_DB_PENDING;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
+ "%d challenges", entry->u.sim.num_chal);
+ num_chal = entry->u.sim.num_chal;
+ if (num_chal > max_chal)
+ num_chal = max_chal;
+ os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN);
+ os_memcpy(sres, entry->u.sim.sres,
+ num_chal * EAP_SIM_SRES_LEN);
+ os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
+ os_free(entry);
+ return num_chal;
+ }
+
+ if (data->sock < 0) {
+ if (eap_sim_db_open_socket(data) < 0)
+ return EAP_SIM_DB_FAILURE;
+ }
+
+ len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
+ if (len < 0 || len + identity_len >= sizeof(msg))
+ return EAP_SIM_DB_FAILURE;
+ os_memcpy(msg + len, identity, identity_len);
+ len += identity_len;
+ ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
+ if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
+ return EAP_SIM_DB_FAILURE;
+ len += ret;
+
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
+ "data for IMSI", identity, identity_len);
+ if (eap_sim_db_send(data, msg, len) < 0)
+ return EAP_SIM_DB_FAILURE;
+
+ entry = os_zalloc(sizeof(*entry));
+ if (entry == NULL)
+ return EAP_SIM_DB_FAILURE;
+
+ os_get_time(&entry->timestamp);
+ os_memcpy(entry->imsi, identity, identity_len);
+ entry->imsi_len = identity_len;
+ entry->cb_session_ctx = cb_session_ctx;
+ entry->state = PENDING;
+ eap_sim_db_add_pending(data, entry);
+ eap_sim_db_expire_pending(data);
+
+ return EAP_SIM_DB_PENDING;
+}
+
+
+static struct eap_sim_pseudonym *
+eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
+ size_t identity_len)
+{
+ char *pseudonym;
+ size_t len;
+ struct eap_sim_pseudonym *p;
+
+ if (identity_len == 0 ||
+ (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
+ identity[0] != EAP_AKA_PSEUDONYM_PREFIX))
+ return NULL;
+
+ /* Remove possible realm from identity */
+ len = 0;
+ while (len < identity_len) {
+ if (identity[len] == '@')
+ break;
+ len++;
+ }
+
+ pseudonym = os_malloc(len + 1);
+ if (pseudonym == NULL)
+ return NULL;
+ os_memcpy(pseudonym, identity, len);
+ pseudonym[len] = '\0';
+
+ p = data->pseudonyms;
+ while (p) {
+ if (os_strcmp(p->pseudonym, pseudonym) == 0)
+ break;
+ p = p->next;
+ }
+
+ os_free(pseudonym);
+
+ return p;
+}
+
+
+static struct eap_sim_pseudonym *
+eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
+ size_t identity_len)
+{
+ struct eap_sim_pseudonym *p;
+
+ if (identity_len == 0 ||
+ (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
+ identity[0] != EAP_AKA_PERMANENT_PREFIX))
+ return NULL;
+
+ p = data->pseudonyms;
+ while (p) {
+ if (identity_len == p->identity_len &&
+ os_memcmp(p->identity, identity, identity_len) == 0)
+ break;
+ p = p->next;
+ }
+
+ return p;
+}
+
+
+static struct eap_sim_reauth *
+eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
+ size_t identity_len)
+{
+ char *reauth_id;
+ size_t len;
+ struct eap_sim_reauth *r;
+
+ if (identity_len == 0 ||
+ (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
+ identity[0] != EAP_AKA_REAUTH_ID_PREFIX))
+ return NULL;
+
+ /* Remove possible realm from identity */
+ len = 0;
+ while (len < identity_len) {
+ if (identity[len] == '@')
+ break;
+ len++;
+ }
+
+ reauth_id = os_malloc(len + 1);
+ if (reauth_id == NULL)
+ return NULL;
+ os_memcpy(reauth_id, identity, len);
+ reauth_id[len] = '\0';
+
+ r = data->reauths;
+ while (r) {
+ if (os_strcmp(r->reauth_id, reauth_id) == 0)
+ break;
+ r = r->next;
+ }
+
+ os_free(reauth_id);
+
+ return r;
+}
+
+
+static struct eap_sim_reauth *
+eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
+ size_t identity_len)
+{
+ struct eap_sim_pseudonym *p;
+ struct eap_sim_reauth *r;
+
+ if (identity_len == 0)
+ return NULL;
+
+ p = eap_sim_db_get_pseudonym(data, identity, identity_len);
+ if (p == NULL)
+ p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
+ if (p) {
+ identity = p->identity;
+ identity_len = p->identity_len;
+ }
+
+ r = data->reauths;
+ while (r) {
+ if (identity_len == r->identity_len &&
+ os_memcmp(r->identity, identity, identity_len) == 0)
+ break;
+ r = r->next;
+ }
+
+ return r;
+}
+
+
+/**
+ * eap_sim_db_identity_known - Verify whether the given identity is known
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: User name identity
+ * @identity_len: Length of identity in bytes
+ * Returns: 0 if the user is found or -1 on failure
+ *
+ * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the
+ * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id.
+ */
+int eap_sim_db_identity_known(void *priv, const u8 *identity,
+ size_t identity_len)
+{
+ struct eap_sim_db_data *data = priv;
+
+ if (identity == NULL || identity_len < 2)
+ return -1;
+
+ if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
+ identity[0] == EAP_AKA_PSEUDONYM_PREFIX) {
+ struct eap_sim_pseudonym *p =
+ eap_sim_db_get_pseudonym(data, identity, identity_len);
+ return p ? 0 : -1;
+ }
+
+ if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
+ identity[0] == EAP_AKA_REAUTH_ID_PREFIX) {
+ struct eap_sim_reauth *r =
+ eap_sim_db_get_reauth(data, identity, identity_len);
+ return r ? 0 : -1;
+ }
+
+ if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
+ identity[0] != EAP_AKA_PERMANENT_PREFIX) {
+ /* Unknown identity prefix */
+ return -1;
+ }
+
+ /* TODO: Should consider asking HLR/AuC gateway whether this permanent
+ * identity is known. If it is, EAP-SIM/AKA can skip identity request.
+ * In case of EAP-AKA, this would reduce number of needed round-trips.
+ * Ideally, this would be done with one wait, i.e., just request
+ * authentication data and store it for the next use. This would then
+ * need to use similar pending-request functionality as the normal
+ * request for authentication data at later phase.
+ */
+ return -1;
+}
+
+
+static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
+{
+ char *id, *pos, *end;
+ u8 buf[10];
+
+ if (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
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @aka: Using EAP-AKA instead of EAP-SIM
+ * Returns: Next pseudonym (allocated string) or %NULL on failure
+ *
+ * This function is used to generate a pseudonym for EAP-SIM. The returned
+ * pseudonym is not added to database at this point; it will need to be added
+ * with eap_sim_db_add_pseudonym() once the authentication has been completed
+ * successfully. Caller is responsible for freeing the returned buffer.
+ */
+char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
+{
+ struct eap_sim_db_data *data = priv;
+ return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX :
+ EAP_SIM_PSEUDONYM_PREFIX);
+}
+
+
+/**
+ * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @aka: Using EAP-AKA instead of EAP-SIM
+ * Returns: Next reauth_id (allocated string) or %NULL on failure
+ *
+ * This function is used to generate a fast re-authentication identity for
+ * EAP-SIM. The returned reauth_id is not added to database at this point; it
+ * will need to be added with eap_sim_db_add_reauth() once the authentication
+ * has been completed successfully. Caller is responsible for freeing the
+ * returned buffer.
+ */
+char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
+{
+ struct eap_sim_db_data *data = priv;
+ return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX :
+ EAP_SIM_REAUTH_ID_PREFIX);
+}
+
+
+/**
+ * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: Identity of the user (may be permanent identity or pseudonym)
+ * @identity_len: Length of identity
+ * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
+ * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
+ * free it.
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
+ * responsible of freeing pseudonym buffer once it is not needed anymore.
+ */
+int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
+ size_t identity_len, char *pseudonym)
+{
+ struct eap_sim_db_data *data = priv;
+ struct eap_sim_pseudonym *p;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
+ identity, identity_len);
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
+
+ /* TODO: could store last two pseudonyms */
+ p = eap_sim_db_get_pseudonym(data, identity, identity_len);
+ if (p == NULL)
+ p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
+
+ if (p) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
+ "pseudonym: %s", p->pseudonym);
+ os_free(p->pseudonym);
+ p->pseudonym = pseudonym;
+ return 0;
+ }
+
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL) {
+ os_free(pseudonym);
+ return -1;
+ }
+
+ p->next = data->pseudonyms;
+ p->identity = os_malloc(identity_len);
+ if (p->identity == NULL) {
+ os_free(p);
+ os_free(pseudonym);
+ return -1;
+ }
+ os_memcpy(p->identity, identity, identity_len);
+ p->identity_len = identity_len;
+ p->pseudonym = pseudonym;
+ data->pseudonyms = p;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
+ return 0;
+}
+
+
+static struct eap_sim_reauth *
+eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
+ size_t identity_len, char *reauth_id, u16 counter)
+{
+ struct eap_sim_reauth *r;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
+ identity, identity_len);
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
+
+ r = eap_sim_db_get_reauth(data, identity, identity_len);
+ if (r == NULL)
+ r = eap_sim_db_get_reauth_id(data, identity, identity_len);
+
+ if (r) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
+ "reauth_id: %s", r->reauth_id);
+ os_free(r->reauth_id);
+ r->reauth_id = reauth_id;
+ } else {
+ r = os_zalloc(sizeof(*r));
+ if (r == NULL) {
+ os_free(reauth_id);
+ return NULL;
+ }
+
+ r->next = data->reauths;
+ r->identity = os_malloc(identity_len);
+ if (r->identity == NULL) {
+ os_free(r);
+ os_free(reauth_id);
+ return NULL;
+ }
+ os_memcpy(r->identity, identity, identity_len);
+ r->identity_len = identity_len;
+ r->reauth_id = reauth_id;
+ data->reauths = r;
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
+ }
+
+ r->counter = counter;
+
+ return r;
+}
+
+
+/**
+ * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: Identity of the user (may be permanent identity or pseudonym)
+ * @identity_len: Length of identity
+ * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
+ * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
+ * free it.
+ * @counter: AT_COUNTER value for fast re-authentication
+ * @mk: 16-byte MK from the previous full authentication or %NULL
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds a new re-authentication entry for an EAP-SIM user.
+ * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
+ * anymore.
+ */
+int eap_sim_db_add_reauth(void *priv, const u8 *identity,
+ size_t identity_len, char *reauth_id, u16 counter,
+ const u8 *mk)
+{
+ struct eap_sim_db_data *data = priv;
+ struct eap_sim_reauth *r;
+
+ r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
+ counter);
+ if (r == NULL)
+ return -1;
+
+ os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
+ r->aka_prime = 0;
+
+ return 0;
+}
+
+
+#ifdef EAP_SERVER_AKA_PRIME
+/**
+ * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: Identity of the user (may be permanent identity or pseudonym)
+ * @identity_len: Length of identity
+ * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
+ * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
+ * free it.
+ * @counter: AT_COUNTER value for fast re-authentication
+ * @k_encr: K_encr from the previous full authentication
+ * @k_aut: K_aut from the previous full authentication
+ * @k_re: 32-byte K_re from the previous full authentication
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds a new re-authentication entry for an EAP-AKA' user.
+ * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
+ * anymore.
+ */
+int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
+ size_t identity_len, char *reauth_id,
+ u16 counter, const u8 *k_encr, const u8 *k_aut,
+ const u8 *k_re)
+{
+ struct eap_sim_db_data *data = priv;
+ struct eap_sim_reauth *r;
+
+ r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
+ counter);
+ if (r == NULL)
+ return -1;
+
+ r->aka_prime = 1;
+ os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
+ os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
+ os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
+
+ return 0;
+}
+#endif /* EAP_SERVER_AKA_PRIME */
+
+
+/**
+ * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: Identity of the user (may be permanent identity or pseudonym)
+ * @identity_len: Length of identity
+ * @len: Buffer for length of the returned permanent identity
+ * Returns: Pointer to the permanent identity, or %NULL if not found
+ */
+const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
+ size_t identity_len, size_t *len)
+{
+ struct eap_sim_db_data *data = priv;
+ struct eap_sim_pseudonym *p;
+
+ if (identity == NULL)
+ return NULL;
+
+ p = eap_sim_db_get_pseudonym(data, identity, identity_len);
+ if (p == NULL)
+ p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
+ if (p == NULL)
+ return NULL;
+
+ *len = p->identity_len;
+ return p->identity;
+}
+
+
+/**
+ * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: Identity of the user (may be permanent identity, pseudonym, or
+ * reauth_id)
+ * @identity_len: Length of identity
+ * Returns: Pointer to the re-auth entry, or %NULL if not found
+ */
+struct eap_sim_reauth *
+eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
+ size_t identity_len)
+{
+ struct eap_sim_db_data *data = priv;
+ struct eap_sim_reauth *r;
+
+ if (identity == NULL)
+ return NULL;
+ r = eap_sim_db_get_reauth(data, identity, identity_len);
+ if (r == NULL)
+ r = eap_sim_db_get_reauth_id(data, identity, identity_len);
+ return r;
+}
+
+
+/**
+ * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @reauth: Pointer to re-authentication entry from
+ * eap_sim_db_get_reauth_entry()
+ */
+void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
+{
+ struct eap_sim_db_data *data = priv;
+ struct eap_sim_reauth *r, *prev = NULL;
+ r = data->reauths;
+ while (r) {
+ if (r == reauth) {
+ if (prev)
+ prev->next = r->next;
+ else
+ data->reauths = r->next;
+ eap_sim_db_free_reauth(r);
+ return;
+ }
+ prev = r;
+ r = r->next;
+ }
+}
+
+
+/**
+ * eap_sim_db_get_aka_auth - Get AKA authentication values
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: User name identity
+ * @identity_len: Length of identity in bytes
+ * @_rand: Buffer for RAND value
+ * @autn: Buffer for AUTN value
+ * @ik: Buffer for IK value
+ * @ck: Buffer for CK value
+ * @res: Buffer for RES value
+ * @res_len: Buffer for RES length
+ * @cb_session_ctx: Session callback context for get_complete_cb()
+ * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
+ * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
+ * case, the callback function registered with eap_sim_db_init() will be
+ * called once the results become available.
+ *
+ * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
+ * ASCII format.
+ *
+ * When using an external server for AKA authentication, this function can
+ * always start a request and return EAP_SIM_DB_PENDING immediately if
+ * authentication triplets are not available. Once the authentication data are
+ * received, callback function registered with eap_sim_db_init() is called to
+ * notify EAP state machine to reprocess the message. This
+ * eap_sim_db_get_aka_auth() function will then be called again and the newly
+ * received triplets will then be given to the caller.
+ */
+int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
+ size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
+ u8 *ck, u8 *res, size_t *res_len,
+ void *cb_session_ctx)
+{
+ struct eap_sim_db_data *data = priv;
+ struct eap_sim_db_pending *entry;
+ int len;
+ size_t i;
+ char msg[40];
+
+ if (identity_len < 2 || identity == NULL ||
+ identity[0] != EAP_AKA_PERMANENT_PREFIX) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+ identity, identity_len);
+ return EAP_SIM_DB_FAILURE;
+ }
+ identity++;
+ identity_len--;
+ for (i = 0; i < identity_len; i++) {
+ if (identity[i] == '@') {
+ identity_len = i;
+ break;
+ }
+ }
+ if (identity_len + 1 > sizeof(entry->imsi)) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+ identity, identity_len);
+ return EAP_SIM_DB_FAILURE;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
+ identity, identity_len);
+
+ entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
+ if (entry) {
+ if (entry->state == FAILURE) {
+ os_free(entry);
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
+ return EAP_SIM_DB_FAILURE;
+ }
+
+ if (entry->state == PENDING) {
+ eap_sim_db_add_pending(data, entry);
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
+ return EAP_SIM_DB_PENDING;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
+ "received authentication data");
+ os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
+ os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
+ os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
+ os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
+ os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
+ *res_len = entry->u.aka.res_len;
+ os_free(entry);
+ return 0;
+ }
+
+ if (data->sock < 0) {
+ if (eap_sim_db_open_socket(data) < 0)
+ return EAP_SIM_DB_FAILURE;
+ }
+
+ len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
+ if (len < 0 || len + identity_len >= sizeof(msg))
+ return EAP_SIM_DB_FAILURE;
+ os_memcpy(msg + len, identity, identity_len);
+ len += identity_len;
+
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
+ "data for IMSI", identity, identity_len);
+ if (eap_sim_db_send(data, msg, len) < 0)
+ return EAP_SIM_DB_FAILURE;
+
+ entry = os_zalloc(sizeof(*entry));
+ if (entry == NULL)
+ return EAP_SIM_DB_FAILURE;
+
+ os_get_time(&entry->timestamp);
+ entry->aka = 1;
+ os_memcpy(entry->imsi, identity, identity_len);
+ entry->imsi_len = identity_len;
+ entry->cb_session_ctx = cb_session_ctx;
+ entry->state = PENDING;
+ eap_sim_db_add_pending(data, entry);
+ eap_sim_db_expire_pending(data);
+
+ return EAP_SIM_DB_PENDING;
+}
+
+
+/**
+ * eap_sim_db_resynchronize - Resynchronize AKA AUTN
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: User name identity
+ * @identity_len: Length of identity in bytes
+ * @auts: AUTS value from the peer
+ * @_rand: RAND value used in the rejected message
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when the peer reports synchronization failure in the
+ * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
+ * HLR/AuC to allow it to resynchronize with the peer. After this,
+ * eap_sim_db_get_aka_auth() will be called again to to fetch updated
+ * RAND/AUTN values for the next challenge.
+ */
+int eap_sim_db_resynchronize(void *priv, const u8 *identity,
+ size_t identity_len, const u8 *auts,
+ const u8 *_rand)
+{
+ struct eap_sim_db_data *data = priv;
+ size_t i;
+
+ if (identity_len < 2 || identity == NULL ||
+ identity[0] != EAP_AKA_PERMANENT_PREFIX) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+ identity, identity_len);
+ return -1;
+ }
+ identity++;
+ identity_len--;
+ for (i = 0; i < identity_len; i++) {
+ if (identity[i] == '@') {
+ identity_len = i;
+ break;
+ }
+ }
+ if (identity_len > 20) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+ identity, identity_len);
+ return -1;
+ }
+
+ if (data->sock >= 0) {
+ char msg[100];
+ int len, ret;
+
+ len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
+ if (len < 0 || len + identity_len >= sizeof(msg))
+ return -1;
+ os_memcpy(msg + len, identity, identity_len);
+ len += identity_len;
+
+ ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
+ if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
+ return -1;
+ len += ret;
+ len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
+ auts, EAP_AKA_AUTS_LEN);
+ ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
+ if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
+ return -1;
+ len += ret;
+ len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
+ _rand, EAP_AKA_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
+ "IMSI", identity, identity_len);
+ if (eap_sim_db_send(data, msg, len) < 0)
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/eap_server/eap_sim_db.h b/src/eap_server/eap_sim_db.h
new file mode 100644
index 0000000..ab89ae9
--- /dev/null
+++ b/src/eap_server/eap_sim_db.h
@@ -0,0 +1,91 @@
+/*
+ * hostapd / EAP-SIM database/authenticator gateway
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_SIM_DB_H
+#define EAP_SIM_DB_H
+
+#include "eap_common/eap_sim_common.h"
+
+/* Identity prefixes */
+#define EAP_SIM_PERMANENT_PREFIX '1'
+#define EAP_SIM_PSEUDONYM_PREFIX '3'
+#define EAP_SIM_REAUTH_ID_PREFIX '5'
+#define EAP_AKA_PERMANENT_PREFIX '0'
+#define EAP_AKA_PSEUDONYM_PREFIX '2'
+#define EAP_AKA_REAUTH_ID_PREFIX '4'
+
+void * eap_sim_db_init(const char *config,
+ void (*get_complete_cb)(void *ctx, void *session_ctx),
+ void *ctx);
+
+void eap_sim_db_deinit(void *priv);
+
+int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
+ size_t identity_len, int max_chal,
+ u8 *_rand, u8 *kc, u8 *sres,
+ void *cb_session_ctx);
+
+#define EAP_SIM_DB_FAILURE -1
+#define EAP_SIM_DB_PENDING -2
+
+int eap_sim_db_identity_known(void *priv, const u8 *identity,
+ size_t identity_len);
+
+char * eap_sim_db_get_next_pseudonym(void *priv, int aka);
+
+char * eap_sim_db_get_next_reauth_id(void *priv, int aka);
+
+int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
+ size_t identity_len, char *pseudonym);
+
+int eap_sim_db_add_reauth(void *priv, const u8 *identity,
+ size_t identity_len, char *reauth_id, u16 counter,
+ const u8 *mk);
+int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
+ size_t identity_len, char *reauth_id,
+ u16 counter, const u8 *k_encr, const u8 *k_aut,
+ const u8 *k_re);
+
+const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
+ size_t identity_len, size_t *len);
+
+struct eap_sim_reauth {
+ struct eap_sim_reauth *next;
+ u8 *identity;
+ size_t identity_len;
+ char *reauth_id;
+ u16 counter;
+ int aka_prime;
+ u8 mk[EAP_SIM_MK_LEN];
+ u8 k_encr[EAP_SIM_K_ENCR_LEN];
+ u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
+ u8 k_re[EAP_AKA_PRIME_K_RE_LEN];
+};
+
+struct eap_sim_reauth *
+eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
+ size_t identity_len);
+
+void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth);
+
+int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
+ size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
+ u8 *ck, u8 *res, size_t *res_len,
+ void *cb_session_ctx);
+
+int eap_sim_db_resynchronize(void *priv, const u8 *identity,
+ size_t identity_len, const u8 *auts,
+ const u8 *_rand);
+
+#endif /* EAP_SIM_DB_H */
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
new file mode 100644
index 0000000..c34c401
--- /dev/null
+++ b/src/eap_server/eap_tls_common.h
@@ -0,0 +1,91 @@
+/*
+ * EAP-TLS/PEAP/TTLS/FAST server common functions
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_TLS_COMMON_H
+#define EAP_TLS_COMMON_H
+
+/**
+ * struct eap_ssl_data - TLS data for EAP methods
+ */
+struct eap_ssl_data {
+ /**
+ * conn - TLS connection context data from tls_connection_init()
+ */
+ struct tls_connection *conn;
+
+ /**
+ * tls_out - TLS message to be sent out in fragments
+ */
+ struct wpabuf *tls_out;
+
+ /**
+ * tls_out_pos - The current position in the outgoing TLS message
+ */
+ size_t tls_out_pos;
+
+ /**
+ * tls_out_limit - Maximum fragment size for outgoing TLS messages
+ */
+ size_t tls_out_limit;
+
+ /**
+ * tls_in - Received TLS message buffer for re-assembly
+ */
+ struct wpabuf *tls_in;
+
+ /**
+ * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
+ */
+ int phase2;
+
+ /**
+ * eap - EAP state machine allocated with eap_server_sm_init()
+ */
+ struct eap_sm *eap;
+
+ enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state;
+ struct wpabuf tmpbuf;
+};
+
+
+/* EAP TLS Flags */
+#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TLS_FLAGS_START 0x20
+#define EAP_TLS_VERSION_MASK 0x07
+
+ /* could be up to 128 bytes, but only the first 64 bytes are used */
+#define EAP_TLS_KEY_LEN 64
+
+
+int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+ int verify_peer);
+void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
+u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+ char *label, size_t len);
+struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
+ int eap_type, int version, u8 id);
+struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version);
+int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data);
+struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm,
+ struct eap_ssl_data *data,
+ const struct wpabuf *plain);
+int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
+ struct wpabuf *respData, void *priv, int eap_type,
+ int (*proc_version)(struct eap_sm *sm, void *priv,
+ int peer_version),
+ void (*proc_msg)(struct eap_sm *sm, void *priv,
+ const struct wpabuf *respData));
+
+#endif /* EAP_TLS_COMMON_H */
diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c
new file mode 100644
index 0000000..9624d53
--- /dev/null
+++ b/src/eap_server/ikev2.c
@@ -0,0 +1,1206 @@
+/*
+ * IKEv2 initiator (RFC 4306) for EAP-IKEV2
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/dh_groups.h"
+#include "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: <Proposals> */
+
+ if (sar1 == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: SAr1 not received");
+ return -1;
+ }
+
+ os_memset(&prop, 0, sizeof(prop));
+ prop.proposal_num = 1;
+
+ pos = sar1;
+ end = sar1 + sar1_len;
+
+ while (pos < end) {
+ int plen;
+
+ prop.integ = -1;
+ prop.prf = -1;
+ prop.encr = -1;
+ prop.dh = -1;
+ plen = ikev2_parse_proposal(data, &prop, pos, end);
+ if (plen < 0)
+ return -1;
+
+ if (!found && prop.integ != -1 && prop.prf != -1 &&
+ prop.encr != -1 && prop.dh != -1) {
+ found = 1;
+ }
+
+ pos += plen;
+
+ /* Only one proposal expected in SAr */
+ break;
+ }
+
+ if (pos != end) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposal");
+ return -1;
+ }
+
+ if (!found) {
+ wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d "
+ "INTEG:%d D-H:%d", data->proposal.proposal_num,
+ data->proposal.encr, data->proposal.prf,
+ data->proposal.integ, data->proposal.dh);
+
+ return 0;
+}
+
+
+static int ikev2_process_ker(struct ikev2_initiator_data *data,
+ const u8 *ker, size_t ker_len)
+{
+ u16 group;
+
+ /*
+ * Key Exchange Payload:
+ * DH Group # (16 bits)
+ * RESERVED (16 bits)
+ * Key Exchange Data (Diffie-Hellman public value)
+ */
+
+ if (ker == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: KEr not received");
+ return -1;
+ }
+
+ if (ker_len < 4 + 96) {
+ wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload");
+ return -1;
+ }
+
+ group = WPA_GET_BE16(ker);
+ wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u", group);
+
+ if (group != data->proposal.dh) {
+ wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u does not match "
+ "with the selected proposal (%u)",
+ group, data->proposal.dh);
+ return -1;
+ }
+
+ if (data->dh == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group");
+ return -1;
+ }
+
+ /* RFC 4306, Section 3.4:
+ * The length of DH public value MUST be equal to the lenght of the
+ * prime modulus.
+ */
+ if (ker_len - 4 != data->dh->prime_len) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length "
+ "%ld (expected %ld)",
+ (long) (ker_len - 4), (long) data->dh->prime_len);
+ return -1;
+ }
+
+ wpabuf_free(data->r_dh_public);
+ data->r_dh_public = wpabuf_alloc_copy(ker + 4, ker_len - 4);
+ if (data->r_dh_public == NULL)
+ return -1;
+
+ wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEr Diffie-Hellman Public Value",
+ data->r_dh_public);
+
+ return 0;
+}
+
+
+static int ikev2_process_nr(struct ikev2_initiator_data *data,
+ const u8 *nr, size_t nr_len)
+{
+ if (nr == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: Nr not received");
+ return -1;
+ }
+
+ if (nr_len < IKEV2_NONCE_MIN_LEN || nr_len > IKEV2_NONCE_MAX_LEN) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid Nr length %ld",
+ (long) nr_len);
+ return -1;
+ }
+
+ data->r_nonce_len = nr_len;
+ os_memcpy(data->r_nonce, nr, nr_len);
+ wpa_hexdump(MSG_MSGDUMP, "IKEV2: Nr",
+ data->r_nonce, data->r_nonce_len);
+
+ return 0;
+}
+
+
+static int ikev2_process_sa_init_encr(struct ikev2_initiator_data *data,
+ const struct ikev2_hdr *hdr,
+ const u8 *encrypted,
+ size_t encrypted_len, u8 next_payload)
+{
+ u8 *decrypted;
+ size_t decrypted_len;
+ struct ikev2_payloads pl;
+ int ret = 0;
+
+ decrypted = ikev2_decrypt_payload(data->proposal.encr,
+ data->proposal.integ, &data->keys, 0,
+ hdr, encrypted, encrypted_len,
+ &decrypted_len);
+ if (decrypted == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads");
+
+ if (ikev2_parse_payloads(&pl, next_payload, decrypted,
+ decrypted + decrypted_len) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted "
+ "payloads");
+ return -1;
+ }
+
+ if (pl.idr)
+ ret = ikev2_process_idr(data, pl.idr, pl.idr_len);
+
+ os_free(decrypted);
+
+ return ret;
+}
+
+
+static int ikev2_process_sa_init(struct ikev2_initiator_data *data,
+ const struct ikev2_hdr *hdr,
+ struct ikev2_payloads *pl)
+{
+ if (ikev2_process_sar1(data, pl->sa, pl->sa_len) < 0 ||
+ ikev2_process_ker(data, pl->ke, pl->ke_len) < 0 ||
+ ikev2_process_nr(data, pl->nonce, pl->nonce_len) < 0)
+ return -1;
+
+ os_memcpy(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN);
+
+ if (ikev2_derive_keys(data) < 0)
+ return -1;
+
+ if (pl->encrypted) {
+ wpa_printf(MSG_DEBUG, "IKEV2: Encrypted payload in SA_INIT - "
+ "try to get IDr from it");
+ if (ikev2_process_sa_init_encr(data, hdr, pl->encrypted,
+ pl->encrypted_len,
+ pl->encr_next_payload) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Failed to process "
+ "encrypted payload");
+ return -1;
+ }
+ }
+
+ data->state = SA_AUTH;
+
+ return 0;
+}
+
+
+static int ikev2_process_idr(struct ikev2_initiator_data *data,
+ const u8 *idr, size_t idr_len)
+{
+ u8 id_type;
+
+ if (idr == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No IDr received");
+ return -1;
+ }
+
+ if (idr_len < 4) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short IDr payload");
+ return -1;
+ }
+
+ id_type = idr[0];
+ idr += 4;
+ idr_len -= 4;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: IDr ID Type %d", id_type);
+ wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDr", idr, idr_len);
+ if (data->IDr) {
+ if (id_type != data->IDr_type || idr_len != data->IDr_len ||
+ os_memcmp(idr, data->IDr, idr_len) != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: IDr differs from the one "
+ "received earlier");
+ wpa_printf(MSG_DEBUG, "IKEV2: Previous IDr ID Type %d",
+ id_type);
+ wpa_hexdump_ascii(MSG_DEBUG, "Previous IKEV2: IDr",
+ data->IDr, data->IDr_len);
+ return -1;
+ }
+ os_free(data->IDr);
+ }
+ data->IDr = os_malloc(idr_len);
+ if (data->IDr == NULL)
+ return -1;
+ os_memcpy(data->IDr, idr, idr_len);
+ data->IDr_len = idr_len;
+ data->IDr_type = id_type;
+
+ return 0;
+}
+
+
+static int ikev2_process_cert(struct ikev2_initiator_data *data,
+ const u8 *cert, size_t cert_len)
+{
+ u8 cert_encoding;
+
+ if (cert == NULL) {
+ if (data->peer_auth == PEER_AUTH_CERT) {
+ wpa_printf(MSG_INFO, "IKEV2: No Certificate received");
+ return -1;
+ }
+ return 0;
+ }
+
+ if (cert_len < 1) {
+ wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field");
+ return -1;
+ }
+
+ cert_encoding = cert[0];
+ cert++;
+ cert_len--;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding);
+ wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len);
+
+ /* TODO: validate certificate */
+
+ return 0;
+}
+
+
+static int ikev2_process_auth_cert(struct ikev2_initiator_data *data,
+ u8 method, const u8 *auth, size_t auth_len)
+{
+ if (method != AUTH_RSA_SIGN) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+ "method %d", method);
+ return -1;
+ }
+
+ /* TODO: validate AUTH */
+ return 0;
+}
+
+
+static int ikev2_process_auth_secret(struct ikev2_initiator_data *data,
+ u8 method, const u8 *auth,
+ size_t auth_len)
+{
+ u8 auth_data[IKEV2_MAX_HASH_LEN];
+ const struct ikev2_prf_alg *prf;
+
+ if (method != AUTH_SHARED_KEY_MIC) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+ "method %d", method);
+ return -1;
+ }
+
+ /* msg | Ni | prf(SK_pr,IDr') */
+ if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg,
+ data->IDr, data->IDr_len, data->IDr_type,
+ &data->keys, 0, data->shared_secret,
+ data->shared_secret_len,
+ data->i_nonce, data->i_nonce_len,
+ data->key_pad, data->key_pad_len,
+ auth_data) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+ return -1;
+ }
+
+ wpabuf_free(data->r_sign_msg);
+ data->r_sign_msg = NULL;
+
+ prf = ikev2_get_prf(data->proposal.prf);
+ if (prf == NULL)
+ return -1;
+
+ if (auth_len != prf->hash_len ||
+ os_memcmp(auth, auth_data, auth_len) != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data");
+ wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data",
+ auth, auth_len);
+ wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data",
+ auth_data, prf->hash_len);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Peer authenticated successfully "
+ "using shared keys");
+
+ return 0;
+}
+
+
+static int ikev2_process_auth(struct ikev2_initiator_data *data,
+ const u8 *auth, size_t auth_len)
+{
+ u8 auth_method;
+
+ if (auth == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload");
+ return -1;
+ }
+
+ if (auth_len < 4) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short Authentication "
+ "Payload");
+ return -1;
+ }
+
+ auth_method = auth[0];
+ auth += 4;
+ auth_len -= 4;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method);
+ wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len);
+
+ switch (data->peer_auth) {
+ case PEER_AUTH_CERT:
+ return ikev2_process_auth_cert(data, auth_method, auth,
+ auth_len);
+ case PEER_AUTH_SECRET:
+ return ikev2_process_auth_secret(data, auth_method, auth,
+ auth_len);
+ }
+
+ return -1;
+}
+
+
+static int ikev2_process_sa_auth_decrypted(struct ikev2_initiator_data *data,
+ u8 next_payload,
+ u8 *payload, size_t payload_len)
+{
+ struct ikev2_payloads pl;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads");
+
+ if (ikev2_parse_payloads(&pl, next_payload, payload, payload +
+ payload_len) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted "
+ "payloads");
+ return -1;
+ }
+
+ if (ikev2_process_idr(data, pl.idr, pl.idr_len) < 0 ||
+ ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 ||
+ ikev2_process_auth(data, pl.auth, pl.auth_len) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int ikev2_process_sa_auth(struct ikev2_initiator_data *data,
+ const struct ikev2_hdr *hdr,
+ struct ikev2_payloads *pl)
+{
+ u8 *decrypted;
+ size_t decrypted_len;
+ int ret;
+
+ decrypted = ikev2_decrypt_payload(data->proposal.encr,
+ data->proposal.integ,
+ &data->keys, 0, hdr, pl->encrypted,
+ pl->encrypted_len, &decrypted_len);
+ if (decrypted == NULL)
+ return -1;
+
+ ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload,
+ decrypted, decrypted_len);
+ os_free(decrypted);
+
+ if (ret == 0 && !data->unknown_user) {
+ wpa_printf(MSG_DEBUG, "IKEV2: Authentication completed");
+ data->state = IKEV2_DONE;
+ }
+
+ return ret;
+}
+
+
+static int ikev2_validate_rx_state(struct ikev2_initiator_data *data,
+ u8 exchange_type, u32 message_id)
+{
+ switch (data->state) {
+ case SA_INIT:
+ /* Expect to receive IKE_SA_INIT: HDR, SAr, KEr, Nr, [CERTREQ],
+ * [SK{IDr}] */
+ if (exchange_type != IKE_SA_INIT) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+ "%u in SA_INIT state", exchange_type);
+ return -1;
+ }
+ if (message_id != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+ "in SA_INIT state", message_id);
+ return -1;
+ }
+ break;
+ case SA_AUTH:
+ /* Expect to receive IKE_SA_AUTH:
+ * HDR, SK {IDr, [CERT,] [CERTREQ,] [NFID,] AUTH}
+ */
+ if (exchange_type != IKE_SA_AUTH) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+ "%u in SA_AUTH state", exchange_type);
+ return -1;
+ }
+ if (message_id != 1) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+ "in SA_AUTH state", message_id);
+ return -1;
+ }
+ break;
+ case CHILD_SA:
+ if (exchange_type != CREATE_CHILD_SA) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+ "%u in CHILD_SA state", exchange_type);
+ return -1;
+ }
+ if (message_id != 2) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+ "in CHILD_SA state", message_id);
+ return -1;
+ }
+ break;
+ case IKEV2_DONE:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int ikev2_initiator_process(struct ikev2_initiator_data *data,
+ const struct wpabuf *buf)
+{
+ const struct ikev2_hdr *hdr;
+ u32 length, message_id;
+ const u8 *pos, *end;
+ struct ikev2_payloads pl;
+
+ wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)",
+ (unsigned long) wpabuf_len(buf));
+
+ if (wpabuf_len(buf) < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR");
+ return -1;
+ }
+
+ hdr = (const struct ikev2_hdr *) wpabuf_head(buf);
+ end = wpabuf_head_u8(buf) + wpabuf_len(buf);
+ message_id = WPA_GET_BE32(hdr->message_id);
+ length = WPA_GET_BE32(hdr->length);
+
+ wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI",
+ hdr->i_spi, IKEV2_SPI_LEN);
+ wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI",
+ hdr->r_spi, IKEV2_SPI_LEN);
+ wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Version: 0x%x "
+ "Exchange Type: %u",
+ hdr->next_payload, hdr->version, hdr->exchange_type);
+ wpa_printf(MSG_DEBUG, "IKEV2: Message ID: %u Length: %u",
+ message_id, length);
+
+ if (hdr->version != IKEV2_VERSION) {
+ wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x "
+ "(expected 0x%x)", hdr->version, IKEV2_VERSION);
+ return -1;
+ }
+
+ if (length != wpabuf_len(buf)) {
+ wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != "
+ "RX: %lu)", (unsigned long) length,
+ (unsigned long) wpabuf_len(buf));
+ return -1;
+ }
+
+ if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0)
+ return -1;
+
+ if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) !=
+ IKEV2_HDR_RESPONSE) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x",
+ hdr->flags);
+ return -1;
+ }
+
+ if (data->state != SA_INIT) {
+ if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+ "Initiator's SPI");
+ return -1;
+ }
+ if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+ "Responder's SPI");
+ return -1;
+ }
+ }
+
+ pos = (const u8 *) (hdr + 1);
+ if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0)
+ return -1;
+
+ switch (data->state) {
+ case SA_INIT:
+ if (ikev2_process_sa_init(data, hdr, &pl) < 0)
+ return -1;
+ wpabuf_free(data->r_sign_msg);
+ data->r_sign_msg = wpabuf_dup(buf);
+ break;
+ case SA_AUTH:
+ if (ikev2_process_sa_auth(data, hdr, &pl) < 0)
+ return -1;
+ break;
+ case CHILD_SA:
+ case IKEV2_DONE:
+ break;
+ }
+
+ return 0;
+}
+
+
+static void ikev2_build_hdr(struct ikev2_initiator_data *data,
+ struct wpabuf *msg, u8 exchange_type,
+ u8 next_payload, u32 message_id)
+{
+ struct ikev2_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR");
+
+ /* HDR - RFC 4306, Sect. 3.1 */
+ hdr = wpabuf_put(msg, sizeof(*hdr));
+ os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN);
+ os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN);
+ hdr->next_payload = next_payload;
+ hdr->version = IKEV2_VERSION;
+ hdr->exchange_type = exchange_type;
+ hdr->flags = IKEV2_HDR_INITIATOR;
+ WPA_PUT_BE32(hdr->message_id, message_id);
+}
+
+
+static int ikev2_build_sai(struct ikev2_initiator_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+ struct ikev2_proposal *p;
+ struct ikev2_transform *t;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding SAi payload");
+
+ /* SAi1 - RFC 4306, Sect. 2.7 and 3.3 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+
+ /* TODO: support for multiple proposals */
+ p = wpabuf_put(msg, sizeof(*p));
+ p->proposal_num = data->proposal.proposal_num;
+ p->protocol_id = IKEV2_PROTOCOL_IKE;
+ p->num_transforms = 4;
+
+ t = wpabuf_put(msg, sizeof(*t));
+ t->type = 3;
+ t->transform_type = IKEV2_TRANSFORM_ENCR;
+ WPA_PUT_BE16(t->transform_id, data->proposal.encr);
+ if (data->proposal.encr == ENCR_AES_CBC) {
+ /* Transform Attribute: Key Len = 128 bits */
+ wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */
+ wpabuf_put_be16(msg, 128); /* 128-bit key */
+ }
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t;
+ WPA_PUT_BE16(t->transform_length, plen);
+
+ t = wpabuf_put(msg, sizeof(*t));
+ t->type = 3;
+ WPA_PUT_BE16(t->transform_length, sizeof(*t));
+ t->transform_type = IKEV2_TRANSFORM_PRF;
+ WPA_PUT_BE16(t->transform_id, data->proposal.prf);
+
+ t = wpabuf_put(msg, sizeof(*t));
+ t->type = 3;
+ WPA_PUT_BE16(t->transform_length, sizeof(*t));
+ t->transform_type = IKEV2_TRANSFORM_INTEG;
+ WPA_PUT_BE16(t->transform_id, data->proposal.integ);
+
+ t = wpabuf_put(msg, sizeof(*t));
+ WPA_PUT_BE16(t->transform_length, sizeof(*t));
+ t->transform_type = IKEV2_TRANSFORM_DH;
+ WPA_PUT_BE16(t->transform_id, data->proposal.dh);
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p;
+ WPA_PUT_BE16(p->proposal_length, plen);
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+
+ return 0;
+}
+
+
+static int ikev2_build_kei(struct ikev2_initiator_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+ struct wpabuf *pv;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding KEi payload");
+
+ data->dh = dh_groups_get(data->proposal.dh);
+ pv = dh_init(data->dh, &data->i_dh_private);
+ if (pv == NULL) {
+ wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH");
+ return -1;
+ }
+
+ /* KEi - RFC 4306, Sect. 3.4 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+
+ wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */
+ wpabuf_put(msg, 2); /* RESERVED */
+ /*
+ * RFC 4306, Sect. 3.4: possible zero padding for public value to
+ * match the length of the prime.
+ */
+ wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv));
+ wpabuf_put_buf(msg, pv);
+ os_free(pv);
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static int ikev2_build_ni(struct ikev2_initiator_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding Ni payload");
+
+ /* Ni - RFC 4306, Sect. 3.9 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+ wpabuf_put_data(msg, data->i_nonce, data->i_nonce_len);
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static int ikev2_build_idi(struct ikev2_initiator_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding IDi payload");
+
+ if (data->IDi == NULL) {
+ wpa_printf(MSG_INFO, "IKEV2: No IDi available");
+ return -1;
+ }
+
+ /* IDi - RFC 4306, Sect. 3.5 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+ wpabuf_put_u8(msg, ID_KEY_ID);
+ wpabuf_put(msg, 3); /* RESERVED */
+ wpabuf_put_data(msg, data->IDi, data->IDi_len);
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static int ikev2_build_auth(struct ikev2_initiator_data *data,
+ struct wpabuf *msg, u8 next_payload)
+{
+ struct ikev2_payload_hdr *phdr;
+ size_t plen;
+ const struct ikev2_prf_alg *prf;
+
+ wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload");
+
+ prf = ikev2_get_prf(data->proposal.prf);
+ if (prf == NULL)
+ return -1;
+
+ /* Authentication - RFC 4306, Sect. 3.8 */
+ phdr = wpabuf_put(msg, sizeof(*phdr));
+ phdr->next_payload = next_payload;
+ phdr->flags = 0;
+ wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC);
+ wpabuf_put(msg, 3); /* RESERVED */
+
+ /* msg | Nr | prf(SK_pi,IDi') */
+ if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg,
+ data->IDi, data->IDi_len, ID_KEY_ID,
+ &data->keys, 1, data->shared_secret,
+ data->shared_secret_len,
+ data->r_nonce, data->r_nonce_len,
+ data->key_pad, data->key_pad_len,
+ wpabuf_put(msg, prf->hash_len)) < 0) {
+ wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+ return -1;
+ }
+ wpabuf_free(data->i_sign_msg);
+ data->i_sign_msg = NULL;
+
+ plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+ WPA_PUT_BE16(phdr->payload_length, plen);
+ return 0;
+}
+
+
+static struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data)
+{
+ struct wpabuf *msg;
+
+ /* build IKE_SA_INIT: HDR, SAi, KEi, Ni */
+
+ if (os_get_random(data->i_spi, IKEV2_SPI_LEN))
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI",
+ data->i_spi, IKEV2_SPI_LEN);
+
+ data->i_nonce_len = IKEV2_NONCE_MIN_LEN;
+ if (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/src/eap_server/ikev2.h b/src/eap_server/ikev2.h
new file mode 100644
index 0000000..8349fbe
--- /dev/null
+++ b/src/eap_server/ikev2.h
@@ -0,0 +1,67 @@
+/*
+ * IKEv2 initiator (RFC 4306) for EAP-IKEV2
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef IKEV2_H
+#define IKEV2_H
+
+#include "eap_common/ikev2_common.h"
+
+struct ikev2_proposal_data {
+ u8 proposal_num;
+ int integ;
+ int prf;
+ int encr;
+ int dh;
+};
+
+
+struct ikev2_initiator_data {
+ enum { SA_INIT, SA_AUTH, CHILD_SA, IKEV2_DONE } state;
+ u8 i_spi[IKEV2_SPI_LEN];
+ u8 r_spi[IKEV2_SPI_LEN];
+ u8 i_nonce[IKEV2_NONCE_MAX_LEN];
+ size_t i_nonce_len;
+ u8 r_nonce[IKEV2_NONCE_MAX_LEN];
+ size_t r_nonce_len;
+ struct wpabuf *r_dh_public;
+ struct wpabuf *i_dh_private;
+ struct ikev2_proposal_data proposal;
+ const struct dh_group *dh;
+ struct ikev2_keys keys;
+ u8 *IDi;
+ size_t IDi_len;
+ u8 *IDr;
+ size_t IDr_len;
+ u8 IDr_type;
+ struct wpabuf *r_sign_msg;
+ struct wpabuf *i_sign_msg;
+ u8 *shared_secret;
+ size_t shared_secret_len;
+ enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth;
+ u8 *key_pad;
+ size_t key_pad_len;
+
+ const u8 * (*get_shared_secret)(void *ctx, const u8 *IDr,
+ size_t IDr_len, size_t *secret_len);
+ void *cb_ctx;
+ int unknown_user;
+};
+
+
+void ikev2_initiator_deinit(struct ikev2_initiator_data *data);
+int ikev2_initiator_process(struct ikev2_initiator_data *data,
+ const struct wpabuf *buf);
+struct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data);
+
+#endif /* IKEV2_H */
diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c
new file mode 100644
index 0000000..637b6f8
--- /dev/null
+++ b/src/eap_server/tncs.c
@@ -0,0 +1,1273 @@
+/*
+ * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
+ * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <dlfcn.h>
+
+#include "common.h"
+#include "base64.h"
+#include "tncs.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_defs.h"
+
+
+/* TODO: TNCS must be thread-safe; review the code and add locking etc. if
+ * needed.. */
+
+#define TNC_CONFIG_FILE "/etc/tnc_config"
+#define IF_TNCCS_START \
+"<?xml version=\"1.0\"?>\n" \
+"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
+"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
+"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
+"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
+"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
+#define IF_TNCCS_END "\n</TNCCS-Batch>"
+
+/* TNC IF-IMV */
+
+typedef unsigned long TNC_UInt32;
+typedef unsigned char *TNC_BufferReference;
+
+typedef TNC_UInt32 TNC_IMVID;
+typedef TNC_UInt32 TNC_ConnectionID;
+typedef TNC_UInt32 TNC_ConnectionState;
+typedef TNC_UInt32 TNC_RetryReason;
+typedef TNC_UInt32 TNC_IMV_Action_Recommendation;
+typedef TNC_UInt32 TNC_IMV_Evaluation_Result;
+typedef TNC_UInt32 TNC_MessageType;
+typedef TNC_MessageType *TNC_MessageTypeList;
+typedef TNC_UInt32 TNC_VendorID;
+typedef TNC_UInt32 TNC_Subtype;
+typedef TNC_UInt32 TNC_Version;
+typedef TNC_UInt32 TNC_Result;
+typedef TNC_UInt32 TNC_AttributeID;
+
+typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
+ TNC_IMVID imvID,
+ char *functionName,
+ void **pOutfunctionPointer);
+
+#define TNC_RESULT_SUCCESS 0
+#define TNC_RESULT_NOT_INITIALIZED 1
+#define TNC_RESULT_ALREADY_INITIALIZED 2
+#define TNC_RESULT_NO_COMMON_VERSION 3
+#define TNC_RESULT_CANT_RETRY 4
+#define TNC_RESULT_WONT_RETRY 5
+#define TNC_RESULT_INVALID_PARAMETER 6
+#define TNC_RESULT_CANT_RESPOND 7
+#define TNC_RESULT_ILLEGAL_OPERATION 8
+#define TNC_RESULT_OTHER 9
+#define TNC_RESULT_FATAL 10
+
+#define TNC_CONNECTION_STATE_CREATE 0
+#define TNC_CONNECTION_STATE_HANDSHAKE 1
+#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
+#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
+#define TNC_CONNECTION_STATE_ACCESS_NONE 4
+#define TNC_CONNECTION_STATE_DELETE 5
+
+#define TNC_IFIMV_VERSION_1 1
+
+#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
+#define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff)
+
+/* TNCC-TNCS Message Types */
+#define TNC_TNCCS_RECOMMENDATION 0x00000001
+#define TNC_TNCCS_ERROR 0x00000002
+#define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003
+#define TNC_TNCCS_REASONSTRINGS 0x00000004
+
+/* Possible TNC_IMV_Action_Recommendation values: */
+enum IMV_Action_Recommendation {
+ TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
+ TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS,
+ TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
+ TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
+};
+
+/* Possible TNC_IMV_Evaluation_Result values: */
+enum IMV_Evaluation_Result {
+ TNC_IMV_EVALUATION_RESULT_COMPLIANT,
+ TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR,
+ TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR,
+ TNC_IMV_EVALUATION_RESULT_ERROR,
+ TNC_IMV_EVALUATION_RESULT_DONT_KNOW
+};
+
+struct tnc_if_imv {
+ struct tnc_if_imv *next;
+ char *name;
+ char *path;
+ void *dlhandle; /* from dlopen() */
+ TNC_IMVID imvID;
+ TNC_MessageTypeList supported_types;
+ size_t num_supported_types;
+
+ /* Functions implemented by IMVs (with TNC_IMV_ prefix) */
+ TNC_Result (*Initialize)(
+ TNC_IMVID imvID,
+ TNC_Version minVersion,
+ TNC_Version maxVersion,
+ TNC_Version *pOutActualVersion);
+ TNC_Result (*NotifyConnectionChange)(
+ TNC_IMVID imvID,
+ TNC_ConnectionID connectionID,
+ TNC_ConnectionState newState);
+ TNC_Result (*ReceiveMessage)(
+ TNC_IMVID imvID,
+ TNC_ConnectionID connectionID,
+ TNC_BufferReference message,
+ TNC_UInt32 messageLength,
+ TNC_MessageType messageType);
+ TNC_Result (*SolicitRecommendation)(
+ TNC_IMVID imvID,
+ TNC_ConnectionID connectionID);
+ TNC_Result (*BatchEnding)(
+ TNC_IMVID imvID,
+ TNC_ConnectionID connectionID);
+ TNC_Result (*Terminate)(TNC_IMVID imvID);
+ TNC_Result (*ProvideBindFunction)(
+ TNC_IMVID imvID,
+ TNC_TNCS_BindFunctionPointer bindFunction);
+};
+
+
+#define TNC_MAX_IMV_ID 10
+
+struct tncs_data {
+ struct tncs_data *next;
+ struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */
+ TNC_ConnectionID connectionID;
+ unsigned int last_batchid;
+ enum IMV_Action_Recommendation recommendation;
+ int done;
+
+ struct conn_imv {
+ u8 *imv_send;
+ size_t imv_send_len;
+ enum IMV_Action_Recommendation recommendation;
+ int recommendation_set;
+ } imv_data[TNC_MAX_IMV_ID];
+
+ char *tncs_message;
+};
+
+
+struct tncs_global {
+ struct tnc_if_imv *imv;
+ TNC_ConnectionID next_conn_id;
+ struct tncs_data *connections;
+};
+
+static struct tncs_global *tncs_global_data = NULL;
+
+
+static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID)
+{
+ struct tnc_if_imv *imv;
+
+ if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL)
+ return NULL;
+ imv = tncs_global_data->imv;
+ while (imv) {
+ if (imv->imvID == imvID)
+ return imv;
+ imv = imv->next;
+ }
+ return NULL;
+}
+
+
+static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID)
+{
+ struct tncs_data *tncs;
+
+ if (tncs_global_data == NULL)
+ return NULL;
+
+ tncs = tncs_global_data->connections;
+ while (tncs) {
+ if (tncs->connectionID == connectionID)
+ return tncs;
+ tncs = tncs->next;
+ }
+
+ wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found",
+ (unsigned long) connectionID);
+
+ return NULL;
+}
+
+
+/* TNCS functions that IMVs can call */
+TNC_Result TNC_TNCS_ReportMessageTypes(
+ TNC_IMVID imvID,
+ TNC_MessageTypeList supportedTypes,
+ TNC_UInt32 typeCount)
+{
+ TNC_UInt32 i;
+ struct tnc_if_imv *imv;
+
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
+ "typeCount=%lu)",
+ (unsigned long) imvID, (unsigned long) typeCount);
+
+ for (i = 0; i < typeCount; i++) {
+ wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
+ i, supportedTypes[i]);
+ }
+
+ imv = tncs_get_imv(imvID);
+ if (imv == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+ os_free(imv->supported_types);
+ imv->supported_types =
+ os_malloc(typeCount * sizeof(TNC_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,
+ "<IMC-IMV-Message><Type>%08X</Type>"
+ "<Base64>%s</Base64></IMC-IMV-Message>",
+ (unsigned int) messageType, b64);
+
+ os_free(b64);
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_RequestHandshakeRetry(
+ TNC_IMVID imvID,
+ TNC_ConnectionID connectionID,
+ TNC_RetryReason reason)
+{
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry");
+ /* TODO */
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_ProvideRecommendation(
+ TNC_IMVID imvID,
+ TNC_ConnectionID connectionID,
+ TNC_IMV_Action_Recommendation recommendation,
+ TNC_IMV_Evaluation_Result evaluation)
+{
+ struct tncs_data *tncs;
+
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu "
+ "connectionID=%lu recommendation=%lu evaluation=%lu)",
+ (unsigned long) imvID, (unsigned long) connectionID,
+ (unsigned long) recommendation, (unsigned long) evaluation);
+
+ if (tncs_get_imv(imvID) == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ tncs = tncs_get_conn(connectionID);
+ if (tncs == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ tncs->imv_data[imvID].recommendation = recommendation;
+ tncs->imv_data[imvID].recommendation_set = 1;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_GetAttribute(
+ TNC_IMVID imvID,
+ TNC_ConnectionID connectionID,
+ TNC_AttributeID attribureID,
+ TNC_UInt32 bufferLength,
+ TNC_BufferReference buffer,
+ TNC_UInt32 *pOutValueLength)
+{
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute");
+ /* TODO */
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_SetAttribute(
+ TNC_IMVID imvID,
+ TNC_ConnectionID connectionID,
+ TNC_AttributeID attribureID,
+ TNC_UInt32 bufferLength,
+ TNC_BufferReference buffer)
+{
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute");
+ /* TODO */
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_BindFunction(
+ TNC_IMVID imvID,
+ char *functionName,
+ void **pOutFunctionPointer)
+{
+ wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, "
+ "functionName='%s')", (unsigned long) imvID, functionName);
+
+ if (tncs_get_imv(imvID) == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ if (pOutFunctionPointer == NULL)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0)
+ *pOutFunctionPointer = TNC_TNCS_ReportMessageTypes;
+ else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0)
+ *pOutFunctionPointer = TNC_TNCS_SendMessage;
+ else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") ==
+ 0)
+ *pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry;
+ else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") ==
+ 0)
+ *pOutFunctionPointer = TNC_TNCS_ProvideRecommendation;
+ else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0)
+ *pOutFunctionPointer = TNC_TNCS_GetAttribute;
+ else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0)
+ *pOutFunctionPointer = TNC_TNCS_SetAttribute;
+ else
+ *pOutFunctionPointer = NULL;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+static void * tncs_get_sym(void *handle, char *func)
+{
+ void *fptr;
+
+ fptr = dlsym(handle, func);
+
+ return fptr;
+}
+
+
+static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv)
+{
+ void *handle = imv->dlhandle;
+
+ /* Mandatory IMV functions */
+ imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize");
+ if (imv->Initialize == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: IMV does not export "
+ "TNC_IMV_Initialize");
+ return -1;
+ }
+
+ imv->SolicitRecommendation = tncs_get_sym(
+ handle, "TNC_IMV_SolicitRecommendation");
+ if (imv->SolicitRecommendation == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: IMV does not export "
+ "TNC_IMV_SolicitRecommendation");
+ return -1;
+ }
+
+ imv->ProvideBindFunction =
+ tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction");
+ if (imv->ProvideBindFunction == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: IMV does not export "
+ "TNC_IMV_ProvideBindFunction");
+ return -1;
+ }
+
+ /* Optional IMV functions */
+ imv->NotifyConnectionChange =
+ tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange");
+ imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage");
+ imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding");
+ imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate");
+
+ return 0;
+}
+
+
+static int tncs_imv_initialize(struct tnc_if_imv *imv)
+{
+ TNC_Result res;
+ TNC_Version imv_ver;
+
+ wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'",
+ imv->name);
+ res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1,
+ TNC_IFIMV_VERSION_1, &imv_ver);
+ wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu",
+ (unsigned long) res, (unsigned long) imv_ver);
+
+ return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncs_imv_terminate(struct tnc_if_imv *imv)
+{
+ TNC_Result res;
+
+ if (imv->Terminate == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'",
+ imv->name);
+ res = imv->Terminate(imv->imvID);
+ wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu",
+ (unsigned long) res);
+
+ return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv)
+{
+ TNC_Result res;
+
+ wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for "
+ "IMV '%s'", imv->name);
+ res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction);
+ wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu",
+ (unsigned long) res);
+
+ return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv,
+ TNC_ConnectionID conn,
+ TNC_ConnectionState state)
+{
+ TNC_Result res;
+
+ if (imv->NotifyConnectionChange == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)"
+ " for IMV '%s'", (int) state, imv->name);
+ res = imv->NotifyConnectionChange(imv->imvID, conn, state);
+ wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
+ (unsigned long) res);
+
+ return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncs_load_imv(struct tnc_if_imv *imv)
+{
+ if (imv->path == NULL) {
+ wpa_printf(MSG_DEBUG, "TNC: No IMV configured");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)",
+ imv->name, imv->path);
+ imv->dlhandle = dlopen(imv->path, RTLD_LAZY);
+ if (imv->dlhandle == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s",
+ imv->name, imv->path, dlerror());
+ return -1;
+ }
+
+ if (tncs_imv_resolve_funcs(imv) < 0) {
+ wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions");
+ return -1;
+ }
+
+ if (tncs_imv_initialize(imv) < 0 ||
+ tncs_imv_provide_bind_function(imv) < 0) {
+ wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void tncs_free_imv(struct tnc_if_imv *imv)
+{
+ os_free(imv->name);
+ os_free(imv->path);
+ os_free(imv->supported_types);
+}
+
+static void tncs_unload_imv(struct tnc_if_imv *imv)
+{
+ tncs_imv_terminate(imv);
+
+ if (imv->dlhandle)
+ dlclose(imv->dlhandle);
+
+ tncs_free_imv(imv);
+}
+
+
+static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type)
+{
+ size_t i;
+ unsigned int vendor, subtype;
+
+ if (imv == NULL || imv->supported_types == NULL)
+ return 0;
+
+ vendor = type >> 8;
+ subtype = type & 0xff;
+
+ for (i = 0; i < imv->num_supported_types; i++) {
+ unsigned int svendor, ssubtype;
+ svendor = imv->supported_types[i] >> 8;
+ ssubtype = imv->supported_types[i] & 0xff;
+ if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
+ (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type,
+ const u8 *msg, size_t len)
+{
+ struct tnc_if_imv *imv;
+ TNC_Result res;
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len);
+
+ for (imv = tncs->imv; imv; imv = imv->next) {
+ if (imv->ReceiveMessage == NULL ||
+ !tncs_supported_type(imv, type))
+ continue;
+
+ wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'",
+ imv->name);
+ res = imv->ReceiveMessage(imv->imvID, tncs->connectionID,
+ (TNC_BufferReference) msg, len,
+ type);
+ wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
+ (unsigned long) res);
+ }
+}
+
+
+static void tncs_batch_ending(struct tncs_data *tncs)
+{
+ struct tnc_if_imv *imv;
+ TNC_Result res;
+
+ for (imv = tncs->imv; imv; imv = imv->next) {
+ if (imv->BatchEnding == NULL)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'",
+ imv->name);
+ res = imv->BatchEnding(imv->imvID, tncs->connectionID);
+ wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu",
+ (unsigned long) res);
+ }
+}
+
+
+static void tncs_solicit_recommendation(struct tncs_data *tncs)
+{
+ struct tnc_if_imv *imv;
+ TNC_Result res;
+
+ for (imv = tncs->imv; imv; imv = imv->next) {
+ if (tncs->imv_data[imv->imvID].recommendation_set)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for "
+ "IMV '%s'", imv->name);
+ res = imv->SolicitRecommendation(imv->imvID,
+ tncs->connectionID);
+ wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu",
+ (unsigned long) res);
+ }
+}
+
+
+void tncs_init_connection(struct tncs_data *tncs)
+{
+ struct tnc_if_imv *imv;
+ int i;
+
+ for (imv = tncs->imv; imv; imv = imv->next) {
+ tncs_imv_notify_connection_change(
+ imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE);
+ tncs_imv_notify_connection_change(
+ imv, tncs->connectionID,
+ TNC_CONNECTION_STATE_HANDSHAKE);
+ }
+
+ for (i = 0; i < TNC_MAX_IMV_ID; i++) {
+ os_free(tncs->imv_data[i].imv_send);
+ tncs->imv_data[i].imv_send = NULL;
+ tncs->imv_data[i].imv_send_len = 0;
+ }
+}
+
+
+size_t tncs_total_send_len(struct tncs_data *tncs)
+{
+ int i;
+ size_t len = 0;
+
+ for (i = 0; i < TNC_MAX_IMV_ID; i++)
+ len += tncs->imv_data[i].imv_send_len;
+ if (tncs->tncs_message)
+ len += os_strlen(tncs->tncs_message);
+ return len;
+}
+
+
+u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos)
+{
+ int i;
+
+ for (i = 0; i < TNC_MAX_IMV_ID; i++) {
+ if (tncs->imv_data[i].imv_send == NULL)
+ continue;
+
+ os_memcpy(pos, tncs->imv_data[i].imv_send,
+ tncs->imv_data[i].imv_send_len);
+ pos += tncs->imv_data[i].imv_send_len;
+ os_free(tncs->imv_data[i].imv_send);
+ tncs->imv_data[i].imv_send = NULL;
+ tncs->imv_data[i].imv_send_len = 0;
+ }
+
+ if (tncs->tncs_message) {
+ size_t len = os_strlen(tncs->tncs_message);
+ os_memcpy(pos, tncs->tncs_message, len);
+ pos += len;
+ os_free(tncs->tncs_message);
+ tncs->tncs_message = NULL;
+ }
+
+ return pos;
+}
+
+
+char * tncs_if_tnccs_start(struct tncs_data *tncs)
+{
+ char *buf = os_malloc(1000);
+ if (buf == NULL)
+ return NULL;
+ tncs->last_batchid++;
+ os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid);
+ return buf;
+}
+
+
+char * tncs_if_tnccs_end(void)
+{
+ char *buf = os_malloc(100);
+ if (buf == NULL)
+ return NULL;
+ os_snprintf(buf, 100, IF_TNCCS_END);
+ return buf;
+}
+
+
+static int tncs_get_type(char *start, unsigned int *type)
+{
+ char *pos = os_strstr(start, "<Type>");
+ if (pos == NULL)
+ return -1;
+ pos += 6;
+ *type = strtoul(pos, NULL, 16);
+ return 0;
+}
+
+
+static unsigned char * tncs_get_base64(char *start, size_t *decoded_len)
+{
+ char *pos, *pos2;
+ unsigned char *decoded;
+
+ pos = os_strstr(start, "<Base64>");
+ if (pos == NULL)
+ return NULL;
+
+ pos += 8;
+ pos2 = os_strstr(pos, "</Base64>");
+ if (pos2 == NULL)
+ return NULL;
+ *pos2 = '\0';
+
+ decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
+ decoded_len);
+ *pos2 = '<';
+ if (decoded == NULL) {
+ wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
+ }
+
+ return decoded;
+}
+
+
+static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
+{
+ enum IMV_Action_Recommendation rec;
+ struct tnc_if_imv *imv;
+ TNC_ConnectionState state;
+ char *txt;
+
+ wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
+
+ if (tncs->done)
+ return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
+
+ tncs_solicit_recommendation(tncs);
+
+ /* Select the most restrictive recommendation */
+ rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
+ for (imv = tncs->imv; imv; imv = imv->next) {
+ TNC_IMV_Action_Recommendation irec;
+ irec = tncs->imv_data[imv->imvID].recommendation;
+ if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
+ rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
+ if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
+ rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
+ rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
+ if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
+ rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
+ rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
+ }
+
+ wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
+ tncs->recommendation = rec;
+ tncs->done = 1;
+
+ txt = NULL;
+ switch (rec) {
+ case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
+ case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
+ txt = "allow";
+ state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
+ break;
+ case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
+ txt = "isolate";
+ state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
+ break;
+ case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
+ txt = "none";
+ state = TNC_CONNECTION_STATE_ACCESS_NONE;
+ break;
+ default:
+ state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
+ break;
+ }
+
+ if (txt) {
+ os_free(tncs->tncs_message);
+ tncs->tncs_message = os_zalloc(200);
+ if (tncs->tncs_message) {
+ os_snprintf(tncs->tncs_message, 199,
+ "<TNCC-TNCS-Message><Type>%08X</Type>"
+ "<XML><TNCCS-Recommendation type=\"%s\">"
+ "</TNCCS-Recommendation></XML>"
+ "</TNCC-TNCS-Message>",
+ TNC_TNCCS_RECOMMENDATION, txt);
+ }
+ }
+
+ for (imv = tncs->imv; imv; imv = imv->next) {
+ tncs_imv_notify_connection_change(imv, tncs->connectionID,
+ state);
+ }
+
+ switch (rec) {
+ case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
+ return TNCCS_RECOMMENDATION_ALLOW;
+ case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
+ return TNCCS_RECOMMENDATION_NO_ACCESS;
+ case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
+ return TNCCS_RECOMMENDATION_ISOLATE;
+ case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
+ return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
+ default:
+ return TNCCS_PROCESS_ERROR;
+ }
+}
+
+
+enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
+ const u8 *msg, size_t len)
+{
+ char *buf, *start, *end, *pos, *pos2, *payload;
+ unsigned int batch_id;
+ unsigned char *decoded;
+ size_t decoded_len;
+
+ buf = os_malloc(len + 1);
+ if (buf == NULL)
+ return TNCCS_PROCESS_ERROR;
+
+ os_memcpy(buf, msg, len);
+ buf[len] = '\0';
+ start = os_strstr(buf, "<TNCCS-Batch ");
+ end = os_strstr(buf, "</TNCCS-Batch>");
+ if (start == NULL || end == NULL || start > end) {
+ os_free(buf);
+ return TNCCS_PROCESS_ERROR;
+ }
+
+ start += 13;
+ while (*start == ' ')
+ start++;
+ *end = '\0';
+
+ pos = os_strstr(start, "BatchId=");
+ if (pos == NULL) {
+ os_free(buf);
+ return TNCCS_PROCESS_ERROR;
+ }
+
+ pos += 8;
+ if (*pos == '"')
+ pos++;
+ batch_id = atoi(pos);
+ wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
+ batch_id);
+ if (batch_id != tncs->last_batchid + 1) {
+ wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
+ "%u (expected %u)",
+ batch_id, tncs->last_batchid + 1);
+ os_free(buf);
+ return TNCCS_PROCESS_ERROR;
+ }
+ tncs->last_batchid = batch_id;
+
+ while (*pos != '\0' && *pos != '>')
+ pos++;
+ if (*pos == '\0') {
+ os_free(buf);
+ return TNCCS_PROCESS_ERROR;
+ }
+ pos++;
+ payload = start;
+
+ /*
+ * <IMC-IMV-Message>
+ * <Type>01234567</Type>
+ * <Base64>foo==</Base64>
+ * </IMC-IMV-Message>
+ */
+
+ while (*start) {
+ char *endpos;
+ unsigned int type;
+
+ pos = os_strstr(start, "<IMC-IMV-Message>");
+ if (pos == NULL)
+ break;
+ start = pos + 17;
+ end = os_strstr(start, "</IMC-IMV-Message>");
+ if (end == NULL)
+ break;
+ *end = '\0';
+ endpos = end;
+ end += 18;
+
+ if (tncs_get_type(start, &type) < 0) {
+ *endpos = '<';
+ start = end;
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
+
+ decoded = tncs_get_base64(start, &decoded_len);
+ if (decoded == NULL) {
+ *endpos = '<';
+ start = end;
+ continue;
+ }
+
+ tncs_send_to_imvs(tncs, type, decoded, decoded_len);
+
+ os_free(decoded);
+
+ start = end;
+ }
+
+ /*
+ * <TNCC-TNCS-Message>
+ * <Type>01234567</Type>
+ * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
+ * <Base64>foo==</Base64>
+ * </TNCC-TNCS-Message>
+ */
+
+ start = payload;
+ while (*start) {
+ unsigned int type;
+ char *xml, *xmlend, *endpos;
+
+ pos = os_strstr(start, "<TNCC-TNCS-Message>");
+ if (pos == NULL)
+ break;
+ start = pos + 19;
+ end = os_strstr(start, "</TNCC-TNCS-Message>");
+ if (end == NULL)
+ break;
+ *end = '\0';
+ endpos = end;
+ end += 20;
+
+ if (tncs_get_type(start, &type) < 0) {
+ *endpos = '<';
+ start = end;
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
+ type);
+
+ /* Base64 OR XML */
+ decoded = NULL;
+ xml = NULL;
+ xmlend = NULL;
+ pos = os_strstr(start, "<XML>");
+ if (pos) {
+ pos += 5;
+ pos2 = os_strstr(pos, "</XML>");
+ if (pos2 == NULL) {
+ *endpos = '<';
+ start = end;
+ continue;
+ }
+ xmlend = pos2;
+ xml = pos;
+ } else {
+ decoded = tncs_get_base64(start, &decoded_len);
+ if (decoded == NULL) {
+ *endpos = '<';
+ start = end;
+ continue;
+ }
+ }
+
+ if (decoded) {
+ wpa_hexdump_ascii(MSG_MSGDUMP,
+ "TNC: TNCC-TNCS-Message Base64",
+ decoded, decoded_len);
+ os_free(decoded);
+ }
+
+ if (xml) {
+ wpa_hexdump_ascii(MSG_MSGDUMP,
+ "TNC: TNCC-TNCS-Message XML",
+ (unsigned char *) xml,
+ xmlend - xml);
+ }
+
+ start = end;
+ }
+
+ os_free(buf);
+
+ tncs_batch_ending(tncs);
+
+ if (tncs_total_send_len(tncs) == 0)
+ return tncs_derive_recommendation(tncs);
+
+ return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
+}
+
+
+static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
+ int *error)
+{
+ struct tnc_if_imv *imv;
+ char *pos, *pos2;
+
+ if (id >= TNC_MAX_IMV_ID) {
+ wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
+ return NULL;
+ }
+
+ imv = os_zalloc(sizeof(*imv));
+ if (imv == NULL) {
+ *error = 1;
+ return NULL;
+ }
+
+ imv->imvID = id;
+
+ pos = start;
+ wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
+ if (pos + 1 >= end || *pos != '"') {
+ wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
+ "(no starting quotation mark)", start);
+ os_free(imv);
+ return NULL;
+ }
+
+ pos++;
+ pos2 = pos;
+ while (pos2 < end && *pos2 != '"')
+ pos2++;
+ if (pos2 >= end) {
+ wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
+ "(no ending quotation mark)", start);
+ os_free(imv);
+ return NULL;
+ }
+ *pos2 = '\0';
+ wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
+ imv->name = os_strdup(pos);
+
+ pos = pos2 + 1;
+ if (pos >= end || *pos != ' ') {
+ wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
+ "(no space after name)", start);
+ os_free(imv);
+ return NULL;
+ }
+
+ pos++;
+ wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
+ imv->path = os_strdup(pos);
+
+ return imv;
+}
+
+
+static int tncs_read_config(struct tncs_global *global)
+{
+ char *config, *end, *pos, *line_end;
+ size_t config_len;
+ struct tnc_if_imv *imv, *last;
+ int id = 0;
+
+ last = NULL;
+
+ config = os_readfile(TNC_CONFIG_FILE, &config_len);
+ if (config == NULL) {
+ wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
+ "file '%s'", TNC_CONFIG_FILE);
+ return -1;
+ }
+
+ end = config + config_len;
+ for (pos = config; pos < end; pos = line_end + 1) {
+ line_end = pos;
+ while (*line_end != '\n' && *line_end != '\r' &&
+ line_end < end)
+ line_end++;
+ *line_end = '\0';
+
+ if (os_strncmp(pos, "IMV ", 4) == 0) {
+ int error = 0;
+
+ imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
+ if (error)
+ return -1;
+ if (imv) {
+ if (last == NULL)
+ global->imv = imv;
+ else
+ last->next = imv;
+ last = imv;
+ }
+ }
+ }
+
+ os_free(config);
+
+ return 0;
+}
+
+
+struct tncs_data * tncs_init(void)
+{
+ struct tncs_data *tncs;
+
+ if (tncs_global_data == NULL)
+ return NULL;
+
+ tncs = os_zalloc(sizeof(*tncs));
+ if (tncs == NULL)
+ return NULL;
+ tncs->imv = tncs_global_data->imv;
+ tncs->connectionID = tncs_global_data->next_conn_id++;
+ tncs->next = tncs_global_data->connections;
+ tncs_global_data->connections = tncs;
+
+ return tncs;
+}
+
+
+void tncs_deinit(struct tncs_data *tncs)
+{
+ int i;
+ struct tncs_data *prev, *conn;
+
+ if (tncs == NULL)
+ return;
+
+ for (i = 0; i < TNC_MAX_IMV_ID; i++)
+ os_free(tncs->imv_data[i].imv_send);
+
+ prev = NULL;
+ conn = tncs_global_data->connections;
+ while (conn) {
+ if (conn == tncs) {
+ if (prev)
+ prev->next = tncs->next;
+ else
+ tncs_global_data->connections = tncs->next;
+ break;
+ }
+ prev = conn;
+ conn = conn->next;
+ }
+
+ os_free(tncs->tncs_message);
+ os_free(tncs);
+}
+
+
+int tncs_global_init(void)
+{
+ struct tnc_if_imv *imv;
+
+ tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
+ if (tncs_global_data == NULL)
+ return -1;
+
+ if (tncs_read_config(tncs_global_data) < 0) {
+ wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
+ goto failed;
+ }
+
+ for (imv = tncs_global_data->imv; imv; imv = imv->next) {
+ if (tncs_load_imv(imv)) {
+ wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
+ imv->name);
+ goto failed;
+ }
+ }
+
+ return 0;
+
+failed:
+ tncs_global_deinit();
+ return -1;
+}
+
+
+void tncs_global_deinit(void)
+{
+ struct tnc_if_imv *imv, *prev;
+
+ if (tncs_global_data == NULL)
+ return;
+
+ imv = tncs_global_data->imv;
+ while (imv) {
+ tncs_unload_imv(imv);
+
+ prev = imv;
+ imv = imv->next;
+ os_free(prev);
+ }
+
+ os_free(tncs_global_data);
+ tncs_global_data = NULL;
+}
+
+
+struct wpabuf * tncs_build_soh_request(void)
+{
+ struct wpabuf *buf;
+
+ /*
+ * Build a SoH Request TLV (to be used inside SoH EAP Extensions
+ * Method)
+ */
+
+ buf = wpabuf_alloc(8 + 4);
+ if (buf == NULL)
+ return NULL;
+
+ /* Vendor-Specific TLV (Microsoft) - SoH Request */
+ wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
+ wpabuf_put_be16(buf, 8); /* Length */
+
+ wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
+
+ wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
+ wpabuf_put_be16(buf, 0); /* Length */
+
+ return buf;
+}
+
+
+struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
+ int *failure)
+{
+ wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
+ *failure = 0;
+
+ /* TODO: return MS-SoH Response TLV */
+
+ return NULL;
+}
diff --git a/src/eap_server/tncs.h b/src/eap_server/tncs.h
new file mode 100644
index 0000000..18a3a1f
--- /dev/null
+++ b/src/eap_server/tncs.h
@@ -0,0 +1,49 @@
+/*
+ * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
+ * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef TNCS_H
+#define TNCS_H
+
+struct tncs_data;
+
+struct tncs_data * tncs_init(void);
+void tncs_deinit(struct tncs_data *tncs);
+void tncs_init_connection(struct tncs_data *tncs);
+size_t tncs_total_send_len(struct tncs_data *tncs);
+u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos);
+char * tncs_if_tnccs_start(struct tncs_data *tncs);
+char * tncs_if_tnccs_end(void);
+
+enum tncs_process_res {
+ TNCCS_PROCESS_ERROR = -1,
+ TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0,
+ TNCCS_RECOMMENDATION_ERROR,
+ TNCCS_RECOMMENDATION_ALLOW,
+ TNCCS_RECOMMENDATION_NONE,
+ TNCCS_RECOMMENDATION_ISOLATE,
+ TNCCS_RECOMMENDATION_NO_ACCESS,
+ TNCCS_RECOMMENDATION_NO_RECOMMENDATION
+};
+
+enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
+ const u8 *msg, size_t len);
+
+int tncs_global_init(void);
+void tncs_global_deinit(void);
+
+struct wpabuf * tncs_build_soh_request(void);
+struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
+ int *failure);
+
+#endif /* TNCS_H */
diff --git a/src/eapol_auth/Makefile b/src/eapol_auth/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/eapol_auth/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/eapol_auth/eapol_auth_dump.c b/src/eapol_auth/eapol_auth_dump.c
new file mode 100644
index 0000000..a0f0e8d
--- /dev/null
+++ b/src/eapol_auth/eapol_auth_dump.c
@@ -0,0 +1,231 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - State dump
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_server/eap.h"
+#include "eapol_auth_sm.h"
+#include "eapol_auth_sm_i.h"
+
+static inline const char * port_type_txt(PortTypes pt)
+{
+ switch (pt) {
+ case ForceUnauthorized: return "ForceUnauthorized";
+ case ForceAuthorized: return "ForceAuthorized";
+ case Auto: return "Auto";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * port_state_txt(PortState ps)
+{
+ switch (ps) {
+ case Unauthorized: return "Unauthorized";
+ case Authorized: return "Authorized";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * ctrl_dir_txt(ControlledDirection dir)
+{
+ switch (dir) {
+ case Both: return "Both";
+ case In: return "In";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * auth_pae_state_txt(int s)
+{
+ switch (s) {
+ case AUTH_PAE_INITIALIZE: return "INITIALIZE";
+ case AUTH_PAE_DISCONNECTED: return "DISCONNECTED";
+ case AUTH_PAE_CONNECTING: return "CONNECTING";
+ case AUTH_PAE_AUTHENTICATING: return "AUTHENTICATING";
+ case AUTH_PAE_AUTHENTICATED: return "AUTHENTICATED";
+ case AUTH_PAE_ABORTING: return "ABORTING";
+ case AUTH_PAE_HELD: return "HELD";
+ case AUTH_PAE_FORCE_AUTH: return "FORCE_AUTH";
+ case AUTH_PAE_FORCE_UNAUTH: return "FORCE_UNAUTH";
+ case AUTH_PAE_RESTART: return "RESTART";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * be_auth_state_txt(int s)
+{
+ switch (s) {
+ case BE_AUTH_REQUEST: return "REQUEST";
+ case BE_AUTH_RESPONSE: return "RESPONSE";
+ case BE_AUTH_SUCCESS: return "SUCCESS";
+ case BE_AUTH_FAIL: return "FAIL";
+ case BE_AUTH_TIMEOUT: return "TIMEOUT";
+ case BE_AUTH_IDLE: return "IDLE";
+ case BE_AUTH_INITIALIZE: return "INITIALIZE";
+ case BE_AUTH_IGNORE: return "IGNORE";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * reauth_timer_state_txt(int s)
+{
+ switch (s) {
+ case REAUTH_TIMER_INITIALIZE: return "INITIALIZE";
+ case REAUTH_TIMER_REAUTHENTICATE: return "REAUTHENTICATE";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * auth_key_tx_state_txt(int s)
+{
+ switch (s) {
+ case AUTH_KEY_TX_NO_KEY_TRANSMIT: return "NO_KEY_TRANSMIT";
+ case AUTH_KEY_TX_KEY_TRANSMIT: return "KEY_TRANSMIT";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * key_rx_state_txt(int s)
+{
+ switch (s) {
+ case KEY_RX_NO_KEY_RECEIVE: return "NO_KEY_RECEIVE";
+ case KEY_RX_KEY_RECEIVE: return "KEY_RECEIVE";
+ default: return "Unknown";
+ }
+}
+
+
+static inline const char * ctrl_dir_state_txt(int s)
+{
+ switch (s) {
+ case CTRL_DIR_FORCE_BOTH: return "FORCE_BOTH";
+ case CTRL_DIR_IN_OR_BOTH: return "IN_OR_BOTH";
+ default: return "Unknown";
+ }
+}
+
+
+void eapol_auth_dump_state(FILE *f, const char *prefix,
+ struct eapol_state_machine *sm)
+{
+ fprintf(f, "%sEAPOL state machine:\n", prefix);
+ fprintf(f, "%s aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix,
+ sm->aWhile, sm->quietWhile, sm->reAuthWhen);
+#define _SB(b) ((b) ? "TRUE" : "FALSE")
+ fprintf(f,
+ "%s authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n"
+ "%s authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n"
+ "%s eapSuccess=%s eapTimeout=%s initialize=%s "
+ "keyAvailable=%s\n"
+ "%s keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n"
+ "%s portEnabled=%s portValid=%s reAuthenticate=%s\n",
+ prefix, _SB(sm->authAbort), _SB(sm->authFail),
+ port_state_txt(sm->authPortStatus), _SB(sm->authStart),
+ prefix, _SB(sm->authTimeout), _SB(sm->authSuccess),
+ _SB(sm->eap_if->eapFail), _SB(sm->eapolEap),
+ prefix, _SB(sm->eap_if->eapSuccess),
+ _SB(sm->eap_if->eapTimeout),
+ _SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable),
+ prefix, _SB(sm->keyDone), _SB(sm->keyRun),
+ _SB(sm->keyTxEnabled), port_type_txt(sm->portControl),
+ prefix, _SB(sm->eap_if->portEnabled), _SB(sm->portValid),
+ _SB(sm->reAuthenticate));
+
+ fprintf(f, "%s Authenticator PAE:\n"
+ "%s state=%s\n"
+ "%s eapolLogoff=%s eapolStart=%s eapRestart=%s\n"
+ "%s portMode=%s reAuthCount=%d\n"
+ "%s quietPeriod=%d reAuthMax=%d\n"
+ "%s authEntersConnecting=%d\n"
+ "%s authEapLogoffsWhileConnecting=%d\n"
+ "%s authEntersAuthenticating=%d\n"
+ "%s authAuthSuccessesWhileAuthenticating=%d\n"
+ "%s authAuthTimeoutsWhileAuthenticating=%d\n"
+ "%s authAuthFailWhileAuthenticating=%d\n"
+ "%s authAuthEapStartsWhileAuthenticating=%d\n"
+ "%s authAuthEapLogoffWhileAuthenticating=%d\n"
+ "%s authAuthReauthsWhileAuthenticated=%d\n"
+ "%s authAuthEapStartsWhileAuthenticated=%d\n"
+ "%s authAuthEapLogoffWhileAuthenticated=%d\n",
+ prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix,
+ _SB(sm->eapolLogoff), _SB(sm->eapolStart),
+ _SB(sm->eap_if->eapRestart),
+ prefix, port_type_txt(sm->portMode), sm->reAuthCount,
+ prefix, sm->quietPeriod, sm->reAuthMax,
+ prefix, sm->authEntersConnecting,
+ prefix, sm->authEapLogoffsWhileConnecting,
+ prefix, sm->authEntersAuthenticating,
+ prefix, sm->authAuthSuccessesWhileAuthenticating,
+ prefix, sm->authAuthTimeoutsWhileAuthenticating,
+ prefix, sm->authAuthFailWhileAuthenticating,
+ prefix, sm->authAuthEapStartsWhileAuthenticating,
+ prefix, sm->authAuthEapLogoffWhileAuthenticating,
+ prefix, sm->authAuthReauthsWhileAuthenticated,
+ prefix, sm->authAuthEapStartsWhileAuthenticated,
+ prefix, sm->authAuthEapLogoffWhileAuthenticated);
+
+ fprintf(f, "%s Backend Authentication:\n"
+ "%s state=%s\n"
+ "%s eapNoReq=%s eapReq=%s eapResp=%s\n"
+ "%s serverTimeout=%d\n"
+ "%s backendResponses=%d\n"
+ "%s backendAccessChallenges=%d\n"
+ "%s backendOtherRequestsToSupplicant=%d\n"
+ "%s backendAuthSuccesses=%d\n"
+ "%s backendAuthFails=%d\n",
+ prefix, prefix,
+ be_auth_state_txt(sm->be_auth_state),
+ prefix, _SB(sm->eap_if->eapNoReq), _SB(sm->eap_if->eapReq),
+ _SB(sm->eap_if->eapResp),
+ prefix, sm->serverTimeout,
+ prefix, sm->backendResponses,
+ prefix, sm->backendAccessChallenges,
+ prefix, sm->backendOtherRequestsToSupplicant,
+ prefix, sm->backendAuthSuccesses,
+ prefix, sm->backendAuthFails);
+
+ fprintf(f, "%s Reauthentication Timer:\n"
+ "%s state=%s\n"
+ "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix,
+ reauth_timer_state_txt(sm->reauth_timer_state), prefix,
+ sm->reAuthPeriod, _SB(sm->reAuthEnabled));
+
+ fprintf(f, "%s Authenticator Key Transmit:\n"
+ "%s state=%s\n", prefix, prefix,
+ auth_key_tx_state_txt(sm->auth_key_tx_state));
+
+ fprintf(f, "%s Key Receive:\n"
+ "%s state=%s\n"
+ "%s rxKey=%s\n", prefix, prefix,
+ key_rx_state_txt(sm->key_rx_state), prefix, _SB(sm->rxKey));
+
+ fprintf(f, "%s Controlled Directions:\n"
+ "%s state=%s\n"
+ "%s adminControlledDirections=%s "
+ "operControlledDirections=%s\n"
+ "%s operEdge=%s\n", prefix, prefix,
+ ctrl_dir_state_txt(sm->ctrl_dir_state),
+ prefix, ctrl_dir_txt(sm->adminControlledDirections),
+ ctrl_dir_txt(sm->operControlledDirections),
+ prefix, _SB(sm->operEdge));
+#undef _SB
+}
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
new file mode 100644
index 0000000..841a1c5
--- /dev/null
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -0,0 +1,1145 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - EAPOL state machine
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "state_machine.h"
+#include "common/eapol_common.h"
+#include "eap_common/eap_defs.h"
+#include "eap_common/eap_common.h"
+#include "eap_server/eap.h"
+#include "eapol_auth_sm.h"
+#include "eapol_auth_sm_i.h"
+
+#define STATE_MACHINE_DATA struct eapol_state_machine
+#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X"
+#define STATE_MACHINE_ADDR sm->addr
+
+static struct eapol_callbacks eapol_cb;
+
+/* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */
+
+#define setPortAuthorized() \
+sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 1)
+#define setPortUnauthorized() \
+sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0)
+
+/* procedures */
+#define txCannedFail() eapol_auth_tx_canned_eap(sm, 0)
+#define txCannedSuccess() eapol_auth_tx_canned_eap(sm, 1)
+#define txReq() eapol_auth_tx_req(sm)
+#define abortAuth() sm->eapol->cb.abort_auth(sm->eapol->conf.ctx, sm->sta)
+#define txKey() sm->eapol->cb.tx_key(sm->eapol->conf.ctx, sm->sta)
+#define processKey() do { } while (0)
+
+
+static void eapol_sm_step_run(struct eapol_state_machine *sm);
+static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx);
+static void eapol_auth_initialize(struct eapol_state_machine *sm);
+
+
+static void eapol_auth_logger(struct eapol_authenticator *eapol,
+ const u8 *addr, eapol_logger_level level,
+ const char *txt)
+{
+ if (eapol->cb.logger == NULL)
+ return;
+ eapol->cb.logger(eapol->conf.ctx, addr, level, txt);
+}
+
+
+static void eapol_auth_vlogger(struct eapol_authenticator *eapol,
+ const u8 *addr, eapol_logger_level level,
+ const char *fmt, ...)
+{
+ char *format;
+ int maxlen;
+ va_list ap;
+
+ if (eapol->cb.logger == NULL)
+ return;
+
+ maxlen = os_strlen(fmt) + 100;
+ format = os_malloc(maxlen);
+ if (!format)
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(format, maxlen, fmt, ap);
+ va_end(ap);
+
+ eapol_auth_logger(eapol, addr, level, format);
+
+ os_free(format);
+}
+
+
+static void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm,
+ int success)
+{
+ struct eap_hdr eap;
+
+ os_memset(&eap, 0, sizeof(eap));
+
+ eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE;
+ eap.identifier = ++sm->last_eap_id;
+ eap.length = host_to_be16(sizeof(eap));
+
+ eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG,
+ "Sending canned EAP packet %s (identifier %d)",
+ success ? "SUCCESS" : "FAILURE", eap.identifier);
+ sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta,
+ IEEE802_1X_TYPE_EAP_PACKET,
+ (u8 *) &eap, sizeof(eap));
+ sm->dot1xAuthEapolFramesTx++;
+}
+
+
+static void eapol_auth_tx_req(struct eapol_state_machine *sm)
+{
+ if (sm->eap_if->eapReqData == NULL ||
+ wpabuf_len(sm->eap_if->eapReqData) < sizeof(struct eap_hdr)) {
+ eapol_auth_logger(sm->eapol, sm->addr,
+ EAPOL_LOGGER_DEBUG,
+ "TxReq called, but there is no EAP request "
+ "from authentication server");
+ return;
+ }
+
+ if (sm->flags & EAPOL_SM_WAIT_START) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Drop EAPOL TX to " MACSTR
+ " while waiting for EAPOL-Start",
+ MAC2STR(sm->addr));
+ return;
+ }
+
+ sm->last_eap_id = eap_get_id(sm->eap_if->eapReqData);
+ eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG,
+ "Sending EAP Packet (identifier %d)",
+ sm->last_eap_id);
+ sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta,
+ IEEE802_1X_TYPE_EAP_PACKET,
+ wpabuf_head(sm->eap_if->eapReqData),
+ wpabuf_len(sm->eap_if->eapReqData));
+ sm->dot1xAuthEapolFramesTx++;
+ if (eap_get_type(sm->eap_if->eapReqData) == EAP_TYPE_IDENTITY)
+ sm->dot1xAuthEapolReqIdFramesTx++;
+ else
+ sm->dot1xAuthEapolReqFramesTx++;
+}
+
+
+/**
+ * eapol_port_timers_tick - Port Timers state machine
+ * @eloop_ctx: struct eapol_state_machine *
+ * @timeout_ctx: Not used
+ *
+ * This statemachine is implemented as a function that will be called
+ * once a second as a registered event loop timeout.
+ */
+static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
+{
+ struct eapol_state_machine *state = timeout_ctx;
+
+ if (state->aWhile > 0) {
+ state->aWhile--;
+ if (state->aWhile == 0) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
+ " - aWhile --> 0",
+ MAC2STR(state->addr));
+ }
+ }
+
+ if (state->quietWhile > 0) {
+ state->quietWhile--;
+ if (state->quietWhile == 0) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
+ " - quietWhile --> 0",
+ MAC2STR(state->addr));
+ }
+ }
+
+ if (state->reAuthWhen > 0) {
+ state->reAuthWhen--;
+ if (state->reAuthWhen == 0) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
+ " - reAuthWhen --> 0",
+ MAC2STR(state->addr));
+ }
+ }
+
+ if (state->eap_if->retransWhile > 0) {
+ state->eap_if->retransWhile--;
+ if (state->eap_if->retransWhile == 0) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
+ " - (EAP) retransWhile --> 0",
+ MAC2STR(state->addr));
+ }
+ }
+
+ eapol_sm_step_run(state);
+
+ eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state);
+}
+
+
+
+/* Authenticator PAE state machine */
+
+SM_STATE(AUTH_PAE, INITIALIZE)
+{
+ SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae);
+ sm->portMode = Auto;
+}
+
+
+SM_STATE(AUTH_PAE, DISCONNECTED)
+{
+ int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE;
+
+ if (sm->eapolLogoff) {
+ if (sm->auth_pae_state == AUTH_PAE_CONNECTING)
+ sm->authEapLogoffsWhileConnecting++;
+ else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED)
+ sm->authAuthEapLogoffWhileAuthenticated++;
+ }
+
+ SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae);
+
+ sm->authPortStatus = Unauthorized;
+ setPortUnauthorized();
+ sm->reAuthCount = 0;
+ sm->eapolLogoff = FALSE;
+ if (!from_initialize) {
+ sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
+ sm->flags & EAPOL_SM_PREAUTH);
+ }
+}
+
+
+SM_STATE(AUTH_PAE, RESTART)
+{
+ if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) {
+ if (sm->reAuthenticate)
+ sm->authAuthReauthsWhileAuthenticated++;
+ if (sm->eapolStart)
+ sm->authAuthEapStartsWhileAuthenticated++;
+ if (sm->eapolLogoff)
+ sm->authAuthEapLogoffWhileAuthenticated++;
+ }
+
+ SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae);
+
+ sm->eap_if->eapRestart = TRUE;
+}
+
+
+SM_STATE(AUTH_PAE, CONNECTING)
+{
+ if (sm->auth_pae_state != AUTH_PAE_CONNECTING)
+ sm->authEntersConnecting++;
+
+ SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae);
+
+ sm->reAuthenticate = FALSE;
+ sm->reAuthCount++;
+}
+
+
+SM_STATE(AUTH_PAE, HELD)
+{
+ if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail)
+ sm->authAuthFailWhileAuthenticating++;
+
+ SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae);
+
+ sm->authPortStatus = Unauthorized;
+ setPortUnauthorized();
+ sm->quietWhile = sm->quietPeriod;
+ sm->eapolLogoff = FALSE;
+
+ eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING,
+ "authentication failed - EAP type: %d (%s)",
+ sm->eap_type_authsrv,
+ eap_server_get_name(0, sm->eap_type_authsrv));
+ if (sm->eap_type_authsrv != sm->eap_type_supp) {
+ eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO,
+ "Supplicant used different EAP type: "
+ "%d (%s)", sm->eap_type_supp,
+ eap_server_get_name(0, sm->eap_type_supp));
+ }
+ sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
+ sm->flags & EAPOL_SM_PREAUTH);
+}
+
+
+SM_STATE(AUTH_PAE, AUTHENTICATED)
+{
+ char *extra = "";
+
+ if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess)
+ sm->authAuthSuccessesWhileAuthenticating++;
+
+ SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae);
+
+ sm->authPortStatus = Authorized;
+ setPortAuthorized();
+ sm->reAuthCount = 0;
+ if (sm->flags & EAPOL_SM_PREAUTH)
+ extra = " (pre-authentication)";
+ else if (sm->flags & EAPOL_SM_FROM_PMKSA_CACHE)
+ extra = " (PMKSA cache)";
+ eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO,
+ "authenticated - EAP type: %d (%s)%s",
+ sm->eap_type_authsrv,
+ eap_server_get_name(0, sm->eap_type_authsrv),
+ extra);
+ sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1,
+ sm->flags & EAPOL_SM_PREAUTH);
+}
+
+
+SM_STATE(AUTH_PAE, AUTHENTICATING)
+{
+ SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae);
+
+ sm->eapolStart = FALSE;
+ sm->authSuccess = FALSE;
+ sm->authFail = FALSE;
+ sm->authTimeout = FALSE;
+ sm->authStart = TRUE;
+ sm->keyRun = FALSE;
+ sm->keyDone = FALSE;
+}
+
+
+SM_STATE(AUTH_PAE, ABORTING)
+{
+ if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) {
+ if (sm->authTimeout)
+ sm->authAuthTimeoutsWhileAuthenticating++;
+ if (sm->eapolStart)
+ sm->authAuthEapStartsWhileAuthenticating++;
+ if (sm->eapolLogoff)
+ sm->authAuthEapLogoffWhileAuthenticating++;
+ }
+
+ SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae);
+
+ sm->authAbort = TRUE;
+ sm->keyRun = FALSE;
+ sm->keyDone = FALSE;
+}
+
+
+SM_STATE(AUTH_PAE, FORCE_AUTH)
+{
+ SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae);
+
+ sm->authPortStatus = Authorized;
+ setPortAuthorized();
+ sm->portMode = ForceAuthorized;
+ sm->eapolStart = FALSE;
+ txCannedSuccess();
+}
+
+
+SM_STATE(AUTH_PAE, FORCE_UNAUTH)
+{
+ SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae);
+
+ sm->authPortStatus = Unauthorized;
+ setPortUnauthorized();
+ sm->portMode = ForceUnauthorized;
+ sm->eapolStart = FALSE;
+ txCannedFail();
+}
+
+
+SM_STEP(AUTH_PAE)
+{
+ if ((sm->portControl == Auto && sm->portMode != sm->portControl) ||
+ sm->initialize || !sm->eap_if->portEnabled)
+ SM_ENTER_GLOBAL(AUTH_PAE, INITIALIZE);
+ else if (sm->portControl == ForceAuthorized &&
+ sm->portMode != sm->portControl &&
+ !(sm->initialize || !sm->eap_if->portEnabled))
+ SM_ENTER_GLOBAL(AUTH_PAE, FORCE_AUTH);
+ else if (sm->portControl == ForceUnauthorized &&
+ sm->portMode != sm->portControl &&
+ !(sm->initialize || !sm->eap_if->portEnabled))
+ SM_ENTER_GLOBAL(AUTH_PAE, FORCE_UNAUTH);
+ else {
+ switch (sm->auth_pae_state) {
+ case AUTH_PAE_INITIALIZE:
+ SM_ENTER(AUTH_PAE, DISCONNECTED);
+ break;
+ case AUTH_PAE_DISCONNECTED:
+ SM_ENTER(AUTH_PAE, RESTART);
+ break;
+ case AUTH_PAE_RESTART:
+ if (!sm->eap_if->eapRestart)
+ SM_ENTER(AUTH_PAE, CONNECTING);
+ break;
+ case AUTH_PAE_HELD:
+ if (sm->quietWhile == 0)
+ SM_ENTER(AUTH_PAE, RESTART);
+ break;
+ case AUTH_PAE_CONNECTING:
+ if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax)
+ SM_ENTER(AUTH_PAE, DISCONNECTED);
+ else if ((sm->eap_if->eapReq &&
+ sm->reAuthCount <= sm->reAuthMax) ||
+ sm->eap_if->eapSuccess || sm->eap_if->eapFail)
+ SM_ENTER(AUTH_PAE, AUTHENTICATING);
+ break;
+ case AUTH_PAE_AUTHENTICATED:
+ if (sm->eapolStart || sm->reAuthenticate)
+ SM_ENTER(AUTH_PAE, RESTART);
+ else if (sm->eapolLogoff || !sm->portValid)
+ SM_ENTER(AUTH_PAE, DISCONNECTED);
+ break;
+ case AUTH_PAE_AUTHENTICATING:
+ if (sm->authSuccess && sm->portValid)
+ SM_ENTER(AUTH_PAE, AUTHENTICATED);
+ else if (sm->authFail ||
+ (sm->keyDone && !sm->portValid))
+ SM_ENTER(AUTH_PAE, HELD);
+ else if (sm->eapolStart || sm->eapolLogoff ||
+ sm->authTimeout)
+ SM_ENTER(AUTH_PAE, ABORTING);
+ break;
+ case AUTH_PAE_ABORTING:
+ if (sm->eapolLogoff && !sm->authAbort)
+ SM_ENTER(AUTH_PAE, DISCONNECTED);
+ else if (!sm->eapolLogoff && !sm->authAbort)
+ SM_ENTER(AUTH_PAE, RESTART);
+ break;
+ case AUTH_PAE_FORCE_AUTH:
+ if (sm->eapolStart)
+ SM_ENTER(AUTH_PAE, FORCE_AUTH);
+ break;
+ case AUTH_PAE_FORCE_UNAUTH:
+ if (sm->eapolStart)
+ SM_ENTER(AUTH_PAE, FORCE_UNAUTH);
+ break;
+ }
+ }
+}
+
+
+
+/* Backend Authentication state machine */
+
+SM_STATE(BE_AUTH, INITIALIZE)
+{
+ SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth);
+
+ abortAuth();
+ sm->eap_if->eapNoReq = FALSE;
+ sm->authAbort = FALSE;
+}
+
+
+SM_STATE(BE_AUTH, REQUEST)
+{
+ SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth);
+
+ txReq();
+ sm->eap_if->eapReq = FALSE;
+ sm->backendOtherRequestsToSupplicant++;
+
+ /*
+ * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but
+ * it looks like this would be logical thing to do there since the old
+ * EAP response would not be valid anymore after the new EAP request
+ * was sent out.
+ *
+ * A race condition has been reported, in which hostapd ended up
+ * sending out EAP-Response/Identity as a response to the first
+ * EAP-Request from the main EAP method. This can be avoided by
+ * clearing eapolEap here.
+ */
+ sm->eapolEap = FALSE;
+}
+
+
+SM_STATE(BE_AUTH, RESPONSE)
+{
+ SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth);
+
+ sm->authTimeout = FALSE;
+ sm->eapolEap = FALSE;
+ sm->eap_if->eapNoReq = FALSE;
+ sm->aWhile = sm->serverTimeout;
+ sm->eap_if->eapResp = TRUE;
+ /* sendRespToServer(); */
+ sm->backendResponses++;
+}
+
+
+SM_STATE(BE_AUTH, SUCCESS)
+{
+ SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth);
+
+ txReq();
+ sm->authSuccess = TRUE;
+ sm->keyRun = TRUE;
+}
+
+
+SM_STATE(BE_AUTH, FAIL)
+{
+ SM_ENTRY_MA(BE_AUTH, FAIL, be_auth);
+
+ txReq();
+ sm->authFail = TRUE;
+}
+
+
+SM_STATE(BE_AUTH, TIMEOUT)
+{
+ SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth);
+
+ sm->authTimeout = TRUE;
+}
+
+
+SM_STATE(BE_AUTH, IDLE)
+{
+ SM_ENTRY_MA(BE_AUTH, IDLE, be_auth);
+
+ sm->authStart = FALSE;
+}
+
+
+SM_STATE(BE_AUTH, IGNORE)
+{
+ SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth);
+
+ sm->eap_if->eapNoReq = FALSE;
+}
+
+
+SM_STEP(BE_AUTH)
+{
+ if (sm->portControl != Auto || sm->initialize || sm->authAbort) {
+ SM_ENTER_GLOBAL(BE_AUTH, INITIALIZE);
+ return;
+ }
+
+ switch (sm->be_auth_state) {
+ case BE_AUTH_INITIALIZE:
+ SM_ENTER(BE_AUTH, IDLE);
+ break;
+ case BE_AUTH_REQUEST:
+ if (sm->eapolEap)
+ SM_ENTER(BE_AUTH, RESPONSE);
+ else if (sm->eap_if->eapReq)
+ SM_ENTER(BE_AUTH, REQUEST);
+ else if (sm->eap_if->eapTimeout)
+ SM_ENTER(BE_AUTH, TIMEOUT);
+ break;
+ case BE_AUTH_RESPONSE:
+ if (sm->eap_if->eapNoReq)
+ SM_ENTER(BE_AUTH, IGNORE);
+ if (sm->eap_if->eapReq) {
+ sm->backendAccessChallenges++;
+ SM_ENTER(BE_AUTH, REQUEST);
+ } else if (sm->aWhile == 0)
+ SM_ENTER(BE_AUTH, TIMEOUT);
+ else if (sm->eap_if->eapFail) {
+ sm->backendAuthFails++;
+ SM_ENTER(BE_AUTH, FAIL);
+ } else if (sm->eap_if->eapSuccess) {
+ sm->backendAuthSuccesses++;
+ SM_ENTER(BE_AUTH, SUCCESS);
+ }
+ break;
+ case BE_AUTH_SUCCESS:
+ SM_ENTER(BE_AUTH, IDLE);
+ break;
+ case BE_AUTH_FAIL:
+ SM_ENTER(BE_AUTH, IDLE);
+ break;
+ case BE_AUTH_TIMEOUT:
+ SM_ENTER(BE_AUTH, IDLE);
+ break;
+ case BE_AUTH_IDLE:
+ if (sm->eap_if->eapFail && sm->authStart)
+ SM_ENTER(BE_AUTH, FAIL);
+ else if (sm->eap_if->eapReq && sm->authStart)
+ SM_ENTER(BE_AUTH, REQUEST);
+ else if (sm->eap_if->eapSuccess && sm->authStart)
+ SM_ENTER(BE_AUTH, SUCCESS);
+ break;
+ case BE_AUTH_IGNORE:
+ if (sm->eapolEap)
+ SM_ENTER(BE_AUTH, RESPONSE);
+ else if (sm->eap_if->eapReq)
+ SM_ENTER(BE_AUTH, REQUEST);
+ else if (sm->eap_if->eapTimeout)
+ SM_ENTER(BE_AUTH, TIMEOUT);
+ break;
+ }
+}
+
+
+
+/* Reauthentication Timer state machine */
+
+SM_STATE(REAUTH_TIMER, INITIALIZE)
+{
+ SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer);
+
+ sm->reAuthWhen = sm->reAuthPeriod;
+}
+
+
+SM_STATE(REAUTH_TIMER, REAUTHENTICATE)
+{
+ SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer);
+
+ sm->reAuthenticate = TRUE;
+ sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta,
+ EAPOL_AUTH_REAUTHENTICATE);
+}
+
+
+SM_STEP(REAUTH_TIMER)
+{
+ if (sm->portControl != Auto || sm->initialize ||
+ sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) {
+ SM_ENTER_GLOBAL(REAUTH_TIMER, INITIALIZE);
+ return;
+ }
+
+ switch (sm->reauth_timer_state) {
+ case REAUTH_TIMER_INITIALIZE:
+ if (sm->reAuthWhen == 0)
+ SM_ENTER(REAUTH_TIMER, REAUTHENTICATE);
+ break;
+ case REAUTH_TIMER_REAUTHENTICATE:
+ SM_ENTER(REAUTH_TIMER, INITIALIZE);
+ break;
+ }
+}
+
+
+
+/* Authenticator Key Transmit state machine */
+
+SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT)
+{
+ SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx);
+}
+
+
+SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT)
+{
+ SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx);
+
+ txKey();
+ sm->eap_if->eapKeyAvailable = FALSE;
+ sm->keyDone = TRUE;
+}
+
+
+SM_STEP(AUTH_KEY_TX)
+{
+ if (sm->initialize || sm->portControl != Auto) {
+ SM_ENTER_GLOBAL(AUTH_KEY_TX, NO_KEY_TRANSMIT);
+ return;
+ }
+
+ switch (sm->auth_key_tx_state) {
+ case AUTH_KEY_TX_NO_KEY_TRANSMIT:
+ if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable &&
+ sm->keyRun && !(sm->flags & EAPOL_SM_USES_WPA))
+ SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT);
+ break;
+ case AUTH_KEY_TX_KEY_TRANSMIT:
+ if (!sm->keyTxEnabled || !sm->keyRun)
+ SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT);
+ else if (sm->eap_if->eapKeyAvailable)
+ SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT);
+ break;
+ }
+}
+
+
+
+/* Key Receive state machine */
+
+SM_STATE(KEY_RX, NO_KEY_RECEIVE)
+{
+ SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx);
+}
+
+
+SM_STATE(KEY_RX, KEY_RECEIVE)
+{
+ SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx);
+
+ processKey();
+ sm->rxKey = FALSE;
+}
+
+
+SM_STEP(KEY_RX)
+{
+ if (sm->initialize || !sm->eap_if->portEnabled) {
+ SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
+ return;
+ }
+
+ switch (sm->key_rx_state) {
+ case KEY_RX_NO_KEY_RECEIVE:
+ if (sm->rxKey)
+ SM_ENTER(KEY_RX, KEY_RECEIVE);
+ break;
+ case KEY_RX_KEY_RECEIVE:
+ if (sm->rxKey)
+ SM_ENTER(KEY_RX, KEY_RECEIVE);
+ break;
+ }
+}
+
+
+
+/* Controlled Directions state machine */
+
+SM_STATE(CTRL_DIR, FORCE_BOTH)
+{
+ SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir);
+ sm->operControlledDirections = Both;
+}
+
+
+SM_STATE(CTRL_DIR, IN_OR_BOTH)
+{
+ SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir);
+ sm->operControlledDirections = sm->adminControlledDirections;
+}
+
+
+SM_STEP(CTRL_DIR)
+{
+ if (sm->initialize) {
+ SM_ENTER_GLOBAL(CTRL_DIR, IN_OR_BOTH);
+ return;
+ }
+
+ switch (sm->ctrl_dir_state) {
+ case CTRL_DIR_FORCE_BOTH:
+ if (sm->eap_if->portEnabled && sm->operEdge)
+ SM_ENTER(CTRL_DIR, IN_OR_BOTH);
+ break;
+ case CTRL_DIR_IN_OR_BOTH:
+ if (sm->operControlledDirections !=
+ sm->adminControlledDirections)
+ SM_ENTER(CTRL_DIR, IN_OR_BOTH);
+ if (!sm->eap_if->portEnabled || !sm->operEdge)
+ SM_ENTER(CTRL_DIR, FORCE_BOTH);
+ break;
+ }
+}
+
+
+
+struct eapol_state_machine *
+eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
+ int flags, const struct wpabuf *assoc_wps_ie,
+ const struct wpabuf *assoc_p2p_ie, void *sta_ctx)
+{
+ struct eapol_state_machine *sm;
+ struct eap_config eap_conf;
+
+ if (eapol == NULL)
+ return NULL;
+
+ sm = os_zalloc(sizeof(*sm));
+ if (sm == NULL) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X state machine allocation "
+ "failed");
+ return NULL;
+ }
+ sm->radius_identifier = -1;
+ os_memcpy(sm->addr, addr, ETH_ALEN);
+ sm->flags = flags;
+
+ sm->eapol = eapol;
+ sm->sta = sta_ctx;
+
+ /* Set default values for state machine constants */
+ sm->auth_pae_state = AUTH_PAE_INITIALIZE;
+ sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod;
+ sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax;
+
+ sm->be_auth_state = BE_AUTH_INITIALIZE;
+ sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout;
+
+ sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE;
+ sm->reAuthPeriod = eapol->conf.eap_reauth_period;
+ sm->reAuthEnabled = eapol->conf.eap_reauth_period > 0 ? TRUE : FALSE;
+
+ sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT;
+
+ sm->key_rx_state = KEY_RX_NO_KEY_RECEIVE;
+
+ sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH;
+
+ sm->portControl = Auto;
+
+ if (!eapol->conf.wpa &&
+ (eapol->default_wep_key || eapol->conf.individual_wep_key_len > 0))
+ sm->keyTxEnabled = TRUE;
+ else
+ sm->keyTxEnabled = FALSE;
+ if (eapol->conf.wpa)
+ sm->portValid = FALSE;
+ else
+ sm->portValid = TRUE;
+
+ os_memset(&eap_conf, 0, sizeof(eap_conf));
+ eap_conf.eap_server = eapol->conf.eap_server;
+ eap_conf.ssl_ctx = eapol->conf.ssl_ctx;
+ eap_conf.msg_ctx = eapol->conf.msg_ctx;
+ eap_conf.eap_sim_db_priv = eapol->conf.eap_sim_db_priv;
+ eap_conf.pac_opaque_encr_key = eapol->conf.pac_opaque_encr_key;
+ eap_conf.eap_fast_a_id = eapol->conf.eap_fast_a_id;
+ eap_conf.eap_fast_a_id_len = eapol->conf.eap_fast_a_id_len;
+ eap_conf.eap_fast_a_id_info = eapol->conf.eap_fast_a_id_info;
+ eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov;
+ eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime;
+ eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time;
+ eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind;
+ eap_conf.tnc = eapol->conf.tnc;
+ eap_conf.wps = eapol->conf.wps;
+ eap_conf.assoc_wps_ie = assoc_wps_ie;
+ eap_conf.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;
+ sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
+ if (sm->eap == NULL) {
+ eapol_auth_free(sm);
+ return NULL;
+ }
+ sm->eap_if = eap_get_interface(sm->eap);
+
+ eapol_auth_initialize(sm);
+
+ return sm;
+}
+
+
+void eapol_auth_free(struct eapol_state_machine *sm)
+{
+ if (sm == NULL)
+ return;
+
+ eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+ eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL);
+ if (sm->eap)
+ eap_server_sm_deinit(sm->eap);
+ os_free(sm);
+}
+
+
+static int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol,
+ const u8 *addr)
+{
+ return eapol->cb.sta_entry_alive(eapol->conf.ctx, addr);
+}
+
+
+static void eapol_sm_step_run(struct eapol_state_machine *sm)
+{
+ struct eapol_authenticator *eapol = sm->eapol;
+ u8 addr[ETH_ALEN];
+ unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer,
+ prev_auth_key_tx, prev_key_rx, prev_ctrl_dir;
+ int max_steps = 100;
+
+ os_memcpy(addr, sm->addr, ETH_ALEN);
+
+ /*
+ * Allow EAPOL state machines to run as long as there are state
+ * changes, but exit and return here through event loop if more than
+ * 100 steps is needed as a precaution against infinite loops inside
+ * eloop callback.
+ */
+restart:
+ prev_auth_pae = sm->auth_pae_state;
+ prev_be_auth = sm->be_auth_state;
+ prev_reauth_timer = sm->reauth_timer_state;
+ prev_auth_key_tx = sm->auth_key_tx_state;
+ prev_key_rx = sm->key_rx_state;
+ prev_ctrl_dir = sm->ctrl_dir_state;
+
+ SM_STEP_RUN(AUTH_PAE);
+ if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
+ SM_STEP_RUN(BE_AUTH);
+ if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
+ SM_STEP_RUN(REAUTH_TIMER);
+ if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
+ SM_STEP_RUN(AUTH_KEY_TX);
+ if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
+ SM_STEP_RUN(KEY_RX);
+ if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
+ SM_STEP_RUN(CTRL_DIR);
+
+ if (prev_auth_pae != sm->auth_pae_state ||
+ prev_be_auth != sm->be_auth_state ||
+ prev_reauth_timer != sm->reauth_timer_state ||
+ prev_auth_key_tx != sm->auth_key_tx_state ||
+ prev_key_rx != sm->key_rx_state ||
+ prev_ctrl_dir != sm->ctrl_dir_state) {
+ if (--max_steps > 0)
+ goto restart;
+ /* Re-run from eloop timeout */
+ eapol_auth_step(sm);
+ return;
+ }
+
+ if (eapol_sm_sta_entry_alive(eapol, addr) && sm->eap) {
+ if (eap_server_sm_step(sm->eap)) {
+ if (--max_steps > 0)
+ goto restart;
+ /* Re-run from eloop timeout */
+ eapol_auth_step(sm);
+ return;
+ }
+
+ /* TODO: find a better location for this */
+ if (sm->eap_if->aaaEapResp) {
+ sm->eap_if->aaaEapResp = FALSE;
+ if (sm->eap_if->aaaEapRespData == NULL) {
+ wpa_printf(MSG_DEBUG, "EAPOL: aaaEapResp set, "
+ "but no aaaEapRespData available");
+ return;
+ }
+ sm->eapol->cb.aaa_send(
+ sm->eapol->conf.ctx, sm->sta,
+ wpabuf_head(sm->eap_if->aaaEapRespData),
+ wpabuf_len(sm->eap_if->aaaEapRespData));
+ }
+ }
+
+ if (eapol_sm_sta_entry_alive(eapol, addr))
+ sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta,
+ EAPOL_AUTH_SM_CHANGE);
+}
+
+
+static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct eapol_state_machine *sm = eloop_ctx;
+ eapol_sm_step_run(sm);
+}
+
+
+/**
+ * eapol_auth_step - Advance EAPOL state machines
+ * @sm: EAPOL state machine
+ *
+ * This function is called to advance EAPOL state machines after any change
+ * that could affect their state.
+ */
+void eapol_auth_step(struct eapol_state_machine *sm)
+{
+ /*
+ * Run eapol_sm_step_run from a registered timeout to make sure that
+ * other possible timeouts/events are processed and to avoid long
+ * function call chains.
+ */
+
+ eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL);
+}
+
+
+static void eapol_auth_initialize(struct eapol_state_machine *sm)
+{
+ sm->initializing = TRUE;
+ /* Initialize the state machines by asserting initialize and then
+ * deasserting it after one step */
+ sm->initialize = TRUE;
+ eapol_sm_step_run(sm);
+ sm->initialize = FALSE;
+ eapol_sm_step_run(sm);
+ sm->initializing = FALSE;
+
+ /* Start one second tick for port timers state machine */
+ eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+ eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+}
+
+
+static int eapol_sm_get_eap_user(void *ctx, const u8 *identity,
+ size_t identity_len, int phase2,
+ struct eap_user *user)
+{
+ struct eapol_state_machine *sm = ctx;
+ return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
+ identity_len, phase2, user);
+}
+
+
+static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len)
+{
+ struct eapol_state_machine *sm = ctx;
+ *len = sm->eapol->conf.eap_req_id_text_len;
+ return sm->eapol->conf.eap_req_id_text;
+}
+
+
+static struct eapol_callbacks eapol_cb =
+{
+ eapol_sm_get_eap_user,
+ eapol_sm_get_eap_req_id_text
+};
+
+
+int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx)
+{
+ if (sm == NULL || ctx != sm->eap)
+ return -1;
+
+ eap_sm_pending_cb(sm->eap);
+ eapol_auth_step(sm);
+
+ return 0;
+}
+
+
+static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
+ struct eapol_auth_config *src)
+{
+ dst->ctx = src->ctx;
+ dst->eap_reauth_period = src->eap_reauth_period;
+ dst->wpa = src->wpa;
+ dst->individual_wep_key_len = src->individual_wep_key_len;
+ dst->eap_server = src->eap_server;
+ dst->ssl_ctx = src->ssl_ctx;
+ dst->msg_ctx = src->msg_ctx;
+ dst->eap_sim_db_priv = src->eap_sim_db_priv;
+ os_free(dst->eap_req_id_text);
+ dst->pwd_group = src->pwd_group;
+ if (src->eap_req_id_text) {
+ dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len);
+ if (dst->eap_req_id_text == NULL)
+ return -1;
+ os_memcpy(dst->eap_req_id_text, src->eap_req_id_text,
+ src->eap_req_id_text_len);
+ dst->eap_req_id_text_len = src->eap_req_id_text_len;
+ } else {
+ dst->eap_req_id_text = NULL;
+ dst->eap_req_id_text_len = 0;
+ }
+ if (src->pac_opaque_encr_key) {
+ dst->pac_opaque_encr_key = os_malloc(16);
+ os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key,
+ 16);
+ } else
+ dst->pac_opaque_encr_key = NULL;
+ if (src->eap_fast_a_id) {
+ dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len);
+ if (dst->eap_fast_a_id == NULL) {
+ os_free(dst->eap_req_id_text);
+ return -1;
+ }
+ os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id,
+ src->eap_fast_a_id_len);
+ dst->eap_fast_a_id_len = src->eap_fast_a_id_len;
+ } else
+ dst->eap_fast_a_id = NULL;
+ if (src->eap_fast_a_id_info) {
+ dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info);
+ if (dst->eap_fast_a_id_info == NULL) {
+ os_free(dst->eap_req_id_text);
+ os_free(dst->eap_fast_a_id);
+ return -1;
+ }
+ } else
+ dst->eap_fast_a_id_info = NULL;
+ dst->eap_fast_prov = src->eap_fast_prov;
+ dst->pac_key_lifetime = src->pac_key_lifetime;
+ dst->pac_key_refresh_time = src->pac_key_refresh_time;
+ dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind;
+ dst->tnc = src->tnc;
+ dst->wps = src->wps;
+ 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/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
new file mode 100644
index 0000000..59a10b4
--- /dev/null
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -0,0 +1,92 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - EAPOL state machine
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAPOL_AUTH_SM_H
+#define EAPOL_AUTH_SM_H
+
+#define EAPOL_SM_PREAUTH BIT(0)
+#define EAPOL_SM_WAIT_START BIT(1)
+#define EAPOL_SM_USES_WPA BIT(2)
+#define EAPOL_SM_FROM_PMKSA_CACHE BIT(3)
+
+struct eapol_auth_config {
+ int eap_reauth_period;
+ int wpa;
+ int individual_wep_key_len;
+ int eap_server;
+ void *ssl_ctx;
+ void *msg_ctx;
+ void *eap_sim_db_priv;
+ char *eap_req_id_text; /* a copy of this will be allocated */
+ size_t eap_req_id_text_len;
+ u8 *pac_opaque_encr_key;
+ u8 *eap_fast_a_id;
+ size_t eap_fast_a_id_len;
+ char *eap_fast_a_id_info;
+ int eap_fast_prov;
+ int pac_key_lifetime;
+ int pac_key_refresh_time;
+ int eap_sim_aka_result_ind;
+ int tnc;
+ struct wps_context *wps;
+ int fragment_size;
+ u16 pwd_group;
+
+ /* 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);
+void eapol_auth_free(struct eapol_state_machine *sm);
+void eapol_auth_step(struct eapol_state_machine *sm);
+void eapol_auth_dump_state(FILE *f, const char *prefix,
+ struct eapol_state_machine *sm);
+int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx);
+
+#endif /* EAPOL_AUTH_SM_H */
diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h
new file mode 100644
index 0000000..1000da4
--- /dev/null
+++ b/src/eapol_auth/eapol_auth_sm_i.h
@@ -0,0 +1,183 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - EAPOL state machine (internal definitions)
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAPOL_AUTH_SM_I_H
+#define EAPOL_AUTH_SM_I_H
+
+#include "common/defs.h"
+#include "radius/radius.h"
+
+/* IEEE Std 802.1X-2004, Ch. 8.2 */
+
+typedef enum { ForceUnauthorized = 1, ForceAuthorized = 3, Auto = 2 }
+ PortTypes;
+typedef enum { Unauthorized = 2, Authorized = 1 } PortState;
+typedef enum { Both = 0, In = 1 } ControlledDirection;
+typedef unsigned int Counter;
+
+
+/**
+ * struct eapol_authenticator - Global EAPOL authenticator data
+ */
+struct eapol_authenticator {
+ struct eapol_auth_config conf;
+ struct eapol_auth_cb cb;
+
+ u8 *default_wep_key;
+ u8 default_wep_key_idx;
+};
+
+
+/**
+ * struct eapol_state_machine - Per-Supplicant Authenticator state machines
+ */
+struct eapol_state_machine {
+ /* timers */
+ int aWhile;
+ int quietWhile;
+ int reAuthWhen;
+
+ /* global variables */
+ Boolean authAbort;
+ Boolean authFail;
+ PortState authPortStatus;
+ Boolean authStart;
+ Boolean authTimeout;
+ Boolean authSuccess;
+ Boolean eapolEap;
+ Boolean initialize;
+ Boolean keyDone;
+ Boolean keyRun;
+ Boolean keyTxEnabled;
+ PortTypes portControl;
+ Boolean portValid;
+ Boolean reAuthenticate;
+
+ /* Port Timers state machine */
+ /* 'Boolean tick' implicitly handled as registered timeout */
+
+ /* Authenticator PAE state machine */
+ enum { AUTH_PAE_INITIALIZE, AUTH_PAE_DISCONNECTED, AUTH_PAE_CONNECTING,
+ AUTH_PAE_AUTHENTICATING, AUTH_PAE_AUTHENTICATED,
+ AUTH_PAE_ABORTING, AUTH_PAE_HELD, AUTH_PAE_FORCE_AUTH,
+ AUTH_PAE_FORCE_UNAUTH, AUTH_PAE_RESTART } auth_pae_state;
+ /* variables */
+ Boolean eapolLogoff;
+ Boolean eapolStart;
+ PortTypes portMode;
+ unsigned int reAuthCount;
+ /* constants */
+ unsigned int quietPeriod; /* default 60; 0..65535 */
+#define AUTH_PAE_DEFAULT_quietPeriod 60
+ unsigned int reAuthMax; /* default 2 */
+#define AUTH_PAE_DEFAULT_reAuthMax 2
+ /* counters */
+ Counter authEntersConnecting;
+ Counter authEapLogoffsWhileConnecting;
+ Counter authEntersAuthenticating;
+ Counter authAuthSuccessesWhileAuthenticating;
+ Counter authAuthTimeoutsWhileAuthenticating;
+ Counter authAuthFailWhileAuthenticating;
+ Counter authAuthEapStartsWhileAuthenticating;
+ Counter authAuthEapLogoffWhileAuthenticating;
+ Counter authAuthReauthsWhileAuthenticated;
+ Counter authAuthEapStartsWhileAuthenticated;
+ Counter authAuthEapLogoffWhileAuthenticated;
+
+ /* Backend Authentication state machine */
+ enum { BE_AUTH_REQUEST, BE_AUTH_RESPONSE, BE_AUTH_SUCCESS,
+ BE_AUTH_FAIL, BE_AUTH_TIMEOUT, BE_AUTH_IDLE, BE_AUTH_INITIALIZE,
+ BE_AUTH_IGNORE
+ } be_auth_state;
+ /* constants */
+ unsigned int serverTimeout; /* default 30; 1..X */
+#define BE_AUTH_DEFAULT_serverTimeout 30
+ /* counters */
+ Counter backendResponses;
+ Counter backendAccessChallenges;
+ Counter backendOtherRequestsToSupplicant;
+ Counter backendAuthSuccesses;
+ Counter backendAuthFails;
+
+ /* Reauthentication Timer state machine */
+ enum { REAUTH_TIMER_INITIALIZE, REAUTH_TIMER_REAUTHENTICATE
+ } reauth_timer_state;
+ /* constants */
+ unsigned int reAuthPeriod; /* default 3600 s */
+ Boolean reAuthEnabled;
+
+ /* Authenticator Key Transmit state machine */
+ enum { AUTH_KEY_TX_NO_KEY_TRANSMIT, AUTH_KEY_TX_KEY_TRANSMIT
+ } auth_key_tx_state;
+
+ /* Key Receive state machine */
+ enum { KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } key_rx_state;
+ /* variables */
+ Boolean rxKey;
+
+ /* Controlled Directions state machine */
+ enum { CTRL_DIR_FORCE_BOTH, CTRL_DIR_IN_OR_BOTH } ctrl_dir_state;
+ /* variables */
+ ControlledDirection adminControlledDirections;
+ ControlledDirection operControlledDirections;
+ Boolean operEdge;
+
+ /* Authenticator Statistics Table */
+ Counter dot1xAuthEapolFramesRx;
+ Counter dot1xAuthEapolFramesTx;
+ Counter dot1xAuthEapolStartFramesRx;
+ Counter dot1xAuthEapolLogoffFramesRx;
+ Counter dot1xAuthEapolRespIdFramesRx;
+ Counter dot1xAuthEapolRespFramesRx;
+ Counter dot1xAuthEapolReqIdFramesTx;
+ Counter dot1xAuthEapolReqFramesTx;
+ Counter dot1xAuthInvalidEapolFramesRx;
+ Counter dot1xAuthEapLengthErrorFramesRx;
+ Counter dot1xAuthLastEapolFrameVersion;
+
+ /* Other variables - not defined in IEEE 802.1X */
+ u8 addr[ETH_ALEN]; /* Supplicant address */
+ int flags; /* EAPOL_SM_* */
+
+ /* EAPOL/AAA <-> EAP full authenticator interface */
+ struct eap_eapol_interface *eap_if;
+
+ int radius_identifier;
+ /* TODO: check when the last messages can be released */
+ struct radius_msg *last_recv_radius;
+ u8 last_eap_id; /* last used EAP Identifier */
+ u8 *identity;
+ size_t identity_len;
+ u8 eap_type_authsrv; /* EAP type of the last EAP packet from
+ * Authentication server */
+ u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */
+ struct radius_class_data radius_class;
+
+ /* Keys for encrypting and signing EAPOL-Key frames */
+ u8 *eapol_key_sign;
+ size_t eapol_key_sign_len;
+ u8 *eapol_key_crypt;
+ size_t eapol_key_crypt_len;
+
+ struct eap_sm *eap;
+
+ Boolean initializing; /* in process of initializing state machines */
+ Boolean changed;
+
+ struct eapol_authenticator *eapol;
+
+ void *sta; /* station context pointer to use in callbacks */
+};
+
+#endif /* EAPOL_AUTH_SM_I_H */
diff --git a/src/eapol_supp/Makefile b/src/eapol_supp/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/eapol_supp/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
new file mode 100644
index 0000000..18abb4e
--- /dev/null
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -0,0 +1,1913 @@
+/*
+ * EAPOL supplicant state machines
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "state_machine.h"
+#include "wpabuf.h"
+#include "eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/md5.h"
+#include "common/eapol_common.h"
+#include "eap_peer/eap.h"
+#include "eapol_supp_sm.h"
+
+#define STATE_MACHINE_DATA struct eapol_sm
+#define STATE_MACHINE_DEBUG_PREFIX "EAPOL"
+
+
+/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */
+
+/**
+ * struct eapol_sm - Internal data for EAPOL state machines
+ */
+struct eapol_sm {
+ /* Timers */
+ unsigned int authWhile;
+ unsigned int heldWhile;
+ unsigned int startWhen;
+ unsigned int idleWhile; /* for EAP state machine */
+ int timer_tick_enabled;
+
+ /* Global variables */
+ Boolean eapFail;
+ Boolean eapolEap;
+ Boolean eapSuccess;
+ Boolean initialize;
+ Boolean keyDone;
+ Boolean keyRun;
+ PortControl portControl;
+ Boolean portEnabled;
+ PortStatus suppPortStatus; /* dot1xSuppControlledPortStatus */
+ Boolean portValid;
+ Boolean suppAbort;
+ Boolean suppFail;
+ Boolean suppStart;
+ Boolean suppSuccess;
+ Boolean suppTimeout;
+
+ /* Supplicant PAE state machine */
+ enum {
+ SUPP_PAE_UNKNOWN = 0,
+ SUPP_PAE_DISCONNECTED = 1,
+ SUPP_PAE_LOGOFF = 2,
+ SUPP_PAE_CONNECTING = 3,
+ SUPP_PAE_AUTHENTICATING = 4,
+ SUPP_PAE_AUTHENTICATED = 5,
+ /* unused(6) */
+ SUPP_PAE_HELD = 7,
+ SUPP_PAE_RESTART = 8,
+ SUPP_PAE_S_FORCE_AUTH = 9,
+ SUPP_PAE_S_FORCE_UNAUTH = 10
+ } SUPP_PAE_state; /* dot1xSuppPaeState */
+ /* Variables */
+ Boolean userLogoff;
+ Boolean logoffSent;
+ unsigned int startCount;
+ Boolean eapRestart;
+ PortControl sPortMode;
+ /* Constants */
+ unsigned int heldPeriod; /* dot1xSuppHeldPeriod */
+ unsigned int startPeriod; /* dot1xSuppStartPeriod */
+ unsigned int maxStart; /* dot1xSuppMaxStart */
+
+ /* Key Receive state machine */
+ enum {
+ KEY_RX_UNKNOWN = 0,
+ KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE
+ } KEY_RX_state;
+ /* Variables */
+ Boolean rxKey;
+
+ /* Supplicant Backend state machine */
+ enum {
+ SUPP_BE_UNKNOWN = 0,
+ SUPP_BE_INITIALIZE = 1,
+ SUPP_BE_IDLE = 2,
+ SUPP_BE_REQUEST = 3,
+ SUPP_BE_RECEIVE = 4,
+ SUPP_BE_RESPONSE = 5,
+ SUPP_BE_FAIL = 6,
+ SUPP_BE_TIMEOUT = 7,
+ SUPP_BE_SUCCESS = 8
+ } SUPP_BE_state; /* dot1xSuppBackendPaeState */
+ /* Variables */
+ Boolean eapNoResp;
+ Boolean eapReq;
+ Boolean eapResp;
+ /* Constants */
+ unsigned int authPeriod; /* dot1xSuppAuthPeriod */
+
+ /* Statistics */
+ unsigned int dot1xSuppEapolFramesRx;
+ unsigned int dot1xSuppEapolFramesTx;
+ unsigned int dot1xSuppEapolStartFramesTx;
+ unsigned int dot1xSuppEapolLogoffFramesTx;
+ unsigned int dot1xSuppEapolRespFramesTx;
+ unsigned int dot1xSuppEapolReqIdFramesRx;
+ unsigned int dot1xSuppEapolReqFramesRx;
+ unsigned int dot1xSuppInvalidEapolFramesRx;
+ unsigned int dot1xSuppEapLengthErrorFramesRx;
+ unsigned int dot1xSuppLastEapolFrameVersion;
+ unsigned char dot1xSuppLastEapolFrameSource[6];
+
+ /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */
+ Boolean changed;
+ struct eap_sm *eap;
+ struct eap_peer_config *config;
+ Boolean initial_req;
+ u8 *last_rx_key;
+ size_t last_rx_key_len;
+ struct wpabuf *eapReqData; /* for EAP */
+ Boolean altAccept; /* for EAP */
+ Boolean altReject; /* for EAP */
+ Boolean replay_counter_valid;
+ u8 last_replay_counter[16];
+ struct eapol_config conf;
+ struct eapol_ctx *ctx;
+ enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE }
+ cb_status;
+ Boolean cached_pmk;
+
+ Boolean unicast_key_received, broadcast_key_received;
+};
+
+
+#define IEEE8021X_REPLAY_COUNTER_LEN 8
+#define IEEE8021X_KEY_SIGN_LEN 16
+#define IEEE8021X_KEY_IV_LEN 16
+
+#define IEEE8021X_KEY_INDEX_FLAG 0x80
+#define IEEE8021X_KEY_INDEX_MASK 0x03
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee802_1x_eapol_key {
+ u8 type;
+ /* Note: key_length is unaligned */
+ u8 key_length[2];
+ /* does not repeat within the life of the keying material used to
+ * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
+ u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN];
+ u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */
+ u8 key_index; /* key flag in the most significant bit:
+ * 0 = broadcast (default key),
+ * 1 = unicast (key mapping key); key index is in the
+ * 7 least significant bits */
+ /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
+ * the key */
+ u8 key_signature[IEEE8021X_KEY_SIGN_LEN];
+
+ /* followed by key: if packet body length = 44 + key length, then the
+ * key field (of key_length bytes) contains the key in encrypted form;
+ * if packet body length = 44, key field is absent and key_length
+ * represents the number of least significant octets from
+ * MS-MPPE-Send-Key attribute to be used as the keying material;
+ * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+static void eapol_sm_txLogoff(struct eapol_sm *sm);
+static void eapol_sm_txStart(struct eapol_sm *sm);
+static void eapol_sm_processKey(struct eapol_sm *sm);
+static void eapol_sm_getSuppRsp(struct eapol_sm *sm);
+static void eapol_sm_txSuppRsp(struct eapol_sm *sm);
+static void eapol_sm_abortSupp(struct eapol_sm *sm);
+static void eapol_sm_abort_cached(struct eapol_sm *sm);
+static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx);
+static void eapol_sm_set_port_authorized(struct eapol_sm *sm);
+static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm);
+
+
+/* Port Timers state machine - implemented as a function that will be called
+ * once a second as a registered event loop timeout */
+static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
+{
+ struct eapol_sm *sm = timeout_ctx;
+
+ if (sm->authWhile > 0) {
+ sm->authWhile--;
+ if (sm->authWhile == 0)
+ wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0");
+ }
+ if (sm->heldWhile > 0) {
+ sm->heldWhile--;
+ if (sm->heldWhile == 0)
+ wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0");
+ }
+ if (sm->startWhen > 0) {
+ sm->startWhen--;
+ if (sm->startWhen == 0)
+ wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0");
+ }
+ if (sm->idleWhile > 0) {
+ sm->idleWhile--;
+ if (sm->idleWhile == 0)
+ wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0");
+ }
+
+ if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) {
+ eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx,
+ sm);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick");
+ sm->timer_tick_enabled = 0;
+ }
+ eapol_sm_step(sm);
+}
+
+
+static void eapol_enable_timer_tick(struct eapol_sm *sm)
+{
+ if (sm->timer_tick_enabled)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick");
+ sm->timer_tick_enabled = 1;
+ eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+ eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+}
+
+
+SM_STATE(SUPP_PAE, LOGOFF)
+{
+ SM_ENTRY(SUPP_PAE, LOGOFF);
+ eapol_sm_txLogoff(sm);
+ sm->logoffSent = TRUE;
+ sm->suppPortStatus = Unauthorized;
+ eapol_sm_set_port_unauthorized(sm);
+}
+
+
+SM_STATE(SUPP_PAE, DISCONNECTED)
+{
+ SM_ENTRY(SUPP_PAE, DISCONNECTED);
+ sm->sPortMode = Auto;
+ sm->startCount = 0;
+ sm->logoffSent = FALSE;
+ sm->suppPortStatus = Unauthorized;
+ eapol_sm_set_port_unauthorized(sm);
+ sm->suppAbort = TRUE;
+
+ sm->unicast_key_received = FALSE;
+ sm->broadcast_key_received = FALSE;
+}
+
+
+SM_STATE(SUPP_PAE, CONNECTING)
+{
+ int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING;
+ SM_ENTRY(SUPP_PAE, CONNECTING);
+ if (send_start) {
+ sm->startWhen = sm->startPeriod;
+ sm->startCount++;
+ } else {
+ /*
+ * Do not send EAPOL-Start immediately since in most cases,
+ * Authenticator is going to start authentication immediately
+ * after association and an extra EAPOL-Start is just going to
+ * delay authentication. Use a short timeout to send the first
+ * EAPOL-Start if Authenticator does not start authentication.
+ */
+#ifdef CONFIG_WPS
+ /* Reduce latency on starting WPS negotiation. */
+ sm->startWhen = 1;
+#else /* CONFIG_WPS */
+ sm->startWhen = 3;
+#endif /* CONFIG_WPS */
+ }
+ eapol_enable_timer_tick(sm);
+ sm->eapolEap = FALSE;
+ if (send_start)
+ eapol_sm_txStart(sm);
+}
+
+
+SM_STATE(SUPP_PAE, AUTHENTICATING)
+{
+ SM_ENTRY(SUPP_PAE, AUTHENTICATING);
+ sm->startCount = 0;
+ sm->suppSuccess = FALSE;
+ sm->suppFail = FALSE;
+ sm->suppTimeout = FALSE;
+ sm->keyRun = FALSE;
+ sm->keyDone = FALSE;
+ sm->suppStart = TRUE;
+}
+
+
+SM_STATE(SUPP_PAE, HELD)
+{
+ SM_ENTRY(SUPP_PAE, HELD);
+ sm->heldWhile = sm->heldPeriod;
+ eapol_enable_timer_tick(sm);
+ sm->suppPortStatus = Unauthorized;
+ eapol_sm_set_port_unauthorized(sm);
+ sm->cb_status = EAPOL_CB_FAILURE;
+}
+
+
+SM_STATE(SUPP_PAE, AUTHENTICATED)
+{
+ SM_ENTRY(SUPP_PAE, AUTHENTICATED);
+ sm->suppPortStatus = Authorized;
+ eapol_sm_set_port_authorized(sm);
+ sm->cb_status = EAPOL_CB_SUCCESS;
+}
+
+
+SM_STATE(SUPP_PAE, RESTART)
+{
+ SM_ENTRY(SUPP_PAE, RESTART);
+ sm->eapRestart = TRUE;
+}
+
+
+SM_STATE(SUPP_PAE, S_FORCE_AUTH)
+{
+ SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
+ sm->suppPortStatus = Authorized;
+ eapol_sm_set_port_authorized(sm);
+ sm->sPortMode = ForceAuthorized;
+}
+
+
+SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
+{
+ SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
+ sm->suppPortStatus = Unauthorized;
+ eapol_sm_set_port_unauthorized(sm);
+ sm->sPortMode = ForceUnauthorized;
+ eapol_sm_txLogoff(sm);
+}
+
+
+SM_STEP(SUPP_PAE)
+{
+ if ((sm->userLogoff && !sm->logoffSent) &&
+ !(sm->initialize || !sm->portEnabled))
+ SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF);
+ else if (((sm->portControl == Auto) &&
+ (sm->sPortMode != sm->portControl)) ||
+ sm->initialize || !sm->portEnabled)
+ SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED);
+ else if ((sm->portControl == ForceAuthorized) &&
+ (sm->sPortMode != sm->portControl) &&
+ !(sm->initialize || !sm->portEnabled))
+ SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH);
+ else if ((sm->portControl == ForceUnauthorized) &&
+ (sm->sPortMode != sm->portControl) &&
+ !(sm->initialize || !sm->portEnabled))
+ SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH);
+ else switch (sm->SUPP_PAE_state) {
+ case SUPP_PAE_UNKNOWN:
+ break;
+ case SUPP_PAE_LOGOFF:
+ if (!sm->userLogoff)
+ SM_ENTER(SUPP_PAE, DISCONNECTED);
+ break;
+ case SUPP_PAE_DISCONNECTED:
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ break;
+ case SUPP_PAE_CONNECTING:
+ if (sm->startWhen == 0 && sm->startCount < sm->maxStart)
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ else if (sm->startWhen == 0 &&
+ sm->startCount >= sm->maxStart &&
+ sm->portValid)
+ SM_ENTER(SUPP_PAE, AUTHENTICATED);
+ else if (sm->eapSuccess || sm->eapFail)
+ SM_ENTER(SUPP_PAE, AUTHENTICATING);
+ else if (sm->eapolEap)
+ SM_ENTER(SUPP_PAE, RESTART);
+ else if (sm->startWhen == 0 &&
+ sm->startCount >= sm->maxStart &&
+ !sm->portValid)
+ SM_ENTER(SUPP_PAE, HELD);
+ break;
+ case SUPP_PAE_AUTHENTICATING:
+ if (sm->eapSuccess && !sm->portValid &&
+ sm->conf.accept_802_1x_keys &&
+ sm->conf.required_keys == 0) {
+ wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for "
+ "plaintext connection; no EAPOL-Key frames "
+ "required");
+ sm->portValid = TRUE;
+ if (sm->ctx->eapol_done_cb)
+ sm->ctx->eapol_done_cb(sm->ctx->ctx);
+ }
+ if (sm->eapSuccess && sm->portValid)
+ SM_ENTER(SUPP_PAE, AUTHENTICATED);
+ else if (sm->eapFail || (sm->keyDone && !sm->portValid))
+ SM_ENTER(SUPP_PAE, HELD);
+ else if (sm->suppTimeout)
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ break;
+ case SUPP_PAE_HELD:
+ if (sm->heldWhile == 0)
+ SM_ENTER(SUPP_PAE, CONNECTING);
+ else if (sm->eapolEap)
+ SM_ENTER(SUPP_PAE, RESTART);
+ break;
+ case SUPP_PAE_AUTHENTICATED:
+ if (sm->eapolEap && sm->portValid)
+ SM_ENTER(SUPP_PAE, RESTART);
+ else if (!sm->portValid)
+ SM_ENTER(SUPP_PAE, DISCONNECTED);
+ break;
+ case SUPP_PAE_RESTART:
+ if (!sm->eapRestart)
+ SM_ENTER(SUPP_PAE, AUTHENTICATING);
+ break;
+ case SUPP_PAE_S_FORCE_AUTH:
+ break;
+ case SUPP_PAE_S_FORCE_UNAUTH:
+ break;
+ }
+}
+
+
+SM_STATE(KEY_RX, NO_KEY_RECEIVE)
+{
+ SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);
+}
+
+
+SM_STATE(KEY_RX, KEY_RECEIVE)
+{
+ SM_ENTRY(KEY_RX, KEY_RECEIVE);
+ eapol_sm_processKey(sm);
+ sm->rxKey = FALSE;
+}
+
+
+SM_STEP(KEY_RX)
+{
+ if (sm->initialize || !sm->portEnabled)
+ SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
+ switch (sm->KEY_RX_state) {
+ case KEY_RX_UNKNOWN:
+ break;
+ case KEY_RX_NO_KEY_RECEIVE:
+ if (sm->rxKey)
+ SM_ENTER(KEY_RX, KEY_RECEIVE);
+ break;
+ case KEY_RX_KEY_RECEIVE:
+ if (sm->rxKey)
+ SM_ENTER(KEY_RX, KEY_RECEIVE);
+ break;
+ }
+}
+
+
+SM_STATE(SUPP_BE, REQUEST)
+{
+ SM_ENTRY(SUPP_BE, REQUEST);
+ sm->authWhile = 0;
+ sm->eapReq = TRUE;
+ eapol_sm_getSuppRsp(sm);
+}
+
+
+SM_STATE(SUPP_BE, RESPONSE)
+{
+ SM_ENTRY(SUPP_BE, RESPONSE);
+ eapol_sm_txSuppRsp(sm);
+ sm->eapResp = FALSE;
+}
+
+
+SM_STATE(SUPP_BE, SUCCESS)
+{
+ SM_ENTRY(SUPP_BE, SUCCESS);
+ sm->keyRun = TRUE;
+ sm->suppSuccess = TRUE;
+
+ if (eap_key_available(sm->eap)) {
+ /* New key received - clear IEEE 802.1X EAPOL-Key replay
+ * counter */
+ sm->replay_counter_valid = FALSE;
+ }
+}
+
+
+SM_STATE(SUPP_BE, FAIL)
+{
+ SM_ENTRY(SUPP_BE, FAIL);
+ sm->suppFail = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, TIMEOUT)
+{
+ SM_ENTRY(SUPP_BE, TIMEOUT);
+ sm->suppTimeout = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, IDLE)
+{
+ SM_ENTRY(SUPP_BE, IDLE);
+ sm->suppStart = FALSE;
+ sm->initial_req = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, INITIALIZE)
+{
+ SM_ENTRY(SUPP_BE, INITIALIZE);
+ eapol_sm_abortSupp(sm);
+ sm->suppAbort = FALSE;
+}
+
+
+SM_STATE(SUPP_BE, RECEIVE)
+{
+ SM_ENTRY(SUPP_BE, RECEIVE);
+ sm->authWhile = sm->authPeriod;
+ eapol_enable_timer_tick(sm);
+ sm->eapolEap = FALSE;
+ sm->eapNoResp = FALSE;
+ sm->initial_req = FALSE;
+}
+
+
+SM_STEP(SUPP_BE)
+{
+ if (sm->initialize || sm->suppAbort)
+ SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE);
+ else switch (sm->SUPP_BE_state) {
+ case SUPP_BE_UNKNOWN:
+ break;
+ case SUPP_BE_REQUEST:
+ /*
+ * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL
+ * and SUCCESS based on eapFail and eapSuccess, respectively.
+ * However, IEEE Std 802.1X-2004 is also specifying that
+ * eapNoResp should be set in conjuction with eapSuccess and
+ * eapFail which would mean that more than one of the
+ * transitions here would be activated at the same time.
+ * Skipping RESPONSE and/or RECEIVE states in these cases can
+ * cause problems and the direct transitions to do not seem
+ * correct. Because of this, the conditions for these
+ * transitions are verified only after eapNoResp. They are
+ * unlikely to be used since eapNoResp should always be set if
+ * either of eapSuccess or eapFail is set.
+ */
+ if (sm->eapResp && sm->eapNoResp) {
+ wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both "
+ "eapResp and eapNoResp set?!");
+ }
+ if (sm->eapResp)
+ SM_ENTER(SUPP_BE, RESPONSE);
+ else if (sm->eapNoResp)
+ SM_ENTER(SUPP_BE, RECEIVE);
+ else if (sm->eapFail)
+ SM_ENTER(SUPP_BE, FAIL);
+ else if (sm->eapSuccess)
+ SM_ENTER(SUPP_BE, SUCCESS);
+ break;
+ case SUPP_BE_RESPONSE:
+ SM_ENTER(SUPP_BE, RECEIVE);
+ break;
+ case SUPP_BE_SUCCESS:
+ SM_ENTER(SUPP_BE, IDLE);
+ break;
+ case SUPP_BE_FAIL:
+ SM_ENTER(SUPP_BE, IDLE);
+ break;
+ case SUPP_BE_TIMEOUT:
+ SM_ENTER(SUPP_BE, IDLE);
+ break;
+ case SUPP_BE_IDLE:
+ if (sm->eapFail && sm->suppStart)
+ SM_ENTER(SUPP_BE, FAIL);
+ else if (sm->eapolEap && sm->suppStart)
+ SM_ENTER(SUPP_BE, REQUEST);
+ else if (sm->eapSuccess && sm->suppStart)
+ SM_ENTER(SUPP_BE, SUCCESS);
+ break;
+ case SUPP_BE_INITIALIZE:
+ SM_ENTER(SUPP_BE, IDLE);
+ break;
+ case SUPP_BE_RECEIVE:
+ if (sm->eapolEap)
+ SM_ENTER(SUPP_BE, REQUEST);
+ else if (sm->eapFail)
+ SM_ENTER(SUPP_BE, FAIL);
+ else if (sm->authWhile == 0)
+ SM_ENTER(SUPP_BE, TIMEOUT);
+ else if (sm->eapSuccess)
+ SM_ENTER(SUPP_BE, SUCCESS);
+ break;
+ }
+}
+
+
+static void eapol_sm_txLogoff(struct eapol_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");
+ sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
+ IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0);
+ sm->dot1xSuppEapolLogoffFramesTx++;
+ sm->dot1xSuppEapolFramesTx++;
+}
+
+
+static void eapol_sm_txStart(struct eapol_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: txStart");
+ sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
+ IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0);
+ sm->dot1xSuppEapolStartFramesTx++;
+ sm->dot1xSuppEapolFramesTx++;
+}
+
+
+#define IEEE8021X_ENCR_KEY_LEN 32
+#define IEEE8021X_SIGN_KEY_LEN 32
+
+struct eap_key_data {
+ u8 encr_key[IEEE8021X_ENCR_KEY_LEN];
+ u8 sign_key[IEEE8021X_SIGN_KEY_LEN];
+};
+
+
+static void eapol_sm_processKey(struct eapol_sm *sm)
+{
+ struct ieee802_1x_hdr *hdr;
+ struct ieee802_1x_eapol_key *key;
+ struct eap_key_data keydata;
+ u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
+ u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
+ int key_len, res, sign_key_len, encr_key_len;
+ u16 rx_key_length;
+
+ wpa_printf(MSG_DEBUG, "EAPOL: processKey");
+ if (sm->last_rx_key == NULL)
+ return;
+
+ if (!sm->conf.accept_802_1x_keys) {
+ wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"
+ " even though this was not accepted - "
+ "ignoring this packet");
+ return;
+ }
+
+ hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
+ key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+ if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) {
+ wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
+ return;
+ }
+ rx_key_length = WPA_GET_BE16(key->key_length);
+ wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
+ "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
+ hdr->version, hdr->type, be_to_host16(hdr->length),
+ key->type, rx_key_length, key->key_index);
+
+ eapol_sm_notify_lower_layer_success(sm, 1);
+ sign_key_len = IEEE8021X_SIGN_KEY_LEN;
+ encr_key_len = IEEE8021X_ENCR_KEY_LEN;
+ res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "
+ "decrypting EAPOL-Key keys");
+ return;
+ }
+ if (res == 16) {
+ /* LEAP derives only 16 bytes of keying material. */
+ res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "
+ "master key for decrypting EAPOL-Key keys");
+ return;
+ }
+ sign_key_len = 16;
+ encr_key_len = 16;
+ os_memcpy(keydata.sign_key, keydata.encr_key, 16);
+ } else if (res) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "
+ "data for decrypting EAPOL-Key keys (res=%d)", res);
+ return;
+ }
+
+ /* The key replay_counter must increase when same master key */
+ if (sm->replay_counter_valid &&
+ os_memcmp(sm->last_replay_counter, key->replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {
+ wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "
+ "not increase - ignoring key");
+ wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",
+ sm->last_replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",
+ key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);
+ return;
+ }
+
+ /* Verify key signature (HMAC-MD5) */
+ os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);
+ os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);
+ hmac_md5(keydata.sign_key, sign_key_len,
+ sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
+ key->key_signature);
+ if (os_memcmp(orig_key_sign, key->key_signature,
+ IEEE8021X_KEY_SIGN_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
+ "EAPOL-Key packet");
+ os_memcpy(key->key_signature, orig_key_sign,
+ IEEE8021X_KEY_SIGN_LEN);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
+
+ key_len = be_to_host16(hdr->length) - sizeof(*key);
+ if (key_len > 32 || rx_key_length > 32) {
+ wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
+ key_len ? key_len : rx_key_length);
+ return;
+ }
+ if (key_len == rx_key_length) {
+ os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
+ os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
+ encr_key_len);
+ os_memcpy(datakey, key + 1, key_len);
+ rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0,
+ datakey, key_len);
+ wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
+ datakey, key_len);
+ } else if (key_len == 0) {
+ /*
+ * IEEE 802.1X-2004 specifies that least significant Key Length
+ * octets from MS-MPPE-Send-Key are used as the key if the key
+ * data is not present. This seems to be meaning the beginning
+ * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
+ * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
+ * Anyway, taking the beginning of the keying material from EAP
+ * seems to interoperate with Authenticators.
+ */
+ key_len = rx_key_length;
+ os_memcpy(datakey, keydata.encr_key, key_len);
+ wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "
+ "material data encryption key",
+ datakey, key_len);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "
+ "(key_length=%d)", key_len, rx_key_length);
+ return;
+ }
+
+ sm->replay_counter_valid = TRUE;
+ os_memcpy(sm->last_replay_counter, key->replay_counter,
+ IEEE8021X_REPLAY_COUNTER_LEN);
+
+ wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
+ "len %d",
+ key->key_index & IEEE8021X_KEY_INDEX_FLAG ?
+ "unicast" : "broadcast",
+ key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);
+
+ if (sm->ctx->set_wep_key &&
+ sm->ctx->set_wep_key(sm->ctx->ctx,
+ key->key_index & IEEE8021X_KEY_INDEX_FLAG,
+ key->key_index & IEEE8021X_KEY_INDEX_MASK,
+ datakey, key_len) < 0) {
+ wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "
+ " driver.");
+ } else {
+ if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)
+ sm->unicast_key_received = TRUE;
+ else
+ sm->broadcast_key_received = TRUE;
+
+ if ((sm->unicast_key_received ||
+ !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&
+ (sm->broadcast_key_received ||
+ !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))
+ {
+ wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "
+ "frames received");
+ sm->portValid = TRUE;
+ if (sm->ctx->eapol_done_cb)
+ sm->ctx->eapol_done_cb(sm->ctx->ctx);
+ }
+ }
+}
+
+
+static void eapol_sm_getSuppRsp(struct eapol_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");
+ /* EAP layer processing; no special code is needed, since Supplicant
+ * Backend state machine is waiting for eapNoResp or eapResp to be set
+ * and these are only set in the EAP state machine when the processing
+ * has finished. */
+}
+
+
+static void eapol_sm_txSuppRsp(struct eapol_sm *sm)
+{
+ struct wpabuf *resp;
+
+ wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
+ resp = eap_get_eapRespData(sm->eap);
+ if (resp == NULL) {
+ wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
+ "not available");
+ return;
+ }
+
+ /* Send EAP-Packet from the EAP layer to the Authenticator */
+ sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
+ IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp),
+ wpabuf_len(resp));
+
+ /* eapRespData is not used anymore, so free it here */
+ wpabuf_free(resp);
+
+ if (sm->initial_req)
+ sm->dot1xSuppEapolReqIdFramesRx++;
+ else
+ sm->dot1xSuppEapolReqFramesRx++;
+ sm->dot1xSuppEapolRespFramesTx++;
+ sm->dot1xSuppEapolFramesTx++;
+}
+
+
+static void eapol_sm_abortSupp(struct eapol_sm *sm)
+{
+ /* release system resources that may have been allocated for the
+ * authentication session */
+ os_free(sm->last_rx_key);
+ sm->last_rx_key = NULL;
+ wpabuf_free(sm->eapReqData);
+ sm->eapReqData = NULL;
+ eap_sm_abort(sm->eap);
+}
+
+
+static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ eapol_sm_step(timeout_ctx);
+}
+
+
+static void eapol_sm_set_port_authorized(struct eapol_sm *sm)
+{
+ if (sm->ctx->port_cb)
+ sm->ctx->port_cb(sm->ctx->ctx, 1);
+}
+
+
+static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm)
+{
+ if (sm->ctx->port_cb)
+ sm->ctx->port_cb(sm->ctx->ctx, 0);
+}
+
+
+/**
+ * eapol_sm_step - EAPOL state machine step function
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * This function is called to notify the state machine about changed external
+ * variables. It will step through the EAPOL state machines in loop to process
+ * all triggered state changes.
+ */
+void eapol_sm_step(struct eapol_sm *sm)
+{
+ int i;
+
+ /* In theory, it should be ok to run this in loop until !changed.
+ * However, it is better to use a limit on number of iterations to
+ * allow events (e.g., SIGTERM) to stop the program cleanly if the
+ * state machine were to generate a busy loop. */
+ for (i = 0; i < 100; i++) {
+ sm->changed = FALSE;
+ SM_STEP_RUN(SUPP_PAE);
+ SM_STEP_RUN(KEY_RX);
+ SM_STEP_RUN(SUPP_BE);
+ if (eap_peer_sm_step(sm->eap))
+ sm->changed = TRUE;
+ if (!sm->changed)
+ break;
+ }
+
+ if (sm->changed) {
+ /* restart EAPOL state machine step from timeout call in order
+ * to allow other events to be processed. */
+ eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
+ eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
+ }
+
+ if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
+ int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
+ sm->cb_status = EAPOL_CB_IN_PROGRESS;
+ sm->ctx->cb(sm, success, sm->ctx->cb_ctx);
+ }
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static const char *eapol_supp_pae_state(int state)
+{
+ switch (state) {
+ case SUPP_PAE_LOGOFF:
+ return "LOGOFF";
+ case SUPP_PAE_DISCONNECTED:
+ return "DISCONNECTED";
+ case SUPP_PAE_CONNECTING:
+ return "CONNECTING";
+ case SUPP_PAE_AUTHENTICATING:
+ return "AUTHENTICATING";
+ case SUPP_PAE_HELD:
+ return "HELD";
+ case SUPP_PAE_AUTHENTICATED:
+ return "AUTHENTICATED";
+ case SUPP_PAE_RESTART:
+ return "RESTART";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static const char *eapol_supp_be_state(int state)
+{
+ switch (state) {
+ case SUPP_BE_REQUEST:
+ return "REQUEST";
+ case SUPP_BE_RESPONSE:
+ return "RESPONSE";
+ case SUPP_BE_SUCCESS:
+ return "SUCCESS";
+ case SUPP_BE_FAIL:
+ return "FAIL";
+ case SUPP_BE_TIMEOUT:
+ return "TIMEOUT";
+ case SUPP_BE_IDLE:
+ return "IDLE";
+ case SUPP_BE_INITIALIZE:
+ return "INITIALIZE";
+ case SUPP_BE_RECEIVE:
+ return "RECEIVE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static const char * eapol_port_status(PortStatus status)
+{
+ if (status == Authorized)
+ return "Authorized";
+ else
+ return "Unauthorized";
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eapol_port_control(PortControl ctrl)
+{
+ switch (ctrl) {
+ case Auto:
+ return "Auto";
+ case ForceUnauthorized:
+ return "ForceUnauthorized";
+ case ForceAuthorized:
+ return "ForceAuthorized";
+ default:
+ return "Unknown";
+ }
+}
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+/**
+ * eapol_sm_configure - Set EAPOL variables
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @heldPeriod: dot1xSuppHeldPeriod
+ * @authPeriod: dot1xSuppAuthPeriod
+ * @startPeriod: dot1xSuppStartPeriod
+ * @maxStart: dot1xSuppMaxStart
+ *
+ * Set configurable EAPOL state machine variables. Each variable can be set to
+ * the given value or ignored if set to -1 (to set only some of the variables).
+ */
+void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
+ int startPeriod, int maxStart)
+{
+ if (sm == NULL)
+ return;
+ if (heldPeriod >= 0)
+ sm->heldPeriod = heldPeriod;
+ if (authPeriod >= 0)
+ sm->authPeriod = authPeriod;
+ if (startPeriod >= 0)
+ sm->startPeriod = startPeriod;
+ if (maxStart >= 0)
+ sm->maxStart = maxStart;
+}
+
+
+/**
+ * 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;
+ }
+
+ len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
+
+ return len;
+}
+
+
+/**
+ * eapol_sm_get_mib - Get EAPOL state machine MIBs
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @buf: Buffer for MIB information
+ * @buflen: Maximum buffer length
+ * Returns: Number of bytes written to buf.
+ *
+ * Query EAPOL state machine for MIB information. This function fills in a
+ * text area with current MIB information from the EAPOL state machine. If
+ * the buffer (buf) is not large enough, MIB information will be truncated to
+ * fit the buffer.
+ */
+int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
+{
+ size_t len;
+ int ret;
+
+ if (sm == NULL)
+ return 0;
+ ret = os_snprintf(buf, buflen,
+ "dot1xSuppPaeState=%d\n"
+ "dot1xSuppHeldPeriod=%u\n"
+ "dot1xSuppAuthPeriod=%u\n"
+ "dot1xSuppStartPeriod=%u\n"
+ "dot1xSuppMaxStart=%u\n"
+ "dot1xSuppSuppControlledPortStatus=%s\n"
+ "dot1xSuppBackendPaeState=%d\n",
+ sm->SUPP_PAE_state,
+ sm->heldPeriod,
+ sm->authPeriod,
+ sm->startPeriod,
+ sm->maxStart,
+ sm->suppPortStatus == Authorized ?
+ "Authorized" : "Unauthorized",
+ sm->SUPP_BE_state);
+
+ if (ret < 0 || (size_t) ret >= buflen)
+ return 0;
+ len = ret;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "dot1xSuppEapolFramesRx=%u\n"
+ "dot1xSuppEapolFramesTx=%u\n"
+ "dot1xSuppEapolStartFramesTx=%u\n"
+ "dot1xSuppEapolLogoffFramesTx=%u\n"
+ "dot1xSuppEapolRespFramesTx=%u\n"
+ "dot1xSuppEapolReqIdFramesRx=%u\n"
+ "dot1xSuppEapolReqFramesRx=%u\n"
+ "dot1xSuppInvalidEapolFramesRx=%u\n"
+ "dot1xSuppEapLengthErrorFramesRx=%u\n"
+ "dot1xSuppLastEapolFrameVersion=%u\n"
+ "dot1xSuppLastEapolFrameSource=" MACSTR "\n",
+ sm->dot1xSuppEapolFramesRx,
+ sm->dot1xSuppEapolFramesTx,
+ sm->dot1xSuppEapolStartFramesTx,
+ sm->dot1xSuppEapolLogoffFramesTx,
+ sm->dot1xSuppEapolRespFramesTx,
+ sm->dot1xSuppEapolReqIdFramesRx,
+ sm->dot1xSuppEapolReqFramesRx,
+ sm->dot1xSuppInvalidEapolFramesRx,
+ sm->dot1xSuppEapLengthErrorFramesRx,
+ sm->dot1xSuppLastEapolFrameVersion,
+ MAC2STR(sm->dot1xSuppLastEapolFrameSource));
+
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ return len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+/**
+ * eapol_sm_rx_eapol - Process received EAPOL frames
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @src: Source MAC address of the EAPOL packet
+ * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
+ * @len: Length of the EAPOL frame
+ * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine,
+ * -1 failure
+ */
+int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
+ size_t len)
+{
+ const struct ieee802_1x_hdr *hdr;
+ const struct ieee802_1x_eapol_key *key;
+ int data_len;
+ int res = 1;
+ size_t plen;
+
+ if (sm == NULL)
+ return 0;
+ sm->dot1xSuppEapolFramesRx++;
+ if (len < sizeof(*hdr)) {
+ sm->dot1xSuppInvalidEapolFramesRx++;
+ return 0;
+ }
+ hdr = (const struct ieee802_1x_hdr *) buf;
+ sm->dot1xSuppLastEapolFrameVersion = hdr->version;
+ os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN);
+ if (hdr->version < EAPOL_VERSION) {
+ /* TODO: backwards compatibility */
+ }
+ plen = be_to_host16(hdr->length);
+ if (plen > len - sizeof(*hdr)) {
+ sm->dot1xSuppEapLengthErrorFramesRx++;
+ return 0;
+ }
+#ifdef CONFIG_WPS
+ if (sm->conf.workaround &&
+ plen < len - sizeof(*hdr) &&
+ hdr->type == IEEE802_1X_TYPE_EAP_PACKET &&
+ len - sizeof(*hdr) > sizeof(struct eap_hdr)) {
+ const struct eap_hdr *ehdr =
+ (const struct eap_hdr *) (hdr + 1);
+ u16 elen;
+
+ elen = be_to_host16(ehdr->length);
+ if (elen > plen && elen <= len - sizeof(*hdr)) {
+ /*
+ * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS
+ * packets with too short EAPOL header length field
+ * (14 octets). This is fixed in firmware Ver.1.49.
+ * As a workaround, fix the EAPOL header based on the
+ * correct length in the EAP packet.
+ */
+ wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL "
+ "payload length based on EAP header: "
+ "%d -> %d", (int) plen, elen);
+ plen = elen;
+ }
+ }
+#endif /* CONFIG_WPS */
+ data_len = plen + sizeof(*hdr);
+
+ switch (hdr->type) {
+ case IEEE802_1X_TYPE_EAP_PACKET:
+ if (sm->cached_pmk) {
+ /* Trying to use PMKSA caching, but Authenticator did
+ * not seem to have a matching entry. Need to restart
+ * EAPOL state machines.
+ */
+ eapol_sm_abort_cached(sm);
+ }
+ wpabuf_free(sm->eapReqData);
+ sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen);
+ if (sm->eapReqData) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
+ "frame");
+ sm->eapolEap = TRUE;
+ eapol_sm_step(sm);
+ }
+ break;
+ case IEEE802_1X_TYPE_EAPOL_KEY:
+ if (plen < sizeof(*key)) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key "
+ "frame received");
+ break;
+ }
+ key = (const struct ieee802_1x_eapol_key *) (hdr + 1);
+ if (key->type == EAPOL_KEY_TYPE_WPA ||
+ key->type == EAPOL_KEY_TYPE_RSN) {
+ /* WPA Supplicant takes care of this frame. */
+ wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key "
+ "frame in EAPOL state machines");
+ res = 0;
+ break;
+ }
+ if (key->type != EAPOL_KEY_TYPE_RC4) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown "
+ "EAPOL-Key type %d", key->type);
+ break;
+ }
+ os_free(sm->last_rx_key);
+ sm->last_rx_key = os_malloc(data_len);
+ if (sm->last_rx_key) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key "
+ "frame");
+ os_memcpy(sm->last_rx_key, buf, data_len);
+ sm->last_rx_key_len = data_len;
+ sm->rxKey = TRUE;
+ eapol_sm_step(sm);
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
+ hdr->type);
+ sm->dot1xSuppInvalidEapolFramesRx++;
+ break;
+ }
+
+ return res;
+}
+
+
+/**
+ * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machine about transmitted EAPOL packet from an external
+ * component, e.g., WPA. This will update the statistics.
+ */
+void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
+{
+ if (sm)
+ sm->dot1xSuppEapolFramesTx++;
+}
+
+
+/**
+ * eapol_sm_notify_portEnabled - Notification about portEnabled change
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @enabled: New portEnabled value
+ *
+ * Notify EAPOL state machine about new portEnabled value.
+ */
+void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "portEnabled=%d", enabled);
+ sm->portEnabled = enabled;
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_portValid - Notification about portValid change
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @valid: New portValid value
+ *
+ * Notify EAPOL state machine about new portValid value.
+ */
+void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "portValid=%d", valid);
+ sm->portValid = valid;
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_eap_success - Notification of external EAP success trigger
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @success: %TRUE = set success, %FALSE = clear success
+ *
+ * Notify the EAPOL state machine that external event has forced EAP state to
+ * success (success = %TRUE). This can be cleared by setting success = %FALSE.
+ *
+ * This function is called to update EAP state when WPA-PSK key handshake has
+ * been completed successfully since WPA-PSK does not use EAP state machine.
+ */
+void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "EAP success=%d", success);
+ sm->eapSuccess = success;
+ sm->altAccept = success;
+ if (success)
+ eap_notify_success(sm->eap);
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @fail: %TRUE = set failure, %FALSE = clear failure
+ *
+ * Notify EAPOL state machine that external event has forced EAP state to
+ * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE.
+ */
+void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "EAP fail=%d", fail);
+ sm->eapFail = fail;
+ sm->altReject = fail;
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_config - Notification of EAPOL configuration change
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @config: Pointer to current network EAP configuration
+ * @conf: Pointer to EAPOL configuration data
+ *
+ * Notify EAPOL state machine that configuration has changed. config will be
+ * stored as a backpointer to network configuration. This can be %NULL to clear
+ * the stored pointed. conf will be copied to local EAPOL/EAP configuration
+ * data. If conf is %NULL, this part of the configuration change will be
+ * skipped.
+ */
+void eapol_sm_notify_config(struct eapol_sm *sm,
+ struct eap_peer_config *config,
+ const struct eapol_config *conf)
+{
+ if (sm == NULL)
+ return;
+
+ sm->config = config;
+
+ if (conf == NULL)
+ return;
+
+ sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
+ sm->conf.required_keys = conf->required_keys;
+ sm->conf.fast_reauth = conf->fast_reauth;
+ sm->conf.workaround = conf->workaround;
+ if (sm->eap) {
+ eap_set_fast_reauth(sm->eap, conf->fast_reauth);
+ eap_set_workaround(sm->eap, conf->workaround);
+ eap_set_force_disabled(sm->eap, conf->eap_disabled);
+ }
+}
+
+
+/**
+ * eapol_sm_get_key - Get master session key (MSK) from EAP
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @key: Pointer for key buffer
+ * @len: Number of bytes to copy to key
+ * Returns: 0 on success (len of key available), maximum available key len
+ * (>0) if key is available but it is shorter than len, or -1 on failure.
+ *
+ * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key
+ * is available only after a successful authentication.
+ */
+int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
+{
+ const u8 *eap_key;
+ size_t eap_len;
+
+ if (sm == NULL || !eap_key_available(sm->eap)) {
+ wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
+ return -1;
+ }
+ eap_key = eap_get_eapKeyData(sm->eap, &eap_len);
+ if (eap_key == NULL) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData");
+ return -1;
+ }
+ if (len > eap_len) {
+ wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not "
+ "available (len=%lu)",
+ (unsigned long) len, (unsigned long) eap_len);
+ return eap_len;
+ }
+ os_memcpy(key, eap_key, len);
+ wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)",
+ (unsigned long) len);
+ return 0;
+}
+
+
+/**
+ * eapol_sm_notify_logoff - Notification of logon/logoff commands
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @logoff: Whether command was logoff
+ *
+ * Notify EAPOL state machines that user requested logon/logoff.
+ */
+void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
+{
+ if (sm) {
+ sm->userLogoff = logoff;
+ eapol_sm_step(sm);
+ }
+}
+
+
+/**
+ * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines that PMKSA caching was successful. This is used
+ * to move EAPOL and EAP state machines into authenticated/successful state.
+ */
+void eapol_sm_notify_cached(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL");
+ sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED;
+ sm->suppPortStatus = Authorized;
+ eapol_sm_set_port_authorized(sm);
+ sm->portValid = TRUE;
+ eap_notify_success(sm->eap);
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @attempt: Whether PMKSA caching is tried
+ *
+ * Notify EAPOL state machines whether PMKSA caching is used.
+ */
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt)
+{
+ if (sm == NULL)
+ return;
+ if (attempt) {
+ wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
+ sm->cached_pmk = TRUE;
+ } else {
+ wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA");
+ sm->cached_pmk = FALSE;
+ }
+}
+
+
+static void eapol_sm_abort_cached(struct eapol_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, "
+ "doing full EAP authentication");
+ if (sm == NULL)
+ return;
+ sm->cached_pmk = FALSE;
+ sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
+ sm->suppPortStatus = Unauthorized;
+ eapol_sm_set_port_unauthorized(sm);
+
+ /* Make sure we do not start sending EAPOL-Start frames first, but
+ * instead move to RESTART state to start EAPOL authentication. */
+ sm->startWhen = 3;
+ eapol_enable_timer_tick(sm);
+
+ if (sm->ctx->aborted_cached)
+ sm->ctx->aborted_cached(sm->ctx->ctx);
+}
+
+
+/**
+ * eapol_sm_register_scard_ctx - Notification of smart card context
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @ctx: Context data for smart card operations
+ *
+ * Notify EAPOL state machines of context data for smart card operations. This
+ * context data will be used as a parameter for scard_*() functions.
+ */
+void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx)
+{
+ if (sm) {
+ sm->ctx->scard_ctx = ctx;
+ eap_register_scard_ctx(sm->eap, ctx);
+ }
+}
+
+
+/**
+ * eapol_sm_notify_portControl - Notification of portControl changes
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @portControl: New value for portControl variable
+ *
+ * Notify EAPOL state machines that portControl variable has changed.
+ */
+void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl)
+{
+ if (sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+ "portControl=%s", eapol_port_control(portControl));
+ sm->portControl = portControl;
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_ctrl_attached - Notification of attached monitor
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines that a monitor was attached to the control
+ * interface to trigger re-sending of pending requests for user input.
+ */
+void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ eap_sm_notify_ctrl_attached(sm->eap);
+}
+
+
+/**
+ * eapol_sm_notify_ctrl_response - Notification of received user input
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines that a control response, i.e., user
+ * input, was received in order to trigger retrying of a pending EAP request.
+ */
+void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ if (sm->eapReqData && !sm->eapReq) {
+ wpa_printf(MSG_DEBUG, "EAPOL: received control response (user "
+ "input) notification - retrying pending EAP "
+ "Request");
+ sm->eapolEap = TRUE;
+ sm->eapReq = TRUE;
+ eapol_sm_step(sm);
+ }
+}
+
+
+/**
+ * eapol_sm_request_reauth - Request reauthentication
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * This function can be used to request EAPOL reauthentication, e.g., when the
+ * current PMKSA entry is nearing expiration.
+ */
+void eapol_sm_request_reauth(struct eapol_sm *sm)
+{
+ if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED)
+ return;
+ eapol_sm_txStart(sm);
+}
+
+
+/**
+ * eapol_sm_notify_lower_layer_success - Notification of lower layer success
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @in_eapol_sm: Whether the caller is already running inside EAPOL state
+ * machine loop (eapol_sm_step())
+ *
+ * Notify EAPOL (and EAP) state machines that a lower layer has detected a
+ * successful authentication. This is used to recover from dropped EAP-Success
+ * messages.
+ */
+void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm)
+{
+ if (sm == NULL)
+ return;
+ eap_notify_lower_layer_success(sm->eap);
+ if (!in_eapol_sm)
+ eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ */
+void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
+{
+ if (sm)
+ eap_invalidate_cached_session(sm->eap);
+}
+
+
+static struct eap_peer_config * eapol_sm_get_config(void *ctx)
+{
+ struct eapol_sm *sm = ctx;
+ return sm ? sm->config : NULL;
+}
+
+
+static struct wpabuf * eapol_sm_get_eapReqData(void *ctx)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL || sm->eapReqData == NULL)
+ return NULL;
+
+ return sm->eapReqData;
+}
+
+
+static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return FALSE;
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ return sm->eapSuccess;
+ case EAPOL_eapRestart:
+ return sm->eapRestart;
+ case EAPOL_eapFail:
+ return sm->eapFail;
+ case EAPOL_eapResp:
+ return sm->eapResp;
+ case EAPOL_eapNoResp:
+ return sm->eapNoResp;
+ case EAPOL_eapReq:
+ return sm->eapReq;
+ case EAPOL_portEnabled:
+ return sm->portEnabled;
+ case EAPOL_altAccept:
+ return sm->altAccept;
+ case EAPOL_altReject:
+ return sm->altReject;
+ }
+ return FALSE;
+}
+
+
+static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
+ Boolean value)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return;
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ sm->eapSuccess = value;
+ break;
+ case EAPOL_eapRestart:
+ sm->eapRestart = value;
+ break;
+ case EAPOL_eapFail:
+ sm->eapFail = value;
+ break;
+ case EAPOL_eapResp:
+ sm->eapResp = value;
+ break;
+ case EAPOL_eapNoResp:
+ sm->eapNoResp = value;
+ break;
+ case EAPOL_eapReq:
+ sm->eapReq = value;
+ break;
+ case EAPOL_portEnabled:
+ sm->portEnabled = value;
+ break;
+ case EAPOL_altAccept:
+ sm->altAccept = value;
+ break;
+ case EAPOL_altReject:
+ sm->altReject = value;
+ break;
+ }
+}
+
+
+static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return 0;
+ switch (variable) {
+ case EAPOL_idleWhile:
+ return sm->idleWhile;
+ }
+ return 0;
+}
+
+
+static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
+ unsigned int value)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return;
+ switch (variable) {
+ case EAPOL_idleWhile:
+ sm->idleWhile = value;
+ eapol_enable_timer_tick(sm);
+ break;
+ }
+}
+
+
+static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ struct eapol_sm *sm = ctx;
+ if (sm && sm->ctx && sm->ctx->set_config_blob)
+ sm->ctx->set_config_blob(sm->ctx->ctx, blob);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+static const struct wpa_config_blob *
+eapol_sm_get_config_blob(void *ctx, const char *name)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+ struct eapol_sm *sm = ctx;
+ if (sm && sm->ctx && sm->ctx->get_config_blob)
+ return sm->ctx->get_config_blob(sm->ctx->ctx, name);
+ else
+ return NULL;
+#else /* CONFIG_NO_CONFIG_BLOBS */
+ return NULL;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+static void eapol_sm_notify_pending(void *ctx)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm == NULL)
+ return;
+ if (sm->eapReqData && !sm->eapReq) {
+ wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP "
+ "state machine - retrying pending EAP Request");
+ sm->eapolEap = TRUE;
+ sm->eapReq = TRUE;
+ eapol_sm_step(sm);
+ }
+}
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void eapol_sm_eap_param_needed(void *ctx, const char *field,
+ const char *txt)
+{
+ struct eapol_sm *sm = ctx;
+ wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed");
+ if (sm->ctx->eap_param_needed)
+ sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eapol_sm_eap_param_needed NULL
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+static struct eapol_callbacks eapol_cb =
+{
+ eapol_sm_get_config,
+ eapol_sm_get_bool,
+ eapol_sm_set_bool,
+ eapol_sm_get_int,
+ eapol_sm_set_int,
+ eapol_sm_get_eapReqData,
+ eapol_sm_set_config_blob,
+ eapol_sm_get_config_blob,
+ eapol_sm_notify_pending,
+ eapol_sm_eap_param_needed
+};
+
+
+/**
+ * eapol_sm_init - Initialize EAPOL state machine
+ * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer
+ * and EAPOL state machine will free it in eapol_sm_deinit()
+ * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure
+ *
+ * Allocate and initialize an EAPOL state machine.
+ */
+struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
+{
+ struct eapol_sm *sm;
+ struct eap_config conf;
+ sm = os_zalloc(sizeof(*sm));
+ if (sm == NULL)
+ return NULL;
+ sm->ctx = ctx;
+
+ sm->portControl = Auto;
+
+ /* Supplicant PAE state machine */
+ sm->heldPeriod = 60;
+ sm->startPeriod = 30;
+ sm->maxStart = 3;
+
+ /* Supplicant Backend state machine */
+ sm->authPeriod = 30;
+
+ os_memset(&conf, 0, sizeof(conf));
+ conf.opensc_engine_path = ctx->opensc_engine_path;
+ conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
+ conf.pkcs11_module_path = ctx->pkcs11_module_path;
+ conf.wps = ctx->wps;
+
+ sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
+ if (sm->eap == NULL) {
+ os_free(sm);
+ return NULL;
+ }
+
+ /* Initialize EAPOL state machines */
+ sm->initialize = TRUE;
+ eapol_sm_step(sm);
+ sm->initialize = FALSE;
+ eapol_sm_step(sm);
+
+ sm->timer_tick_enabled = 1;
+ eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+
+ return sm;
+}
+
+
+/**
+ * eapol_sm_deinit - Deinitialize EAPOL state machine
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Deinitialize and free EAPOL state machine.
+ */
+void eapol_sm_deinit(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
+ eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+ eap_peer_sm_deinit(sm->eap);
+ os_free(sm->last_rx_key);
+ wpabuf_free(sm->eapReqData);
+ os_free(sm->ctx);
+ os_free(sm);
+}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
new file mode 100644
index 0000000..1bdf8cd
--- /dev/null
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -0,0 +1,352 @@
+/*
+ * EAPOL supplicant state machines
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAPOL_SUPP_SM_H
+#define EAPOL_SUPP_SM_H
+
+#include "common/defs.h"
+
+typedef enum { Unauthorized, Authorized } PortStatus;
+typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl;
+
+/**
+ * struct eapol_config - Per network configuration for EAPOL state machines
+ */
+struct eapol_config {
+ /**
+ * accept_802_1x_keys - Accept IEEE 802.1X (non-WPA) EAPOL-Key frames
+ *
+ * This variable should be set to 1 when using EAPOL state machines
+ * with non-WPA security policy to generate dynamic WEP keys. When
+ * using WPA, this should be set to 0 so that WPA state machine can
+ * process the EAPOL-Key frames.
+ */
+ int accept_802_1x_keys;
+
+#define EAPOL_REQUIRE_KEY_UNICAST BIT(0)
+#define EAPOL_REQUIRE_KEY_BROADCAST BIT(1)
+ /**
+ * required_keys - Which EAPOL-Key packets are required
+ *
+ * This variable determines which EAPOL-Key packets are required before
+ * marking connection authenticated. This is a bit field of
+ * EAPOL_REQUIRE_KEY_UNICAST and EAPOL_REQUIRE_KEY_BROADCAST flags.
+ */
+ int required_keys;
+
+ /**
+ * fast_reauth - Whether fast EAP reauthentication is enabled
+ */
+ int fast_reauth;
+
+ /**
+ * workaround - Whether EAP workarounds are enabled
+ */
+ unsigned int workaround;
+
+ /**
+ * eap_disabled - Whether EAP is disabled
+ */
+ int eap_disabled;
+};
+
+struct eapol_sm;
+struct wpa_config_blob;
+
+/**
+ * struct eapol_ctx - Global (for all networks) EAPOL state machine context
+ */
+struct eapol_ctx {
+ /**
+ * ctx - Pointer to arbitrary upper level context
+ */
+ void *ctx;
+
+ /**
+ * preauth - IEEE 802.11i/RSN pre-authentication
+ *
+ * This EAPOL state machine is used for IEEE 802.11i/RSN
+ * pre-authentication
+ */
+ int preauth;
+
+ /**
+ * cb - Function to be called when EAPOL negotiation has been completed
+ * @eapol: Pointer to EAPOL state machine data
+ * @success: Whether the authentication was completed successfully
+ * @ctx: Pointer to context data (cb_ctx)
+ *
+ * This optional callback function will be called when the EAPOL
+ * authentication has been completed. This allows the owner of the
+ * EAPOL state machine to process the key and terminate the EAPOL state
+ * machine. Currently, this is used only in RSN pre-authentication.
+ */
+ void (*cb)(struct eapol_sm *eapol, int success, void *ctx);
+
+ /**
+ * cb_ctx - Callback context for cb()
+ */
+ void *cb_ctx;
+
+ /**
+ * msg_ctx - Callback context for wpa_msg() calls
+ */
+ void *msg_ctx;
+
+ /**
+ * scard_ctx - Callback context for PC/SC scard_*() function calls
+ *
+ * This context can be updated with eapol_sm_register_scard_ctx().
+ */
+ void *scard_ctx;
+
+ /**
+ * eapol_send_ctx - Callback context for eapol_send() calls
+ */
+ void *eapol_send_ctx;
+
+ /**
+ * eapol_done_cb - Function to be called at successful completion
+ * @ctx: Callback context (ctx)
+ *
+ * This function is called at the successful completion of EAPOL
+ * authentication. If dynamic WEP keys are used, this is called only
+ * after all the expected keys have been received.
+ */
+ void (*eapol_done_cb)(void *ctx);
+
+ /**
+ * eapol_send - Send EAPOL packets
+ * @ctx: Callback context (eapol_send_ctx)
+ * @type: EAPOL type (IEEE802_1X_TYPE_*)
+ * @buf: Pointer to EAPOL payload
+ * @len: Length of the EAPOL payload
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*eapol_send)(void *ctx, int type, const u8 *buf, size_t len);
+
+ /**
+ * set_wep_key - Configure WEP keys
+ * @ctx: Callback context (ctx)
+ * @unicast: Non-zero = unicast, 0 = multicast/broadcast key
+ * @keyidx: Key index (0..3)
+ * @key: WEP key
+ * @keylen: Length of the WEP key
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_wep_key)(void *ctx, int unicast, int keyidx,
+ const u8 *key, size_t keylen);
+
+ /**
+ * set_config_blob - Set or add a named configuration blob
+ * @ctx: Callback context (ctx)
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an
+ * existing blob.
+ */
+ void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+
+ /**
+ * get_config_blob - Get a named configuration blob
+ * @ctx: Callback context (ctx)
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+ const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+ const char *name);
+
+ /**
+ * aborted_cached - Notify that cached PMK attempt was aborted
+ * @ctx: Callback context (ctx)
+ */
+ void (*aborted_cached)(void *ctx);
+
+ /**
+ * opensc_engine_path - Path to the OpenSSL engine for opensc
+ *
+ * This is an OpenSSL specific configuration option for loading OpenSC
+ * engine (engine_opensc.so); if %NULL, this engine is not loaded.
+ */
+ const char *opensc_engine_path;
+
+ /**
+ * pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11
+ *
+ * This is an OpenSSL specific configuration option for loading PKCS#11
+ * engine (engine_pkcs11.so); if %NULL, this engine is not loaded.
+ */
+ const char *pkcs11_engine_path;
+
+ /**
+ * pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module
+ *
+ * This is an OpenSSL specific configuration option for configuring
+ * path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this
+ * module is not loaded.
+ */
+ const char *pkcs11_module_path;
+
+ /**
+ * wps - WPS context data
+ *
+ * This is only used by EAP-WSC and can be left %NULL if not available.
+ */
+ struct wps_context *wps;
+
+ /**
+ * eap_param_needed - Notify that EAP parameter is needed
+ * @ctx: Callback context (ctx)
+ * @field: Field name (e.g., "IDENTITY")
+ * @txt: User readable text describing the required parameter
+ */
+ void (*eap_param_needed)(void *ctx, const char *field,
+ const char *txt);
+
+ /**
+ * port_cb - Set port authorized/unauthorized callback (optional)
+ * @ctx: Callback context (ctx)
+ * @authorized: Whether the supplicant port is now in authorized state
+ */
+ void (*port_cb)(void *ctx, int authorized);
+};
+
+
+struct eap_peer_config;
+
+#ifdef IEEE8021X_EAPOL
+struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx);
+void eapol_sm_deinit(struct eapol_sm *sm);
+void eapol_sm_step(struct eapol_sm *sm);
+int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
+ int verbose);
+int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen);
+void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
+ int startPeriod, int maxStart);
+int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
+ size_t len);
+void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm);
+void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled);
+void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid);
+void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success);
+void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail);
+void eapol_sm_notify_config(struct eapol_sm *sm,
+ struct eap_peer_config *config,
+ const struct eapol_config *conf);
+int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len);
+void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff);
+void eapol_sm_notify_cached(struct eapol_sm *sm);
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt);
+void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx);
+void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl);
+void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm);
+void eapol_sm_notify_ctrl_response(struct eapol_sm *sm);
+void eapol_sm_request_reauth(struct eapol_sm *sm);
+void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm);
+void eapol_sm_invalidate_cached_session(struct eapol_sm *sm);
+const char * eapol_sm_get_method_name(struct eapol_sm *sm);
+#else /* IEEE8021X_EAPOL */
+static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
+{
+ free(ctx);
+ return (struct eapol_sm *) 1;
+}
+static inline void eapol_sm_deinit(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_step(struct eapol_sm *sm)
+{
+}
+static inline int eapol_sm_get_status(struct eapol_sm *sm, char *buf,
+ size_t buflen, int verbose)
+{
+ return 0;
+}
+static inline int eapol_sm_get_mib(struct eapol_sm *sm, char *buf,
+ size_t buflen)
+{
+ return 0;
+}
+static inline void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod,
+ int authPeriod, int startPeriod,
+ int maxStart)
+{
+}
+static inline int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src,
+ const u8 *buf, size_t len)
+{
+ return 0;
+}
+static inline void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_portEnabled(struct eapol_sm *sm,
+ Boolean enabled)
+{
+}
+static inline void eapol_sm_notify_portValid(struct eapol_sm *sm,
+ Boolean valid)
+{
+}
+static inline void eapol_sm_notify_eap_success(struct eapol_sm *sm,
+ Boolean success)
+{
+}
+static inline void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
+{
+}
+static inline void eapol_sm_notify_config(struct eapol_sm *sm,
+ struct eap_peer_config *config,
+ struct eapol_config *conf)
+{
+}
+static inline int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
+{
+ return -1;
+}
+static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
+{
+}
+static inline void eapol_sm_notify_cached(struct eapol_sm *sm)
+{
+}
+#define eapol_sm_notify_pmkid_attempt(sm, attempt) do { } while (0)
+#define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0)
+static inline void eapol_sm_notify_portControl(struct eapol_sm *sm,
+ PortControl portControl)
+{
+}
+static inline void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_request_reauth(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm,
+ int in_eapol_sm)
+{
+}
+static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
+{
+}
+static inline const char * eapol_sm_get_method_name(struct eapol_sm *sm)
+{
+ return NULL;
+}
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* EAPOL_SUPP_SM_H */
diff --git a/src/l2_packet/Makefile b/src/l2_packet/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/l2_packet/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h
new file mode 100644
index 0000000..c7b5014
--- /dev/null
+++ b/src/l2_packet/l2_packet.h
@@ -0,0 +1,130 @@
+/*
+ * WPA Supplicant - Layer2 packet interface definition
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file defines an interface for layer 2 (link layer) packet sending and
+ * receiving. l2_packet_linux.c is one implementation for such a layer 2
+ * implementation using Linux packet sockets and l2_packet_pcap.c another one
+ * using libpcap and libdnet. When porting %wpa_supplicant to other operating
+ * systems, a new l2_packet implementation may need to be added.
+ */
+
+#ifndef L2_PACKET_H
+#define L2_PACKET_H
+
+/**
+ * struct l2_packet_data - Internal l2_packet data structure
+ *
+ * This structure is used by the l2_packet implementation to store its private
+ * data. Other files use a pointer to this data when calling the l2_packet
+ * functions, but the contents of this structure should not be used directly
+ * outside l2_packet implementation.
+ */
+struct l2_packet_data;
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct l2_ethhdr {
+ u8 h_dest[ETH_ALEN];
+ u8 h_source[ETH_ALEN];
+ be16 h_proto;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+/**
+ * l2_packet_init - Initialize l2_packet interface
+ * @ifname: Interface name
+ * @own_addr: Optional own MAC address if available from driver interface or
+ * %NULL if not available
+ * @protocol: Ethernet protocol number in host byte order
+ * @rx_callback: Callback function that will be called for each received packet
+ * @rx_callback_ctx: Callback data (ctx) for calls to rx_callback()
+ * @l2_hdr: 1 = include layer 2 header, 0 = do not include header
+ * Returns: Pointer to internal data or %NULL on failure
+ *
+ * rx_callback function will be called with src_addr pointing to the source
+ * address (MAC address) of the the packet. If l2_hdr is set to 0, buf
+ * points to len bytes of the payload after the layer 2 header and similarly,
+ * TX buffers start with payload. This behavior can be changed by setting
+ * l2_hdr=1 to include the layer 2 header in the data buffer.
+ */
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr);
+
+/**
+ * l2_packet_deinit - Deinitialize l2_packet interface
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ */
+void l2_packet_deinit(struct l2_packet_data *l2);
+
+/**
+ * l2_packet_get_own_addr - Get own layer 2 address
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @addr: Buffer for the own address (6 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr);
+
+/**
+ * l2_packet_send - Send a packet
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @dst_addr: Destination address for the packet (only used if l2_hdr == 0)
+ * @proto: Protocol/ethertype for the packet in host byte order (only used if
+ * l2_hdr == 0)
+ * @buf: Packet contents to be sent; including layer 2 header if l2_hdr was
+ * set to 1 in l2_packet_init() call. Otherwise, only the payload of the packet
+ * is included.
+ * @len: Length of the buffer (including l2 header only if l2_hdr == 1)
+ * Returns: >=0 on success, <0 on failure
+ */
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len);
+
+/**
+ * l2_packet_get_ip_addr - Get the current IP address from the interface
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @buf: Buffer for the IP address in text format
+ * @len: Maximum buffer length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to get the current IP address from the interface
+ * bound to the l2_packet. This is mainly for status information and the IP
+ * address will be stored as an ASCII string. This function is not essential
+ * for %wpa_supplicant operation, so full implementation is not required.
+ * l2_packet implementation will need to define the function, but it can return
+ * -1 if the IP address information is not available.
+ */
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len);
+
+
+/**
+ * l2_packet_notify_auth_start - Notify l2_packet about start of authentication
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ *
+ * This function is called when authentication is expected to start, e.g., when
+ * association has been completed, in order to prepare l2_packet implementation
+ * for EAPOL frames. This function is used mainly if the l2_packet code needs
+ * to do polling in which case it can increasing polling frequency. This can
+ * also be an empty function if the l2_packet implementation does not benefit
+ * from knowing about the starting authentication.
+ */
+void l2_packet_notify_auth_start(struct l2_packet_data *l2);
+
+#endif /* L2_PACKET_H */
diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c
new file mode 100644
index 0000000..e24277c
--- /dev/null
+++ b/src/l2_packet/l2_packet_freebsd.c
@@ -0,0 +1,316 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with FreeBSD
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005, Sam Leffler <sam@errno.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#if defined(__APPLE__) || defined(__GLIBC__)
+#include <net/bpf.h>
+#endif /* __APPLE__ */
+#include <pcap.h>
+
+#include <sys/ioctl.h>
+#ifdef __sun__
+#include <libdlpi.h>
+#else /* __sun__ */
+#include <sys/sysctl.h>
+#endif /* __sun__ */
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct l2_packet_data {
+ pcap_t *pcap;
+ char ifname[100];
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+ * buffers */
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ if (!l2->l2_hdr) {
+ int ret;
+ struct l2_ethhdr *eth = os_malloc(sizeof(*eth) + len);
+ if (eth == NULL)
+ return -1;
+ os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+ os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+ eth->h_proto = htons(proto);
+ os_memcpy(eth + 1, buf, len);
+ ret = pcap_inject(l2->pcap, (u8 *) eth, len + sizeof(*eth));
+ os_free(eth);
+ return ret;
+ } else
+ return pcap_inject(l2->pcap, buf, len);
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ pcap_t *pcap = sock_ctx;
+ struct pcap_pkthdr hdr;
+ const u_char *packet;
+ struct l2_ethhdr *ethhdr;
+ unsigned char *buf;
+ size_t len;
+
+ packet = pcap_next(pcap, &hdr);
+
+ if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
+ return;
+
+ ethhdr = (struct l2_ethhdr *) packet;
+ if (l2->l2_hdr) {
+ buf = (unsigned char *) ethhdr;
+ len = hdr.caplen;
+ } else {
+ buf = (unsigned char *) (ethhdr + 1);
+ len = hdr.caplen - sizeof(*ethhdr);
+ }
+ l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+}
+
+
+static int l2_packet_init_libpcap(struct l2_packet_data *l2,
+ unsigned short protocol)
+{
+ bpf_u_int32 pcap_maskp, pcap_netp;
+ char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
+ struct bpf_program pcap_fp;
+
+ pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+ l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err);
+ if (l2->pcap == NULL) {
+ fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+ fprintf(stderr, "ifname='%s'\n", l2->ifname);
+ return -1;
+ }
+ if (pcap_datalink(l2->pcap) != DLT_EN10MB &&
+ pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) {
+ fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n",
+ pcap_geterr(l2->pcap));
+ return -1;
+ }
+ os_snprintf(pcap_filter, sizeof(pcap_filter),
+ "not ether src " MACSTR " and "
+ "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+ "ether proto 0x%x",
+ MAC2STR(l2->own_addr), /* do not receive own packets */
+ MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+ protocol);
+ if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
+ fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
+ return -1;
+ }
+
+ if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
+ fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
+ return -1;
+ }
+
+ pcap_freecode(&pcap_fp);
+#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/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
new file mode 100644
index 0000000..93e15eb
--- /dev/null
+++ b/src/l2_packet/l2_packet_linux.c
@@ -0,0 +1,210 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with Linux packet sockets
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <netpacket/packet.h>
+#include <net/if.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+struct l2_packet_data {
+ int fd; /* packet socket for EAPOL frames */
+ char ifname[IFNAMSIZ + 1];
+ int ifindex;
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+ * buffers */
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ int ret;
+ if (l2 == NULL)
+ return -1;
+ if (l2->l2_hdr) {
+ ret = send(l2->fd, buf, len, 0);
+ if (ret < 0)
+ 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/src/l2_packet/l2_packet_ndis.c b/src/l2_packet/l2_packet_ndis.c
new file mode 100644
index 0000000..6ce29aa
--- /dev/null
+++ b/src/l2_packet/l2_packet_ndis.c
@@ -0,0 +1,522 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This implementation requires Windows specific event loop implementation,
+ * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with
+ * driver_ndis.c, so only that driver interface can be used and
+ * CONFIG_USE_NDISUIO must be defined.
+ *
+ * WinXP version of the code uses overlapped I/O and a single threaded design
+ * with callback functions from I/O code. WinCE version uses a separate RX
+ * thread that blocks on ReadFile() whenever the media status is connected.
+ */
+
+#include "includes.h"
+#include <winsock2.h>
+#include <ntddndis.h>
+
+#ifdef _WIN32_WCE
+#include <winioctl.h>
+#include <nuiouser.h>
+#endif /* _WIN32_WCE */
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+#ifndef _WIN32_WCE
+/* from nuiouser.h */
+#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK
+#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
+ CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
+#define IOCTL_NDISUIO_SET_ETHER_TYPE \
+ _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#endif /* _WIN32_WCE */
+
+/* From driver_ndis.c to shared the handle to NDISUIO */
+HANDLE driver_ndis_get_ndisuio_handle(void);
+
+/*
+ * NDISUIO supports filtering of only one ethertype at the time, so we must
+ * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth
+ * whenever wpa_supplicant is trying to pre-authenticate and then switching
+ * back to EAPOL when pre-authentication has been completed.
+ */
+
+struct l2_packet_data;
+
+struct l2_packet_ndisuio_global {
+ int refcount;
+ unsigned short first_proto;
+ struct l2_packet_data *l2[2];
+#ifdef _WIN32_WCE
+ HANDLE rx_thread;
+ HANDLE stop_request;
+ HANDLE ready_for_read;
+ HANDLE rx_processed;
+#endif /* _WIN32_WCE */
+};
+
+static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
+
+struct l2_packet_data {
+ char ifname[100];
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
+ * rx_callback and l2_packet_send() */
+ HANDLE rx_avail;
+#ifndef _WIN32_WCE
+ OVERLAPPED rx_overlapped;
+#endif /* _WIN32_WCE */
+ u8 rx_buf[1514];
+ DWORD rx_written;
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ BOOL res;
+ DWORD written;
+ struct l2_ethhdr *eth;
+#ifndef _WIN32_WCE
+ OVERLAPPED overlapped;
+#endif /* _WIN32_WCE */
+ OVERLAPPED *o;
+
+ if (l2 == NULL)
+ return -1;
+
+#ifdef _WIN32_WCE
+ o = NULL;
+#else /* _WIN32_WCE */
+ os_memset(&overlapped, 0, sizeof(overlapped));
+ o = &overlapped;
+#endif /* _WIN32_WCE */
+
+ if (l2->l2_hdr) {
+ res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len,
+ &written, o);
+ } else {
+ size_t mlen = sizeof(*eth) + len;
+ eth = os_malloc(mlen);
+ if (eth == NULL)
+ return -1;
+
+ os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+ os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+ eth->h_proto = htons(proto);
+ os_memcpy(eth + 1, buf, len);
+ res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen,
+ &written, o);
+ os_free(eth);
+ }
+
+ if (!res) {
+ DWORD err = GetLastError();
+#ifndef _WIN32_WCE
+ if (err == ERROR_IO_PENDING) {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): Wait for pending "
+ "write to complete");
+ res = GetOverlappedResult(
+ driver_ndis_get_ndisuio_handle(), &overlapped,
+ &written, TRUE);
+ if (!res) {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): "
+ "GetOverlappedResult failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+ return 0;
+ }
+#endif /* _WIN32_WCE */
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void l2_packet_callback(struct l2_packet_data *l2);
+
+#ifdef _WIN32_WCE
+static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
+{
+ HANDLE handles[2];
+
+ wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile");
+ if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
+ sizeof(l2->rx_buf), &l2->rx_written, NULL)) {
+ DWORD err = GetLastError();
+ wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: "
+ "%d", (int) err);
+ /*
+ * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED
+ * error whenever the connection is not up. Yield the thread to
+ * avoid triggering a busy loop. Connection event should stop
+ * us from looping for long, but we need to allow enough CPU
+ * for the main thread to process the media disconnection.
+ */
+ Sleep(100);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet",
+ (int) l2->rx_written);
+
+ /*
+ * Notify the main thread about the availability of a frame and wait
+ * for the frame to be processed.
+ */
+ SetEvent(l2->rx_avail);
+ handles[0] = l2_ndisuio_global->stop_request;
+ handles[1] = l2_ndisuio_global->rx_processed;
+ WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ ResetEvent(l2_ndisuio_global->rx_processed);
+}
+
+
+static DWORD WINAPI l2_packet_rx_thread(LPVOID arg)
+{
+ struct l2_packet_data *l2 = arg;
+ DWORD res;
+ HANDLE handles[2];
+ int run = 1;
+
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started");
+ handles[0] = l2_ndisuio_global->stop_request;
+ handles[1] = l2_ndisuio_global->ready_for_read;
+
+ /*
+ * Unfortunately, NDISUIO on WinCE does not seem to support waiting
+ * on the handle. There do not seem to be anything else that we could
+ * wait for either. If one were to modify NDISUIO to set a named event
+ * whenever packets are available, this event could be used here to
+ * avoid having to poll for new packets or we could even move to use a
+ * single threaded design.
+ *
+ * In addition, NDISUIO on WinCE is returning
+ * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while
+ * the adapter is not in connected state. For now, we are just using a
+ * local event to allow ReadFile calls only after having received NDIS
+ * media connect event. This event could be easily converted to handle
+ * another event if the protocol driver is replaced with somewhat more
+ * useful design.
+ */
+
+ while (l2_ndisuio_global && run) {
+ res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ switch (res) {
+ case WAIT_OBJECT_0:
+ wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
+ "request to stop RX thread");
+ run = 0;
+ break;
+ case WAIT_OBJECT_0 + 1:
+ l2_packet_rx_thread_try_read(l2);
+ break;
+ case WAIT_FAILED:
+ default:
+ wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
+ "WaitForMultipleObjects failed: %d",
+ (int) GetLastError());
+ run = 0;
+ break;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
+
+ return 0;
+}
+#else /* _WIN32_WCE */
+static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive)
+{
+ os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped));
+ l2->rx_overlapped.hEvent = l2->rx_avail;
+ if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
+ sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped))
+ {
+ DWORD err = GetLastError();
+ if (err != ERROR_IO_PENDING) {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: "
+ "%d", (int) err);
+ return -1;
+ }
+ /*
+ * Once read is completed, l2_packet_rx_event() will be
+ * called.
+ */
+ } else {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
+ "without wait for completion");
+ if (!recursive)
+ l2_packet_callback(l2);
+ }
+
+ return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static void l2_packet_callback(struct l2_packet_data *l2)
+{
+ const u8 *rx_buf, *rx_src;
+ size_t rx_len;
+ struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf;
+
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes",
+ (int) l2->rx_written);
+
+ if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) {
+ rx_buf = (u8 *) ethhdr;
+ rx_len = l2->rx_written;
+ } else {
+ rx_buf = (u8 *) (ethhdr + 1);
+ rx_len = l2->rx_written - sizeof(*ethhdr);
+ }
+ rx_src = ethhdr->h_source;
+
+ l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len);
+#ifndef _WIN32_WCE
+ l2_ndisuio_start_read(l2, 1);
+#endif /* _WIN32_WCE */
+}
+
+
+static void l2_packet_rx_event(void *eloop_data, void *user_data)
+{
+ struct l2_packet_data *l2 = eloop_data;
+
+ if (l2_ndisuio_global)
+ l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1];
+
+ ResetEvent(l2->rx_avail);
+
+#ifndef _WIN32_WCE
+ if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(),
+ &l2->rx_overlapped, &l2->rx_written, FALSE)) {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult "
+ "failed: %d", (int) GetLastError());
+ return;
+ }
+#endif /* _WIN32_WCE */
+
+ l2_packet_callback(l2);
+
+#ifdef _WIN32_WCE
+ SetEvent(l2_ndisuio_global->rx_processed);
+#endif /* _WIN32_WCE */
+}
+
+
+static int l2_ndisuio_set_ether_type(unsigned short protocol)
+{
+ USHORT proto = htons(protocol);
+ DWORD written;
+
+ if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
+ IOCTL_NDISUIO_SET_ETHER_TYPE, &proto,
+ sizeof(proto), NULL, 0, &written, NULL)) {
+ wpa_printf(MSG_ERROR, "L2(NDISUIO): "
+ "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+
+ if (l2_ndisuio_global == NULL) {
+ l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global));
+ if (l2_ndisuio_global == NULL)
+ return NULL;
+ l2_ndisuio_global->first_proto = protocol;
+ }
+ if (l2_ndisuio_global->refcount >= 2) {
+ wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two "
+ "simultaneous connections allowed");
+ return NULL;
+ }
+ l2_ndisuio_global->refcount++;
+
+ l2 = os_zalloc(sizeof(struct l2_packet_data));
+ if (l2 == NULL)
+ return NULL;
+ l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
+
+ os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+ l2->rx_callback = rx_callback;
+ l2->rx_callback_ctx = rx_callback_ctx;
+ l2->l2_hdr = l2_hdr;
+
+ if (own_addr)
+ os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
+
+ if (l2_ndisuio_set_ether_type(protocol) < 0) {
+ os_free(l2);
+ return NULL;
+ }
+
+ if (l2_ndisuio_global->refcount > 1) {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting "
+ "filtering ethertype to %04x", protocol);
+ if (l2_ndisuio_global->l2[0])
+ l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail;
+ return l2;
+ }
+
+ l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (l2->rx_avail == NULL) {
+ os_free(l2);
+ return NULL;
+ }
+
+ eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
+ l2_packet_rx_event, l2, NULL);
+
+#ifdef _WIN32_WCE
+ l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL);
+ /*
+ * This event is being set based on media connect/disconnect
+ * notifications in driver_ndis.c.
+ */
+ l2_ndisuio_global->ready_for_read =
+ CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
+ l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (l2_ndisuio_global->stop_request == NULL ||
+ l2_ndisuio_global->ready_for_read == NULL ||
+ l2_ndisuio_global->rx_processed == NULL) {
+ if (l2_ndisuio_global->stop_request) {
+ CloseHandle(l2_ndisuio_global->stop_request);
+ l2_ndisuio_global->stop_request = NULL;
+ }
+ if (l2_ndisuio_global->ready_for_read) {
+ CloseHandle(l2_ndisuio_global->ready_for_read);
+ l2_ndisuio_global->ready_for_read = NULL;
+ }
+ if (l2_ndisuio_global->rx_processed) {
+ CloseHandle(l2_ndisuio_global->rx_processed);
+ l2_ndisuio_global->rx_processed = NULL;
+ }
+ eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+ os_free(l2);
+ return NULL;
+ }
+
+ l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
+ l2_packet_rx_thread, l2, 0,
+ NULL);
+ if (l2_ndisuio_global->rx_thread == NULL) {
+ wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX "
+ "thread: %d", (int) GetLastError());
+ eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+ CloseHandle(l2_ndisuio_global->stop_request);
+ l2_ndisuio_global->stop_request = NULL;
+ os_free(l2);
+ return NULL;
+ }
+#else /* _WIN32_WCE */
+ l2_ndisuio_start_read(l2, 0);
+#endif /* _WIN32_WCE */
+
+ return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+ if (l2_ndisuio_global) {
+ l2_ndisuio_global->refcount--;
+ l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL;
+ if (l2_ndisuio_global->refcount) {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering "
+ "ethertype to %04x",
+ l2_ndisuio_global->first_proto);
+ l2_ndisuio_set_ether_type(
+ l2_ndisuio_global->first_proto);
+ return;
+ }
+
+#ifdef _WIN32_WCE
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
+ "stop");
+ SetEvent(l2_ndisuio_global->stop_request);
+ /*
+ * Cancel pending ReadFile() in the RX thread (if we were still
+ * connected at this point).
+ */
+ if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
+ IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL,
+ NULL)) {
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ "
+ "failed: %d", (int) GetLastError());
+ /* RX thread will exit blocking ReadFile once NDISUIO
+ * notices that the adapter is disconnected. */
+ }
+ WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE);
+ wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited");
+ CloseHandle(l2_ndisuio_global->rx_thread);
+ CloseHandle(l2_ndisuio_global->stop_request);
+ CloseHandle(l2_ndisuio_global->ready_for_read);
+ CloseHandle(l2_ndisuio_global->rx_processed);
+#endif /* _WIN32_WCE */
+
+ os_free(l2_ndisuio_global);
+ l2_ndisuio_global = NULL;
+ }
+
+#ifndef _WIN32_WCE
+ CancelIo(driver_ndis_get_ndisuio_handle());
+#endif /* _WIN32_WCE */
+
+ eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+ CloseHandle(l2->rx_avail);
+ os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+}
diff --git a/src/l2_packet/l2_packet_none.c b/src/l2_packet/l2_packet_none.c
new file mode 100644
index 0000000..5e3f6e9
--- /dev/null
+++ b/src/l2_packet/l2_packet_none.c
@@ -0,0 +1,123 @@
+/*
+ * WPA Supplicant - Layer2 packet handling example with dummy functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file can be used as a starting point for layer2 packet implementation.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+struct l2_packet_data {
+ char ifname[17];
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+ * buffers */
+ int fd;
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ if (l2 == NULL)
+ return -1;
+
+ /*
+ * TODO: Send frame (may need different implementation depending on
+ * whether l2->l2_hdr is set).
+ */
+
+ return 0;
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ u8 buf[2300];
+ int res;
+
+ /* TODO: receive frame (e.g., recv() using sock */
+ buf[0] = 0;
+ res = 0;
+
+ l2->rx_callback(l2->rx_callback_ctx, NULL /* TODO: src addr */,
+ buf, res);
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+
+ l2 = os_zalloc(sizeof(struct l2_packet_data));
+ if (l2 == NULL)
+ return NULL;
+ os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+ l2->rx_callback = rx_callback;
+ l2->rx_callback_ctx = rx_callback_ctx;
+ l2->l2_hdr = l2_hdr;
+
+ /*
+ * TODO: open connection for receiving frames
+ */
+ l2->fd = -1;
+ eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+
+ return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+ if (l2->fd >= 0) {
+ eloop_unregister_read_sock(l2->fd);
+ /* TODO: close connection */
+ }
+
+ os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ /* TODO: get interface IP address */
+ return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+ /* This function can be left empty */
+}
diff --git a/src/l2_packet/l2_packet_pcap.c b/src/l2_packet/l2_packet_pcap.c
new file mode 100644
index 0000000..8156e29
--- /dev/null
+++ b/src/l2_packet/l2_packet_pcap.c
@@ -0,0 +1,386 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with libpcap/libdnet and WinPcap
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/ioctl.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+#include <pcap.h>
+#ifndef CONFIG_WINPCAP
+#include <dnet.h>
+#endif /* CONFIG_WINPCAP */
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct l2_packet_data {
+ pcap_t *pcap;
+#ifdef CONFIG_WINPCAP
+ unsigned int num_fast_poll;
+#else /* CONFIG_WINPCAP */
+ eth_t *eth;
+#endif /* CONFIG_WINPCAP */
+ char ifname[100];
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls
+ * to rx_callback */
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+#ifndef CONFIG_WINPCAP
+static int l2_packet_init_libdnet(struct l2_packet_data *l2)
+{
+ eth_addr_t own_addr;
+
+ l2->eth = eth_open(l2->ifname);
+ if (!l2->eth) {
+ printf("Failed to open interface '%s'.\n", l2->ifname);
+ perror("eth_open");
+ return -1;
+ }
+
+ if (eth_get(l2->eth, &own_addr) < 0) {
+ printf("Failed to get own hw address from interface '%s'.\n",
+ l2->ifname);
+ perror("eth_get");
+ eth_close(l2->eth);
+ l2->eth = NULL;
+ return -1;
+ }
+ os_memcpy(l2->own_addr, own_addr.data, ETH_ALEN);
+
+ return 0;
+}
+#endif /* CONFIG_WINPCAP */
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ int ret;
+ struct l2_ethhdr *eth;
+
+ if (l2 == NULL)
+ return -1;
+
+ if (l2->l2_hdr) {
+#ifdef CONFIG_WINPCAP
+ ret = pcap_sendpacket(l2->pcap, buf, len);
+#else /* CONFIG_WINPCAP */
+ ret = eth_send(l2->eth, buf, len);
+#endif /* CONFIG_WINPCAP */
+ } else {
+ size_t mlen = sizeof(*eth) + len;
+ eth = os_malloc(mlen);
+ if (eth == NULL)
+ return -1;
+
+ os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+ os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+ eth->h_proto = htons(proto);
+ os_memcpy(eth + 1, buf, len);
+
+#ifdef CONFIG_WINPCAP
+ ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen);
+#else /* CONFIG_WINPCAP */
+ ret = eth_send(l2->eth, (u8 *) eth, mlen);
+#endif /* CONFIG_WINPCAP */
+
+ os_free(eth);
+ }
+
+ return ret;
+}
+
+
+#ifndef CONFIG_WINPCAP
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ pcap_t *pcap = sock_ctx;
+ struct pcap_pkthdr hdr;
+ const u_char *packet;
+ struct l2_ethhdr *ethhdr;
+ unsigned char *buf;
+ size_t len;
+
+ packet = pcap_next(pcap, &hdr);
+
+ if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
+ return;
+
+ ethhdr = (struct l2_ethhdr *) packet;
+ if (l2->l2_hdr) {
+ buf = (unsigned char *) ethhdr;
+ len = hdr.caplen;
+ } else {
+ buf = (unsigned char *) (ethhdr + 1);
+ len = hdr.caplen - sizeof(*ethhdr);
+ }
+ l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+}
+#endif /* CONFIG_WINPCAP */
+
+
+#ifdef CONFIG_WINPCAP
+static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr,
+ const u_char *pkt_data)
+{
+ struct l2_packet_data *l2 = (struct l2_packet_data *) user;
+ struct l2_ethhdr *ethhdr;
+ unsigned char *buf;
+ size_t len;
+
+ if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr))
+ return;
+
+ ethhdr = (struct l2_ethhdr *) pkt_data;
+ if (l2->l2_hdr) {
+ buf = (unsigned char *) ethhdr;
+ len = hdr->caplen;
+ } else {
+ buf = (unsigned char *) (ethhdr + 1);
+ len = hdr->caplen - sizeof(*ethhdr);
+ }
+ l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+ /*
+ * Use shorter poll interval for 3 seconds to reduce latency during key
+ * handshake.
+ */
+ l2->num_fast_poll = 3 * 50;
+}
+
+
+static void l2_packet_receive_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ pcap_t *pcap = timeout_ctx;
+ int timeout;
+
+ if (l2->num_fast_poll > 0) {
+ timeout = 20000;
+ l2->num_fast_poll--;
+ } else
+ timeout = 100000;
+
+ /* Register new timeout before calling l2_packet_receive() since
+ * receive handler may free this l2_packet instance (which will
+ * cancel this timeout). */
+ eloop_register_timeout(0, timeout, l2_packet_receive_timeout,
+ l2, pcap);
+ pcap_dispatch(pcap, 10, l2_packet_receive_cb, (u_char *) l2);
+}
+#endif /* CONFIG_WINPCAP */
+
+
+static int l2_packet_init_libpcap(struct l2_packet_data *l2,
+ unsigned short protocol)
+{
+ bpf_u_int32 pcap_maskp, pcap_netp;
+ char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
+ struct bpf_program pcap_fp;
+
+#ifdef CONFIG_WINPCAP
+ char ifname[128];
+ os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", l2->ifname);
+ pcap_lookupnet(ifname, &pcap_netp, &pcap_maskp, pcap_err);
+ l2->pcap = pcap_open_live(ifname, 2500, 0, 10, pcap_err);
+ if (l2->pcap == NULL) {
+ fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+ fprintf(stderr, "ifname='%s'\n", ifname);
+ return -1;
+ }
+ if (pcap_setnonblock(l2->pcap, 1, pcap_err) < 0)
+ fprintf(stderr, "pcap_setnonblock: %s\n",
+ pcap_geterr(l2->pcap));
+#else /* CONFIG_WINPCAP */
+ pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+ l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err);
+ if (l2->pcap == NULL) {
+ fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+ fprintf(stderr, "ifname='%s'\n", l2->ifname);
+ return -1;
+ }
+ if (pcap_datalink(l2->pcap) != DLT_EN10MB &&
+ pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) {
+ fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n",
+ pcap_geterr(l2->pcap));
+ return -1;
+ }
+#endif /* CONFIG_WINPCAP */
+ os_snprintf(pcap_filter, sizeof(pcap_filter),
+ "not ether src " MACSTR " and "
+ "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+ "ether proto 0x%x",
+ MAC2STR(l2->own_addr), /* do not receive own packets */
+ MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+ protocol);
+ if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
+ fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
+ return -1;
+ }
+
+ if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
+ fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
+ return -1;
+ }
+
+ pcap_freecode(&pcap_fp);
+#ifdef BIOCIMMEDIATE
+ /*
+ * When libpcap uses BPF we must enable "immediate mode" to
+ * receive frames right away; otherwise the system may
+ * buffer them for us.
+ */
+ {
+ unsigned int on = 1;
+ if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) {
+ fprintf(stderr, "%s: cannot enable immediate mode on "
+ "interface %s: %s\n",
+ __func__, l2->ifname, strerror(errno));
+ /* XXX should we fail? */
+ }
+ }
+#endif /* BIOCIMMEDIATE */
+
+#ifdef CONFIG_WINPCAP
+ eloop_register_timeout(0, 100000, l2_packet_receive_timeout,
+ l2, l2->pcap);
+#else /* CONFIG_WINPCAP */
+ eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
+ l2_packet_receive, l2, l2->pcap);
+#endif /* CONFIG_WINPCAP */
+
+ return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+
+ l2 = os_zalloc(sizeof(struct l2_packet_data));
+ if (l2 == NULL)
+ return NULL;
+ os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+ l2->rx_callback = rx_callback;
+ l2->rx_callback_ctx = rx_callback_ctx;
+ l2->l2_hdr = l2_hdr;
+
+#ifdef CONFIG_WINPCAP
+ if (own_addr)
+ os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
+#else /* CONFIG_WINPCAP */
+ if (l2_packet_init_libdnet(l2))
+ return NULL;
+#endif /* CONFIG_WINPCAP */
+
+ if (l2_packet_init_libpcap(l2, protocol)) {
+#ifndef CONFIG_WINPCAP
+ eth_close(l2->eth);
+#endif /* CONFIG_WINPCAP */
+ os_free(l2);
+ return NULL;
+ }
+
+ return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+#ifdef CONFIG_WINPCAP
+ eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap);
+#else /* CONFIG_WINPCAP */
+ if (l2->eth)
+ eth_close(l2->eth);
+ eloop_unregister_read_sock(pcap_get_selectable_fd(l2->pcap));
+#endif /* CONFIG_WINPCAP */
+ if (l2->pcap)
+ pcap_close(l2->pcap);
+ os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ pcap_if_t *devs, *dev;
+ struct pcap_addr *addr;
+ struct sockaddr_in *saddr;
+ int found = 0;
+ char err[PCAP_ERRBUF_SIZE + 1];
+
+ if (pcap_findalldevs(&devs, err) < 0) {
+ wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+ return -1;
+ }
+
+ for (dev = devs; dev && !found; dev = dev->next) {
+ if (os_strcmp(dev->name, l2->ifname) != 0)
+ continue;
+
+ addr = dev->addresses;
+ while (addr) {
+ saddr = (struct sockaddr_in *) addr->addr;
+ if (saddr && saddr->sin_family == AF_INET) {
+ os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
+ len);
+ found = 1;
+ break;
+ }
+ addr = addr->next;
+ }
+ }
+
+ pcap_freealldevs(devs);
+
+ return found ? 0 : -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+#ifdef CONFIG_WINPCAP
+ /*
+ * Use shorter poll interval for 3 seconds to reduce latency during key
+ * handshake.
+ */
+ l2->num_fast_poll = 3 * 50;
+ eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap);
+ eloop_register_timeout(0, 10000, l2_packet_receive_timeout,
+ l2, l2->pcap);
+#endif /* CONFIG_WINPCAP */
+}
diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c
new file mode 100644
index 0000000..79d2968
--- /dev/null
+++ b/src/l2_packet/l2_packet_privsep.c
@@ -0,0 +1,267 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with privilege separation
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <sys/un.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+#include "common/privsep_commands.h"
+
+
+struct l2_packet_data {
+ int fd; /* UNIX domain socket for privsep access */
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ u8 own_addr[ETH_ALEN];
+ char *own_socket_path;
+ struct sockaddr_un priv_addr;
+};
+
+
+static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd,
+ const void *data, size_t data_len)
+{
+ struct msghdr msg;
+ struct iovec io[2];
+
+ io[0].iov_base = &cmd;
+ io[0].iov_len = sizeof(cmd);
+ io[1].iov_base = (u8 *) data;
+ io[1].iov_len = data_len;
+
+ os_memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = data ? 2 : 1;
+ msg.msg_name = &l2->priv_addr;
+ msg.msg_namelen = sizeof(l2->priv_addr);
+
+ if (sendmsg(l2->fd, &msg, 0) < 0) {
+ perror("L2: sendmsg(cmd)");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ struct msghdr msg;
+ struct iovec io[4];
+ int cmd = PRIVSEP_CMD_L2_SEND;
+
+ io[0].iov_base = &cmd;
+ io[0].iov_len = sizeof(cmd);
+ io[1].iov_base = &dst_addr;
+ io[1].iov_len = ETH_ALEN;
+ io[2].iov_base = &proto;
+ io[2].iov_len = 2;
+ io[3].iov_base = (u8 *) buf;
+ io[3].iov_len = len;
+
+ os_memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = 4;
+ msg.msg_name = &l2->priv_addr;
+ msg.msg_namelen = sizeof(l2->priv_addr);
+
+ if (sendmsg(l2->fd, &msg, 0) < 0) {
+ perror("L2: sendmsg(packet_send)");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ u8 buf[2300];
+ int res;
+ struct sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+
+ os_memset(&from, 0, sizeof(from));
+ res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
+ &fromlen);
+ if (res < 0) {
+ perror("l2_packet_receive - recvfrom");
+ return;
+ }
+ if (res < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "L2: Too show packet received");
+ return;
+ }
+
+ if (from.sun_family != AF_UNIX ||
+ os_strncmp(from.sun_path, l2->priv_addr.sun_path,
+ sizeof(from.sun_path)) != 0) {
+ wpa_printf(MSG_DEBUG, "L2: Received message from unexpected "
+ "source");
+ return;
+ }
+
+ l2->rx_callback(l2->rx_callback_ctx, buf, buf + ETH_ALEN,
+ res - ETH_ALEN);
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+ char *own_dir = "/tmp";
+ char *priv_dir = "/var/run/wpa_priv";
+ size_t len;
+ static unsigned int counter = 0;
+ struct sockaddr_un addr;
+ fd_set rfds;
+ struct timeval tv;
+ int res;
+ u8 reply[ETH_ALEN + 1];
+ int reg_cmd[2];
+
+ l2 = os_zalloc(sizeof(struct l2_packet_data));
+ if (l2 == NULL)
+ return NULL;
+ l2->rx_callback = rx_callback;
+ l2->rx_callback_ctx = rx_callback_ctx;
+
+ len = os_strlen(own_dir) + 50;
+ l2->own_socket_path = os_malloc(len);
+ if (l2->own_socket_path == NULL) {
+ os_free(l2);
+ return NULL;
+ }
+ os_snprintf(l2->own_socket_path, len, "%s/wpa_privsep-l2-%d-%d",
+ own_dir, getpid(), counter++);
+
+ l2->priv_addr.sun_family = AF_UNIX;
+ os_snprintf(l2->priv_addr.sun_path, sizeof(l2->priv_addr.sun_path),
+ "%s/%s", priv_dir, ifname);
+
+ l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (l2->fd < 0) {
+ perror("socket(PF_UNIX)");
+ os_free(l2->own_socket_path);
+ l2->own_socket_path = NULL;
+ os_free(l2);
+ return NULL;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path));
+ if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind(PF_UNIX)");
+ goto fail;
+ }
+
+ reg_cmd[0] = protocol;
+ reg_cmd[1] = l2_hdr;
+ if (wpa_priv_cmd(l2, PRIVSEP_CMD_L2_REGISTER, reg_cmd, sizeof(reg_cmd))
+ < 0) {
+ wpa_printf(MSG_ERROR, "L2: Failed to register with wpa_priv");
+ goto fail;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(l2->fd, &rfds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ res = select(l2->fd + 1, &rfds, NULL, NULL, &tv);
+ if (res < 0 && errno != EINTR) {
+ perror("select");
+ goto fail;
+ }
+
+ if (FD_ISSET(l2->fd, &rfds)) {
+ res = recv(l2->fd, reply, sizeof(reply), 0);
+ if (res < 0) {
+ perror("recv");
+ goto fail;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "L2: Timeout while waiting for "
+ "registration reply");
+ goto fail;
+ }
+
+ if (res != ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "L2: Unexpected registration reply "
+ "(len=%d)", res);
+ }
+ os_memcpy(l2->own_addr, reply, ETH_ALEN);
+
+ eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+
+ return l2;
+
+fail:
+ close(l2->fd);
+ l2->fd = -1;
+ unlink(l2->own_socket_path);
+ os_free(l2->own_socket_path);
+ l2->own_socket_path = NULL;
+ os_free(l2);
+ return NULL;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+ if (l2->fd >= 0) {
+ wpa_priv_cmd(l2, PRIVSEP_CMD_L2_UNREGISTER, NULL, 0);
+ eloop_unregister_read_sock(l2->fd);
+ close(l2->fd);
+ }
+
+ if (l2->own_socket_path) {
+ unlink(l2->own_socket_path);
+ os_free(l2->own_socket_path);
+ }
+
+ os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ /* TODO */
+ return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+ wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0);
+}
diff --git a/src/l2_packet/l2_packet_winpcap.c b/src/l2_packet/l2_packet_winpcap.c
new file mode 100644
index 0000000..f76b386
--- /dev/null
+++ b/src/l2_packet/l2_packet_winpcap.c
@@ -0,0 +1,341 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with WinPcap RX thread
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This l2_packet implementation is explicitly for WinPcap and Windows events.
+ * l2_packet_pcap.c has support for WinPcap, but it requires polling to receive
+ * frames which means relatively long latency for EAPOL RX processing. The
+ * implementation here uses a separate thread to allow WinPcap to be receiving
+ * all the time to reduce latency for EAPOL receiving from about 100 ms to 3 ms
+ * when comparing l2_packet_pcap.c to l2_packet_winpcap.c. Extra sleep of 50 ms
+ * is added in to receive thread whenever no EAPOL frames has been received for
+ * a while. Whenever an EAPOL handshake is expected, this sleep is removed.
+ *
+ * The RX thread receives a frame and signals main thread through Windows event
+ * about the availability of a new frame. Processing the received frame is
+ * synchronized with pair of Windows events so that no extra buffer or queuing
+ * mechanism is needed. This implementation requires Windows specific event
+ * loop implementation, i.e., eloop_win.c.
+ *
+ * WinPcap has pcap_getevent() that could, in theory at least, be used to
+ * implement this kind of waiting with a simpler single-thread design. However,
+ * that event handle is not really signaled immediately when receiving each
+ * frame, so it does not really work for this kind of use.
+ */
+
+#include "includes.h"
+#include <pcap.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+/*
+ * Number of pcap_dispatch() iterations to do without extra wait after each
+ * received EAPOL packet or authentication notification. This is used to reduce
+ * latency for EAPOL receive.
+ */
+static const size_t no_wait_count = 750;
+
+struct l2_packet_data {
+ pcap_t *pcap;
+ unsigned int num_fast_poll;
+ char ifname[100];
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
+ * rx_callback and l2_packet_send() */
+ int running;
+ HANDLE rx_avail, rx_done, rx_thread, rx_thread_done, rx_notify;
+ u8 *rx_buf, *rx_src;
+ size_t rx_len;
+ size_t rx_no_wait;
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ int ret;
+ struct l2_ethhdr *eth;
+
+ if (l2 == NULL)
+ return -1;
+
+ if (l2->l2_hdr) {
+ ret = pcap_sendpacket(l2->pcap, buf, len);
+ } else {
+ size_t mlen = sizeof(*eth) + len;
+ eth = os_malloc(mlen);
+ if (eth == NULL)
+ return -1;
+
+ os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+ os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+ eth->h_proto = htons(proto);
+ os_memcpy(eth + 1, buf, len);
+ ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen);
+ os_free(eth);
+ }
+
+ return ret;
+}
+
+
+/* pcap_dispatch() callback for the RX thread */
+static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr,
+ const u_char *pkt_data)
+{
+ struct l2_packet_data *l2 = (struct l2_packet_data *) user;
+ struct l2_ethhdr *ethhdr;
+
+ if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr))
+ return;
+
+ ethhdr = (struct l2_ethhdr *) pkt_data;
+ if (l2->l2_hdr) {
+ l2->rx_buf = (u8 *) ethhdr;
+ l2->rx_len = hdr->caplen;
+ } else {
+ l2->rx_buf = (u8 *) (ethhdr + 1);
+ l2->rx_len = hdr->caplen - sizeof(*ethhdr);
+ }
+ l2->rx_src = ethhdr->h_source;
+ SetEvent(l2->rx_avail);
+ WaitForSingleObject(l2->rx_done, INFINITE);
+ ResetEvent(l2->rx_done);
+ l2->rx_no_wait = no_wait_count;
+}
+
+
+/* main RX loop that is running in a separate thread */
+static DWORD WINAPI l2_packet_receive_thread(LPVOID arg)
+{
+ struct l2_packet_data *l2 = arg;
+
+ while (l2->running) {
+ pcap_dispatch(l2->pcap, 1, l2_packet_receive_cb,
+ (u_char *) l2);
+ if (l2->rx_no_wait > 0)
+ l2->rx_no_wait--;
+ if (WaitForSingleObject(l2->rx_notify,
+ l2->rx_no_wait ? 0 : 50) ==
+ WAIT_OBJECT_0) {
+ l2->rx_no_wait = no_wait_count;
+ ResetEvent(l2->rx_notify);
+ }
+ }
+ SetEvent(l2->rx_thread_done);
+ ExitThread(0);
+ return 0;
+}
+
+
+/* main thread RX event handler */
+static void l2_packet_rx_event(void *eloop_data, void *user_data)
+{
+ struct l2_packet_data *l2 = eloop_data;
+ l2->rx_callback(l2->rx_callback_ctx, l2->rx_src, l2->rx_buf,
+ l2->rx_len);
+ ResetEvent(l2->rx_avail);
+ SetEvent(l2->rx_done);
+}
+
+
+static int l2_packet_init_libpcap(struct l2_packet_data *l2,
+ unsigned short protocol)
+{
+ bpf_u_int32 pcap_maskp, pcap_netp;
+ char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
+ struct bpf_program pcap_fp;
+
+ pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+ l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 1, pcap_err);
+ if (l2->pcap == NULL) {
+ fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+ fprintf(stderr, "ifname='%s'\n", l2->ifname);
+ return -1;
+ }
+ os_snprintf(pcap_filter, sizeof(pcap_filter),
+ "not ether src " MACSTR " and "
+ "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+ "ether proto 0x%x",
+ MAC2STR(l2->own_addr), /* do not receive own packets */
+ MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+ protocol);
+ if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
+ fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
+ return -1;
+ }
+
+ if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
+ fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
+ return -1;
+ }
+
+ pcap_freecode(&pcap_fp);
+
+ return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+ DWORD thread_id;
+
+ l2 = os_zalloc(sizeof(struct l2_packet_data));
+ if (l2 == NULL)
+ return NULL;
+ if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0)
+ os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+ else
+ os_snprintf(l2->ifname, sizeof(l2->ifname), "\\Device\\NPF_%s",
+ ifname);
+ l2->rx_callback = rx_callback;
+ l2->rx_callback_ctx = rx_callback_ctx;
+ l2->l2_hdr = l2_hdr;
+
+ if (own_addr)
+ os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
+
+ if (l2_packet_init_libpcap(l2, protocol)) {
+ os_free(l2);
+ return NULL;
+ }
+
+ l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+ l2->rx_done = CreateEvent(NULL, TRUE, FALSE, NULL);
+ l2->rx_notify = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (l2->rx_avail == NULL || l2->rx_done == NULL ||
+ l2->rx_notify == NULL) {
+ CloseHandle(l2->rx_avail);
+ CloseHandle(l2->rx_done);
+ CloseHandle(l2->rx_notify);
+ pcap_close(l2->pcap);
+ os_free(l2);
+ return NULL;
+ }
+
+ eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
+ l2_packet_rx_event, l2, NULL);
+
+ l2->running = 1;
+ l2->rx_thread = CreateThread(NULL, 0, l2_packet_receive_thread, l2, 0,
+ &thread_id);
+
+ return l2;
+}
+
+
+static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+
+ if (l2->rx_thread_done &&
+ WaitForSingleObject(l2->rx_thread_done, 2000) != WAIT_OBJECT_0) {
+ wpa_printf(MSG_DEBUG, "l2_packet_winpcap: RX thread did not "
+ "exit - kill it\n");
+ TerminateThread(l2->rx_thread, 0);
+ }
+ CloseHandle(l2->rx_thread_done);
+ CloseHandle(l2->rx_thread);
+ if (l2->pcap)
+ pcap_close(l2->pcap);
+ eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+ CloseHandle(l2->rx_avail);
+ CloseHandle(l2->rx_done);
+ CloseHandle(l2->rx_notify);
+ os_free(l2);
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+ l2->rx_thread_done = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ l2->running = 0;
+ pcap_breakloop(l2->pcap);
+
+ /*
+ * RX thread may be waiting in l2_packet_receive_cb() for l2->rx_done
+ * event and this event is set in l2_packet_rx_event(). However,
+ * l2_packet_deinit() may end up being called from l2->rx_callback(),
+ * so we need to return from here and complete deinitialization in
+ * a registered timeout to avoid having to forcefully kill the RX
+ * thread.
+ */
+ eloop_register_timeout(0, 0, l2_packet_deinit_timeout, l2, NULL);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ pcap_if_t *devs, *dev;
+ struct pcap_addr *addr;
+ struct sockaddr_in *saddr;
+ int found = 0;
+ char err[PCAP_ERRBUF_SIZE + 1];
+
+ if (pcap_findalldevs(&devs, err) < 0) {
+ wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+ return -1;
+ }
+
+ for (dev = devs; dev && !found; dev = dev->next) {
+ if (os_strcmp(dev->name, l2->ifname) != 0)
+ continue;
+
+ addr = dev->addresses;
+ while (addr) {
+ saddr = (struct sockaddr_in *) addr->addr;
+ if (saddr && saddr->sin_family == AF_INET) {
+ os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
+ len);
+ found = 1;
+ break;
+ }
+ addr = addr->next;
+ }
+ }
+
+ pcap_freealldevs(devs);
+
+ return found ? 0 : -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+ if (l2)
+ SetEvent(l2->rx_notify);
+}
diff --git a/src/lib.rules b/src/lib.rules
new file mode 100644
index 0000000..b260d25
--- /dev/null
+++ b/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/src/p2p/Makefile b/src/p2p/Makefile
new file mode 100644
index 0000000..cffba62
--- /dev/null
+++ b/src/p2p/Makefile
@@ -0,0 +1,9 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ for d in $(SUBDIRS); do make -C $$d clean; done
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
new file mode 100644
index 0000000..653609e
--- /dev/null
+++ b/src/p2p/p2p.c
@@ -0,0 +1,3490 @@
+/*
+ * Wi-Fi Direct - P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "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;
+
+ 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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer "
+ "entry " MACSTR, MAC2STR(dev->info.p2p_device_addr));
+ dl_list_del(&dev->list);
+ p2p_device_free(p2p, dev);
+ }
+}
+
+
+static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ p2p_expire_peers(p2p);
+ eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
+ p2p_expiration_timeout, p2p, NULL);
+}
+
+
+static const char * p2p_state_txt(int state)
+{
+ switch (state) {
+ case P2P_IDLE:
+ return "IDLE";
+ case P2P_SEARCH:
+ return "SEARCH";
+ case P2P_CONNECT:
+ return "CONNECT";
+ case P2P_CONNECT_LISTEN:
+ return "CONNECT_LISTEN";
+ case P2P_GO_NEG:
+ return "GO_NEG";
+ case P2P_LISTEN_ONLY:
+ return "LISTEN_ONLY";
+ case P2P_WAIT_PEER_CONNECT:
+ return "WAIT_PEER_CONNECT";
+ case P2P_WAIT_PEER_IDLE:
+ return "WAIT_PEER_IDLE";
+ case P2P_SD_DURING_FIND:
+ return "SD_DURING_FIND";
+ case P2P_PROVISIONING:
+ return "PROVISIONING";
+ case P2P_PD_DURING_FIND:
+ return "PD_DURING_FIND";
+ case P2P_INVITE:
+ return "INVITE";
+ case P2P_INVITE_LISTEN:
+ return "INVITE_LISTEN";
+ default:
+ return "?";
+ }
+}
+
+
+void p2p_set_state(struct p2p_data *p2p, int new_state)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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);
+ 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)
+{
+ unsigned int r, tu;
+ int freq;
+ struct wpabuf *ies;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Starting short listen state (state=%s)",
+ p2p_state_txt(p2p->state));
+
+ freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ 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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Going to listen(only) state");
+
+ freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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_memcpy(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];
+ dev = p2p_get_device(p2p, cli->p2p_device_addr);
+ if (dev) {
+ /*
+ * Update information only if we have not received this
+ * directly from the client.
+ */
+ if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY |
+ P2P_DEV_PROBE_REQ_ONLY))
+ p2p_copy_client_info(dev, cli);
+ 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_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) {
+ dev->info.dev_capab = msg->capability[0];
+ 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) {
+ dev->info.config_methods = msg->config_methods ?
+ msg->config_methods : msg->wps_config_methods;
+ }
+}
+
+
+/**
+ * p2p_add_device - Add peer entries based on scan results
+ * @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
+ * @ies: IEs from the Beacon or Probe Response frame
+ * @ies_len: Length of ies buffer in octets
+ * 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, int level,
+ const u8 *ies, size_t ies_len)
+{
+ struct p2p_device *dev;
+ struct p2p_message msg;
+ const u8 *p2p_dev_addr;
+ int i;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ies, ies_len, &msg)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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 {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not add peer "
+ "filter for " MACSTR " due to peer filter",
+ MAC2STR(p2p_dev_addr));
+ return 0;
+ }
+
+ dev = p2p_create_device(p2p, p2p_dev_addr);
+ if (dev == NULL) {
+ p2p_parse_free(&msg);
+ return -1;
+ }
+ os_get_time(&dev->last_seen);
+ 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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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);
+ }
+ dev->listen_freq = freq;
+ if (msg.group_info)
+ dev->oper_freq = freq;
+ dev->level = level;
+
+ p2p_copy_wps_info(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;
+ }
+
+ 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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer found with Listen frequency %d MHz", freq);
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Do not report rejected device");
+ 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)
+ 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;
+
+ 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;
+ }
+
+ 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(p2p->cfg->country, reg_class, channel);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+
+ if (p2p->drv_in_listen) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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->go_neg_peer) {
+ /*
+ * Only scan the known listen frequency of the peer
+ * during GO Negotiation start.
+ */
+ freq = p2p->go_neg_peer->listen_freq;
+ if (freq <= 0)
+ freq = p2p->go_neg_peer->oper_freq;
+ type = P2P_SCAN_SPECIFIC;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+ "for freq %u (GO Neg)", freq);
+ } else if (p2p->invite_peer) {
+ /*
+ * Only scan the known listen frequency of the peer
+ * during Invite start.
+ */
+ freq = p2p->invite_peer->listen_freq;
+ if (freq <= 0)
+ freq = p2p->invite_peer->oper_freq;
+ type = P2P_SCAN_SPECIFIC;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+ "for freq %u (Invite)", freq);
+ } else if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
+ (freq = p2p_get_next_prog_freq(p2p)) > 0) {
+ type = P2P_SCAN_SOCIAL_PLUS_ONE;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+ "(+ freq %u)", freq);
+ } else {
+ type = P2P_SCAN_SOCIAL;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search");
+ }
+
+ if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
+ p2p->num_req_dev_types, p2p->req_dev_types) < 0)
+ {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Scan request failed");
+ p2p_continue_find(p2p);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+ int ret;
+ /* TODO: schedule p2p_run_after_scan to be called from TX
+ * status callback(?) */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send pending "
+ "Action frame at p2p_scan completion");
+ ret = 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:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously "
+ "requested connect with " MACSTR,
+ MAC2STR(p2p->after_scan_peer));
+ dev = p2p_get_device(p2p, p2p->after_scan_peer);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)
+{
+ int res;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting find (type=%d)",
+ type);
+ if (p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+ }
+
+ 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);
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ 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);
+ 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);
+ break;
+ default:
+ return -1;
+ }
+
+ if (res == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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 {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start "
+ "p2p_scan");
+ }
+
+ return res;
+}
+
+
+void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find");
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+ p2p_free_req_dev_types(p2p);
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+ p2p->go_neg_peer = NULL;
+ p2p->sd_peer = NULL;
+ p2p->invite_peer = NULL;
+ if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip stop_listen "
+ "since we are on correct channel for response");
+ return;
+ }
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+}
+
+
+void p2p_stop_find(struct p2p_data *p2p)
+{
+ p2p_stop_find_for_freq(p2p, 0);
+}
+
+
+static int p2p_prepare_channel(struct p2p_data *p2p, unsigned int force_freq)
+{
+ if (force_freq) {
+ u8 op_reg_class, op_channel;
+ if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
+ &op_reg_class, &op_channel) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported frequency %u MHz",
+ force_freq);
+ return -1;
+ }
+ if (!p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+ op_channel)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Frequency %u MHz (oper_class %u "
+ "channel %u) not allowed for P2P",
+ force_freq, op_reg_class, op_channel);
+ return -1;
+ }
+ p2p->op_reg_class = op_reg_class;
+ p2p->op_channel = op_channel;
+ 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 {
+ u8 op_reg_class, op_channel;
+
+ if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 &&
+ p2p_supported_freq(p2p, p2p->best_freq_overall) &&
+ p2p_freq_to_channel(p2p->cfg->country,
+ p2p->best_freq_overall,
+ &op_reg_class, &op_channel) == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Select best overall channel as "
+ "operating channel preference");
+ p2p->op_reg_class = op_reg_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->cfg->country,
+ p2p->best_freq_5,
+ &op_reg_class, &op_channel) ==
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Select best 5 GHz channel as "
+ "operating channel preference");
+ p2p->op_reg_class = op_reg_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->cfg->country,
+ p2p->best_freq_24,
+ &op_reg_class, &op_channel) ==
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Select best 2.4 GHz channel as "
+ "operating channel preference");
+ p2p->op_reg_class = op_reg_class;
+ p2p->op_channel = op_channel;
+ } else {
+ 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));
+ }
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Own preference for operation channel: "
+ "Operating Class %u Channel %u%s",
+ p2p->op_reg_class, p2p->op_channel,
+ force_freq ? " (forced)" : "");
+
+ return 0;
+}
+
+
+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)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Request to start 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);
+
+ if (p2p_prepare_channel(p2p, force_freq) < 0)
+ return -1;
+
+ dev = p2p_get_device(p2p, peer_addr);
+ if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot connect to unknown P2P Device " MACSTR,
+ MAC2STR(peer_addr));
+ return -1;
+ }
+
+ if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+ if (!(dev->info.dev_capab &
+ P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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.
+ */
+ }
+
+ 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;
+ dev->connect_reqs = 0;
+ dev->go_neg_req_sent = 0;
+ dev->go_state = UNKNOWN_GO;
+ if (persistent_group)
+ dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
+ else
+ dev->flags &= ~P2P_DEV_PREFER_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.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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 (force_freq)
+ dev->flags |= P2P_DEV_FORCE_FREQ;
+ else
+ dev->flags &= ~P2P_DEV_FORCE_FREQ;
+
+ if (p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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);
+
+ if (p2p_prepare_channel(p2p, force_freq) < 0)
+ return -1;
+
+ dev = p2p_get_device(p2p, peer_addr);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot authorize unknown P2P Device " MACSTR,
+ MAC2STR(peer_addr));
+ return -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;
+ if (persistent_group)
+ dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
+ else
+ dev->flags &= ~P2P_DEV_PREFER_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;
+
+ if (force_freq)
+ dev->flags |= P2P_DEV_FORCE_FREQ;
+ else
+ dev->flags &= ~P2P_DEV_FORCE_FREQ;
+
+ 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(dev, 0, msg);
+
+ if (msg->listen_channel) {
+ int freq;
+ freq = p2p_channel_to_freq((char *) msg->listen_channel,
+ msg->listen_channel[3],
+ msg->listen_channel[4]);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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 {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update "
+ "peer " MACSTR " Listen channel: %u -> %u MHz",
+ MAC2STR(dev->info.p2p_device_addr),
+ dev->listen_freq, freq);
+ dev->listen_freq = freq;
+ }
+ }
+
+ if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+ dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Completed device entry based on data from "
+ "GO Negotiation Request");
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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, &params->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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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)
+ res.persistent_group = 1;
+
+ if (go) {
+ /* Setup AP mode for WPS provisioning */
+ res.freq = p2p_channel_to_freq(p2p->cfg->country,
+ 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_intersect(&p2p->channels, &peer->channels,
+ &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(peer->country, 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);
+ 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)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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_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:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported P2P Public Action frame type %d",
+ data[0]);
+ break;
+ }
+}
+
+
+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 */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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);
+ 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(dev->country,
+ msg.listen_channel[3],
+ msg.listen_channel[4]);
+ }
+
+ p2p_copy_wps_info(dev, 1, &msg);
+
+ p2p_parse_free(&msg);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_build_wps_ie(p2p, buf, DEV_PW_DEFAULT, 1);
+
+ /* P2P IE */
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_capability(buf, p2p->dev_capab, 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 void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *ie,
+ size_t ie_len)
+{
+ struct ieee802_11_elems elems;
+ struct wpabuf *buf;
+ struct ieee80211_mgmt *resp;
+ struct wpabuf *wps;
+ struct wpabuf *ies;
+
+ if (!p2p->in_listen || !p2p->drv_in_listen) {
+ /* not in Listen state - ignore Probe Request */
+ return;
+ }
+
+ if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
+ ParseFailed) {
+ /* Ignore invalid Probe Request frames */
+ return;
+ }
+
+ if (elems.p2p == NULL) {
+ /* not a P2P probe - ignore it */
+ return;
+ }
+
+ 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;
+ }
+
+ /* Check Requested Device Type match */
+ wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+ if (wps && !p2p_match_dev_type(p2p, wps)) {
+ wpabuf_free(wps);
+ /* No match with Requested Device Type */
+ return;
+ }
+ wpabuf_free(wps);
+
+ if (!p2p->cfg->send_probe_resp)
+ return; /* Response generated elsewhere */
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ buf = wpabuf_alloc(200 + wpabuf_len(ies));
+ if (buf == NULL) {
+ wpabuf_free(ies);
+ return;
+ }
+
+ 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);
+}
+
+
+int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *ie,
+ size_t ie_len)
+{
+ p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
+
+ p2p_reply_probe(p2p, addr, 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) {
+ /* Received a Probe Request from GO Negotiation peer */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Found GO Negotiation peer - try to start GO "
+ "negotiation from timeout");
+ eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
+ return 1;
+ }
+
+ 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 */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Found Invite peer - try to start Invite from "
+ "timeout");
+ eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+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;
+
+ if (!p2p_group)
+ return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie);
+
+ /*
+ * (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);
+ if (tmp == NULL)
+ return -1;
+
+ 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;
+}
+
+
+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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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 */
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending Group Formation - "
+ "ignore group formation failure notification");
+ return; /* No pending Group Formation */
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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);
+
+ p2p->min_disc_int = 1;
+ p2p->max_disc_int = 3;
+
+ 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);
+
+ return p2p;
+}
+
+
+void p2p_deinit(struct p2p_data *p2p)
+{
+ 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);
+ 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->groups);
+ wpabuf_free(p2p->sd_resp);
+ os_free(p2p->after_scan_tx);
+ p2p_remove_wps_vendor_extensions(p2p);
+ os_free(p2p);
+}
+
+
+void p2p_flush(struct p2p_data *p2p)
+{
+ struct p2p_device *dev, *prev;
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+ p2p->go_neg_peer = NULL;
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ 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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send "
+ "pending Provisioning 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)
+ return;
+ }
+ }
+
+ p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_sd_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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);
+}
+
+static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request TX callback: success=%d",
+ success);
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ if (!success) {
+ if (p2p->state != P2P_IDLE)
+ p2p_continue_find(p2p);
+ return;
+ }
+
+ /* 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,
+ int level, const u8 *ies, size_t ies_len)
+{
+ p2p_add_device(p2p, bssid, freq, level, ies, ies_len);
+
+ if (p2p->go_neg_peer && p2p->state == P2P_SEARCH &&
+ os_memcmp(p2p->go_neg_peer->info.p2p_device_addr, bssid, ETH_ALEN)
+ == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Found GO Negotiation peer - try to start GO "
+ "negotiation");
+ p2p_connect_send(p2p, p2p->go_neg_peer);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+void p2p_scan_res_handled(struct p2p_data *p2p)
+{
+ if (!p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)
+{
+ u8 *len = p2p_buf_add_ie_hdr(ies);
+ p2p_buf_add_capability(ies, p2p->dev_capab, 0);
+ 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);
+}
+
+
+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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Request TX callback: success=%d",
+ success);
+
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending GO Negotiation");
+ return;
+ }
+
+ if (success) {
+ dev->go_neg_req_sent++;
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ p2p_set_state(p2p, P2P_IDLE);
+ return;
+ }
+ }
+
+ if (!success &&
+ (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) &&
+ !is_zero_ether_addr(dev->member_in_go_dev)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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);
+ p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Response TX callback: success=%d",
+ success);
+ if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore TX callback event - GO Negotiation is "
+ "not running anymore");
+ return;
+ }
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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);
+ }
+}
+
+
+static void p2p_go_neg_conf_cb(struct p2p_data *p2p,
+ enum p2p_send_action_result result)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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:
+ 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);
+ 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;
+ }
+}
+
+
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+ unsigned int duration)
+{
+ if (freq == p2p->pending_client_disc_freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Client discoverability remain-awake completed");
+ p2p->pending_client_disc_freq = 0;
+ return;
+ }
+
+ if (freq != p2p->pending_listen_freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected listen callback for freq=%u "
+ "duration=%u (pending_listen_freq=%u)",
+ freq, duration, p2p->pending_listen_freq);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ 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);
+ p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+ p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_timeout_connect_listen(struct p2p_data *p2p)
+{
+ if (p2p->go_neg_peer) {
+ if (p2p->drv_in_listen) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is "
+ "still in Listen state; wait for it to "
+ "complete");
+ return;
+ }
+
+ if (p2p->go_neg_peer->connect_reqs >= 120) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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, 1, 0);
+}
+
+
+static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
+{
+ struct p2p_device *dev = p2p->go_neg_peer;
+
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown GO Neg peer - stop GO Neg wait");
+ return;
+ }
+
+ dev->wait_count++;
+ if (dev->wait_count >= 120) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Timeout on waiting peer to become ready for GO "
+ "Negotiation");
+ p2p_go_neg_failed(p2p, dev, -1);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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);
+}
+
+
+static void p2p_timeout_sd_during_find(struct p2p_data *p2p)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request timeout");
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_continue_find(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.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Inviting in "
+ "active GO role - wait on operating channel");
+ p2p_set_timeout(p2p, 0, 100000);
+ return;
+ }
+ p2p_listen_in_find(p2p);
+}
+
+
+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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request retry limit reached");
+ if (p2p->cfg->invitation_result)
+ p2p->cfg->invitation_result(
+ p2p->cfg->cb_ctx, -1, NULL);
+ }
+ p2p_set_state(p2p, P2P_IDLE);
+ }
+}
+
+
+static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Timeout (state=%s)",
+ p2p_state_txt(p2p->state));
+
+ p2p->in_listen = 0;
+
+ switch (p2p->state) {
+ case P2P_IDLE:
+ break;
+ case P2P_SEARCH:
+ 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:
+ if (p2p->ext_listen_only) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Extended Listen Timing - Listen State "
+ "completed");
+ p2p->ext_listen_only = 0;
+ p2p_set_state(p2p, P2P_IDLE);
+ }
+ break;
+ case P2P_WAIT_PEER_CONNECT:
+ p2p_timeout_wait_peer_connect(p2p);
+ break;
+ case P2P_WAIT_PEER_IDLE:
+ p2p_timeout_wait_peer_idle(p2p);
+ break;
+ case P2P_SD_DURING_FIND:
+ p2p_timeout_sd_during_find(p2p);
+ break;
+ case P2P_PROVISIONING:
+ break;
+ case P2P_PD_DURING_FIND:
+ p2p_timeout_prov_disc_during_find(p2p);
+ break;
+ case P2P_INVITE:
+ p2p_timeout_invite(p2p);
+ break;
+ case P2P_INVITE_LISTEN:
+ p2p_timeout_invite_listen(p2p);
+ break;
+ }
+}
+
+
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr)
+{
+ struct p2p_device *dev;
+
+ dev = p2p_get_device(p2p, peer_addr);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Local request to reject "
+ "connection attempts by peer " MACSTR, MAC2STR(peer_addr));
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+}
+
+
+static const char * p2p_wps_method_text(enum p2p_wps_method method)
+{
+ switch (method) {
+ case WPS_NOT_READY:
+ return "not-ready";
+ case WPS_PIN_LABEL:
+ return "Label";
+ 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 "??";
+}
+
+
+int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
+ char *buf, size_t buflen)
+{
+ struct p2p_device *dev;
+ int res;
+ char *pos, *end;
+ struct os_time now;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+ 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 -1;
+
+ pos = buf;
+ end = buf + buflen;
+
+ res = os_snprintf(pos, end - pos, MACSTR "\n",
+ MAC2STR(dev->info.p2p_device_addr));
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+
+ os_get_time(&now);
+ res = os_snprintf(pos, end - pos,
+ "age=%d\n"
+ "listen_freq=%d\n"
+ "level=%d\n"
+ "wps_method=%s\n"
+ "interface_addr=" MACSTR "\n"
+ "member_in_go_dev=" MACSTR "\n"
+ "member_in_go_iface=" MACSTR "\n"
+ "pri_dev_type=%s\n"
+ "device_name=%s\n"
+ "manufacturer=%s\n"
+ "model_name=%s\n"
+ "model_number=%s\n"
+ "serial_number=%s\n"
+ "config_methods=0x%x\n"
+ "dev_capab=0x%x\n"
+ "group_capab=0x%x\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,
+ dev->level,
+ p2p_wps_method_text(dev->wps_method),
+ MAC2STR(dev->interface_addr),
+ MAC2STR(dev->member_in_go_dev),
+ MAC2STR(dev->member_in_go_iface),
+ wps_dev_type_bin2str(dev->info.pri_dev_type,
+ devtype, sizeof(devtype)),
+ dev->info.device_name,
+ dev->info.manufacturer,
+ dev->info.model_name,
+ dev->info.model_number,
+ dev->info.serial_number,
+ dev->info.config_methods,
+ dev->info.dev_capab,
+ dev->info.group_capab,
+ 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;
+ }
+
+ return pos - buf;
+}
+
+
+void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled)
+{
+ if (enabled) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client "
+ "discoverability enabled");
+ p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore P2P Presence Request for unknown group "
+ MACSTR, MAC2STR(da));
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to parse P2P Presence Request");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ parsed = 1;
+
+ if (msg.noa == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - P2P Presence Response");
+
+ if (p2p_parse(data, len, &msg) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to parse P2P Presence Response");
+ return;
+ }
+
+ if (msg.status == NULL || msg.noa == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Status or NoA attribute in P2P Presence "
+ "Response");
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (*msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: P2P Presence Request was rejected: status %u",
+ *msg.status);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip Extended "
+ "Listen timeout in active state (%s)",
+ p2p_state_txt(p2p->state));
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Extended Listen timeout");
+ p2p->ext_listen_only = 1;
+ if (p2p_listen(p2p, p2p->ext_listen_period) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Disabling Extended Listen Timing");
+ p2p->ext_listen_period = 0;
+ p2p->ext_listen_interval = 0;
+ return 0;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P "
+ "Device operations enabled");
+ p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED;
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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(p2p->cfg->country, reg_class, channel) < 0)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)
+{
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: New SSID postfix", 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_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))
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Disable peer "
+ "filter");
+ else
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Enable peer "
+ "filter for " MACSTR, MAC2STR(p2p->peer_filter));
+}
+
+
+void p2p_set_cross_connect(struct p2p_data *p2p, int enabled)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Intra BSS distribution %s",
+ enabled ? "enabled" : "disabled");
+ p2p->cfg->p2p_intra_bss = enabled;
+}
+
+
+void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update channel list");
+ os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay Action "
+ "frame TX until p2p_scan completes");
+ if (p2p->after_scan_tx) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+}
+
+
+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;
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
new file mode 100644
index 0000000..954f562
--- /dev/null
+++ b/src/p2p/p2p.h
@@ -0,0 +1,1473 @@
+/*
+ * Wi-Fi Direct - P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#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_LABEL, 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;
+
+ /**
+ * ssid - SSID of the group
+ */
+ u8 ssid[32];
+
+ /**
+ * ssid_len - Length of SSID in octets
+ */
+ size_t ssid_len;
+
+ /**
+ * 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
+ */
+ 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_SPECIFIC,
+ 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];
+
+ /**
+ * 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];
+};
+
+/**
+ * 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;
+
+ /**
+ * 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-<random two octets> prefix.
+ */
+ u8 ssid_postfix[32 - 9];
+
+ /**
+ * ssid_postfix_len - Length of the ssid_postfix data
+ */
+ size_t ssid_postfix_len;
+
+ /**
+ * msg_ctx - Context to use with wpa_msg() calls
+ */
+ void *msg_ctx;
+
+ /**
+ * cb_ctx - Context to use with callback functions
+ */
+ void *cb_ctx;
+
+
+ /* 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
+ * 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_SPECIFIC
+ * request a scan of a single channel specified by freq.
+ * @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);
+
+ /**
+ * 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);
+
+ /**
+ * 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
+ *
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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)
+ * 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);
+
+ /**
+ * 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
+ *
+ * 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);
+};
+
+
+/* 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.
+ * 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);
+
+/**
+ * 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_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
+ * 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);
+
+/**
+ * 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
+ * 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);
+
+/**
+ * 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
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * 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
+ * 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);
+
+/**
+ * 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);
+
+
+/* Event notifications from lower layer driver operations */
+
+/**
+ * p2p_probe_req_rx - Report reception of a Probe Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Source MAC address
+ * @ie: Information elements from the Probe Request frame body
+ * @ie_len: Length of ie buffer in octets
+ * Returns: 0 to indicate the frame was not processed or 1 if it was
+ */
+int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, 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
+ * @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,
+ 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
+ */
+ 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;
+
+ /**
+ * 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_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_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
+ */
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies);
+
+/**
+ * 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 in text format
+ * @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
+ * @buf: Buffer for returning text
+ * @buflen: Maximum buffer length
+ * Returns: Number of octets written to the buffer or -1 on failure
+ */
+int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
+ char *buf, size_t buflen);
+
+/**
+ * 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);
+
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
+ const u8 *ies, size_t ies_len);
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *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);
+
+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_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);
+
+#endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
new file mode 100644
index 0000000..c34db91
--- /dev/null
+++ b/src/p2p/p2p_build.c
@@ -0,0 +1,431 @@
+/*
+ * P2P - IE builder
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "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_printf(MSG_DEBUG, "P2P: * Channel List");
+}
+
+
+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_LABEL)
+ methods |= WPS_CONFIG_LABEL;
+ else if (peer->wps_method == WPS_PIN_DISPLAY ||
+ peer->wps_method == WPS_PIN_KEYPAD)
+ methods |= 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));
+}
+
+
+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 void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
+ const char *val)
+{
+ size_t len;
+
+ wpabuf_put_be16(buf, attr);
+ len = val ? os_strlen(val) : 0;
+#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.
+ */
+ wpabuf_put_be16(buf, 1);
+ wpabuf_put_u8(buf, ' ');
+ return;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(buf, len);
+ if (val)
+ wpabuf_put_data(buf, val, len);
+}
+
+
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
+ int all_attr)
+{
+ u8 *len;
+ int i;
+
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(buf, 1);
+ wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
+
+ wps_build_version(buf);
+
+ if (all_attr) {
+ wpabuf_put_be16(buf, ATTR_WPS_STATE);
+ wpabuf_put_be16(buf, 1);
+ wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
+ }
+
+ /* 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) {
+ wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
+ wpabuf_put_be16(buf, 1);
+ wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
+
+ wps_build_uuid_e(buf, p2p->cfg->uuid);
+ p2p_add_wps_string(buf, ATTR_MANUFACTURER,
+ p2p->cfg->manufacturer);
+ p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name);
+ p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
+ p2p->cfg->model_number);
+ p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
+ p2p->cfg->serial_number);
+
+ 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);
+
+ p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name);
+
+ wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
+ wpabuf_put_be16(buf, 2);
+ wpabuf_put_be16(buf, p2p->cfg->config_methods);
+ }
+
+ wps_build_wfa_ext(buf, 0, NULL, 0);
+
+ if (all_attr && p2p->cfg->num_sec_dev_types) {
+ 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);
+}
diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c
new file mode 100644
index 0000000..47cc0fd
--- /dev/null
+++ b/src/p2p/p2p_dev_disc.c
@@ -0,0 +1,365 @@
+/*
+ * Wi-Fi Direct - P2P Device Discoverability procedure
+ * Copyright (c) 2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "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)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+ }
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Client discoverability request failed");
+ p2p_set_timeout(p2p, 0, 500000);
+ }
+
+}
+
+
+void p2p_go_disc_req_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to start listen mode for client "
+ "discoverability");
+ }
+ wpabuf_free(ies);
+}
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
new file mode 100644
index 0000000..1c96486
--- /dev/null
+++ b/src/p2p/p2p_go_neg.c
@@ -0,0 +1,1127 @@
+/*
+ * Wi-Fi Direct - P2P Group Owner Negotiation
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_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);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_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);
+}
+
+
+static u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
+{
+ switch (wps_method) {
+ case WPS_PIN_LABEL:
+ return DEV_PW_DEFAULT;
+ 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_LABEL:
+ return "Label";
+ 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;
+
+ buf = wpabuf_alloc(1000);
+ 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_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 (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, group_capab);
+ p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) |
+ p2p->next_tie_breaker);
+ p2p->next_tie_breaker = !p2p->next_tie_breaker;
+ p2p_buf_add_config_timeout(buf, 100, 20);
+ 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 */
+ p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0);
+
+ return buf;
+}
+
+
+int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
+{
+ struct wpabuf *req;
+ int freq;
+
+ freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+ if (freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ /* Use P2P find to recover and retry */
+ p2p_set_timeout(p2p, 0, 0);
+ }
+
+ 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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Building GO Negotiation Response");
+ buf = wpabuf_alloc(1000);
+ 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 (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, group_capab);
+ p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
+ p2p_buf_add_config_timeout(buf, 100, 20);
+ if (peer && peer->go_state == REMOTE_GO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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 */
+ p2p_build_wps_ie(p2p, buf,
+ p2p_wps_method_pw_id(peer ? peer->wps_method :
+ WPS_NOT_READY), 0);
+
+ return buf;
+}
+
+
+static 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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating "
+ "channel (reg_class %u channel %u) not acceptable to the "
+ "peer", p2p->op_reg_class, p2p->op_channel);
+
+ /* First, try to pick the best channel from another band */
+ freq = p2p_channel_to_freq(p2p->cfg->country, p2p->op_reg_class,
+ p2p->op_channel);
+ if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 &&
+ p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5,
+ &op_reg_class, &op_channel) == 0 &&
+ p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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_freq_to_channel(p2p->cfg->country, p2p->best_freq_24,
+ &op_reg_class, &op_channel) == 0 &&
+ p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+ }
+
+ /*
+ * Fall back to whatever is included in the channel intersection since
+ * no better options seems to be available.
+ */
+ cl = &intersection->reg_class[0];
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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];
+}
+
+
+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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GO Negotiation Request from " MACSTR
+ "(freq=%d)", MAC2STR(sa), rx_freq);
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!msg.capability) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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 {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory GO Intent attribute missing from GO "
+ "Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.config_timeout) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Configuration Timeout attribute "
+ "missing from GO Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.listen_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen Channel attribute received");
+ goto fail;
+ }
+ if (!msg.operating_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Operating Channel attribute received");
+ goto fail;
+ }
+ if (!msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Channel List attribute received");
+ goto fail;
+ }
+ if (!msg.intended_addr) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Intended P2P Interface Address attribute "
+ "received");
+ goto fail;
+ }
+ if (!msg.p2p_device_info) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No P2P Device Info attribute received");
+ goto fail;
+ }
+
+ if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: User has rejected this peer");
+ status = P2P_SC_FAIL_REJECTED_BY_USER;
+ } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Not ready for GO negotiation with " MACSTR,
+ MAC2STR(sa));
+ status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ if (dev)
+ dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
+ 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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Already in Group Formation with another peer");
+ status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ } else {
+ int go;
+
+ if (!p2p->go_neg_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting "
+ "GO Negotiation with previously authorized "
+ "peer");
+ if (!(dev->flags & P2P_DEV_FORCE_FREQ)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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 {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Use previously configured "
+ "forced channel settings");
+ }
+ }
+
+ dev->flags &= ~P2P_DEV_NOT_YET_READY;
+
+ if (!msg.go_intent) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No GO Intent attribute received");
+ goto fail;
+ }
+ if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ switch (msg.dev_password_id) {
+ case DEV_PW_DEFAULT:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: PIN from peer Label");
+ if (dev->wps_method != WPS_PIN_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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_REGISTRAR_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: PIN from peer Display");
+ if (dev->wps_method != WPS_PIN_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer entered PIN on Keypad");
+ if (dev->wps_method != WPS_PIN_LABEL &&
+ dev->wps_method != WPS_PIN_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer using pushbutton");
+ if (dev->wps_method != WPS_PBC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported Device Password ID %d",
+ msg.dev_password_id);
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+
+ if (go) {
+ struct p2p_channels intersection;
+ size_t i;
+ p2p_channels_intersect(&p2p->channels, &dev->channels,
+ &intersection);
+ if (intersection.reg_classes == 0 ||
+ intersection.reg_class[0].channels == 0) {
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ goto fail;
+ }
+ for (i = 0; i < intersection.reg_classes; i++) {
+ struct p2p_reg_class *c;
+ c = &intersection.reg_class[i];
+ wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
+ c->reg_class);
+ wpa_hexdump(MSG_DEBUG, "P2P: channels",
+ c->channel, c->channels);
+ }
+ if (!p2p_channels_includes(&intersection,
+ p2p->op_reg_class,
+ p2p->op_channel))
+ p2p_reselect_channel(p2p, &intersection);
+
+ p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+ }
+
+ dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+ dev->oper_freq = p2p_channel_to_freq((const char *)
+ msg.operating_channel,
+ msg.operating_channel[3],
+ msg.operating_channel[4]);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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];
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending GO Negotiation Response");
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+ } 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), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Building GO Negotiation Confirm");
+ buf = wpabuf_alloc(1000);
+ 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 (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, 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);
+
+ 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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected Dialog Token %u (expected %u)",
+ msg.dialog_token, dev->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Status attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ if (*msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation rejected: status %d",
+ *msg.status);
+ dev->go_neg_req_sent = 0;
+ if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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 {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Intended P2P Interface Address attribute "
+ "received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ if (!msg.go_intent) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No GO Intent attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory P2P Group ID attribute missing from "
+ "GO Negotiation Response");
+ p2p->ssid_len = 0;
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.config_timeout) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Operating Channel attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ if (!msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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((const char *)
+ msg.operating_channel,
+ msg.operating_channel[3],
+ msg.operating_channel[4]);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating "
+ "channel preference: %d MHz", dev->oper_freq);
+ } else
+ dev->oper_freq = 0;
+
+ switch (msg.dev_password_id) {
+ case DEV_PW_DEFAULT:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: PIN from peer Label");
+ if (dev->wps_method != WPS_PIN_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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_REGISTRAR_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: PIN from peer Display");
+ if (dev->wps_method != WPS_PIN_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer entered PIN on Keypad");
+ if (dev->wps_method != WPS_PIN_LABEL &&
+ dev->wps_method != WPS_PIN_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer using pushbutton");
+ if (dev->wps_method != WPS_PBC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported Device Password ID %d",
+ msg.dev_password_id);
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+
+ if (go) {
+ struct p2p_channels intersection;
+ size_t i;
+ p2p_channels_intersect(&p2p->channels, &dev->channels,
+ &intersection);
+ if (intersection.reg_classes == 0 ||
+ intersection.reg_class[0].channels == 0) {
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ goto fail;
+ }
+ for (i = 0; i < intersection.reg_classes; i++) {
+ struct p2p_reg_class *c;
+ c = &intersection.reg_class[i];
+ wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
+ c->reg_class);
+ wpa_hexdump(MSG_DEBUG, "P2P: channels",
+ c->channel, c->channels);
+ }
+ if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+ p2p->op_channel))
+ p2p_reselect_channel(p2p, &intersection);
+
+ p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+ }
+
+ p2p_set_state(p2p, P2P_GO_NEG);
+ p2p_clear_timeout(p2p);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending GO Negotiation Confirm");
+ if (status == P2P_SC_SUCCESS) {
+ p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
+ dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+ } else
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = dev->listen_freq;
+ if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
+ wpabuf_head(conf), wpabuf_len(conf), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ p2p_go_neg_failed(p2p, dev, -1);
+ }
+ wpabuf_free(conf);
+}
+
+
+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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Not ready for GO negotiation with " MACSTR,
+ MAC2STR(sa));
+ return;
+ }
+
+ if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Was not expecting GO Negotiation Confirm - "
+ "ignore");
+ return;
+ }
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+
+ if (msg.dialog_token != dev->dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected Dialog Token %u (expected %u)",
+ msg.dialog_token, dev->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Status attribute received");
+ p2p_parse_free(&msg);
+ return;
+ }
+ if (*msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation rejected: status %d",
+ *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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory P2P Group ID attribute missing from "
+ "GO Negotiation Confirmation");
+ p2p->ssid_len = 0;
+#ifdef CONFIG_P2P_STRICT
+ p2p_parse_free(&msg);
+ return;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.operating_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Operating Channel attribute missing "
+ "from GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+ p2p_parse_free(&msg);
+ return;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected GO Neg state - do not know which end "
+ "becomes GO");
+ return;
+ }
+
+ p2p_go_complete(p2p, dev);
+}
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
new file mode 100644
index 0000000..14a475d
--- /dev/null
+++ b/src/p2p/p2p_group.c
@@ -0,0 +1,673 @@
+/*
+ * Wi-Fi Direct - P2P group operations
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "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 *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;
+};
+
+
+static void p2p_group_update_ies(struct p2p_group *group);
+
+
+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(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->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);
+ 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 = 0, group_capab = 0;
+
+ /* P2P Capability */
+ dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+ dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
+ group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
+ if (group->cfg->persistent_group)
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ 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_build_beacon_ie(struct p2p_group *group)
+{
+ struct wpabuf *ie;
+ u8 *len;
+
+ ie = wpabuf_alloc(257);
+ if (ie == NULL)
+ return NULL;
+
+ 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;
+}
+
+
+static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
+{
+ u8 *group_info;
+ struct wpabuf *ie;
+ struct p2p_group_member *m;
+ u8 *len;
+
+ ie = wpabuf_alloc(257);
+ if (ie == NULL)
+ return NULL;
+
+ len = p2p_buf_add_ie_hdr(ie);
+
+ p2p_group_add_common_ies(group, ie);
+ p2p_group_add_noa(ie, group->noa);
+
+ /* P2P Device Info */
+ p2p_buf_add_device_info(ie, group->p2p, NULL);
+
+ /* P2P Group Info */
+ group_info = wpabuf_put(ie, 0);
+ wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO);
+ wpabuf_put_le16(ie, 0); /* Length to be filled */
+ for (m = group->members; m; m = m->next)
+ p2p_client_info(ie, m);
+ WPA_PUT_LE16(group_info + 1,
+ (u8 *) wpabuf_put(ie, 0) - group_info - 3);
+
+ p2p_buf_update_ie_hdr(ie, len);
+ return ie;
+}
+
+
+static void p2p_group_update_ies(struct p2p_group *group)
+{
+ struct wpabuf *beacon_ie;
+ struct wpabuf *probe_resp_ie;
+
+ 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;
+}
+
+
+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;
+
+ 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);
+ }
+
+ m->next = group->members;
+ group->members = m;
+ group->num_members++;
+ wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR
+ " to group (p2p=%d client_info=%d); num_members=%u/%u",
+ MAC2STR(addr), m->p2p_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;
+
+ /*
+ * (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);
+ if (resp == NULL)
+ return NULL;
+ 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)
+{
+ struct p2p_group_member *m, *prev;
+
+ if (group == NULL)
+ return;
+
+ m = group->members;
+ prev = NULL;
+ while (m) {
+ if (os_memcmp(m->addr, addr, ETH_ALEN) == 0)
+ break;
+ prev = m;
+ m = m->next;
+ }
+
+ if (m) {
+ if (prev)
+ prev->next = m->next;
+ else
+ group->members = m->next;
+ p2p_group_free_member(m);
+ group->num_members--;
+ wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+}
+
+
+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->size = 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;
+}
+
+
+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) {
+ wpa_printf(MSG_DEBUG, "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)) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested client does not support "
+ "client discoverability");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "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)
+ {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_printf(MSG_DEBUG, "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)
+ wpa_printf(MSG_DEBUG, "P2P: Failed to fetch current NoA");
+ else if (curr_noa_len == 0)
+ wpa_printf(MSG_DEBUG, "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)
+ 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;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
new file mode 100644
index 0000000..68b1194
--- /dev/null
+++ b/src/p2p/p2p_i.h
@@ -0,0 +1,638 @@
+/*
+ * P2P - Internal definitions for P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef P2P_I_H
+#define P2P_I_H
+
+#include "utils/list.h"
+#include "p2p.h"
+
+/* TODO: add removal of expired P2P device entries */
+
+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;
+ int level;
+ 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 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 provisioning discovery methods
+ */
+ u16 req_config_methods;
+
+#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)
+ 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;
+ struct wpabuf *tlvs;
+};
+
+struct p2p_pending_action_tx {
+ unsigned int freq;
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ size_t len;
+ unsigned int wait_time;
+ /* Followed by len octets of the frame */
+};
+
+/**
+ * struct p2p_data - P2P module data (internal to P2P module)
+ */
+struct p2p_data {
+ /**
+ * cfg - P2P module configuration
+ *
+ * This is included in the same memory allocation with the
+ * struct p2p_data and as such, must not be freed separately.
+ */
+ struct p2p_config *cfg;
+
+ /**
+ * state - The current P2P state
+ */
+ enum p2p_state {
+ /**
+ * P2P_IDLE - Idle
+ */
+ P2P_IDLE,
+
+ /**
+ * P2P_SEARCH - Search (Device Discovery)
+ */
+ P2P_SEARCH,
+
+ /**
+ * P2P_CONNECT - Trying to start GO Negotiation
+ */
+ P2P_CONNECT,
+
+ /**
+ * P2P_CONNECT_LISTEN - Listen during GO Negotiation start
+ */
+ P2P_CONNECT_LISTEN,
+
+ /**
+ * P2P_GO_NEG - In GO Negotiation
+ */
+ P2P_GO_NEG,
+
+ /**
+ * P2P_LISTEN_ONLY - Listen only
+ */
+ P2P_LISTEN_ONLY,
+
+ /**
+ * P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg
+ */
+ P2P_WAIT_PEER_CONNECT,
+
+ /**
+ * P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg
+ */
+ P2P_WAIT_PEER_IDLE,
+
+ /**
+ * P2P_SD_DURING_FIND - Service Discovery during find
+ */
+ P2P_SD_DURING_FIND,
+
+ /**
+ * P2P_PROVISIONING - Provisioning (during group formation)
+ */
+ P2P_PROVISIONING,
+
+ /**
+ * P2P_PD_DURING_FIND - Provision Discovery during find
+ */
+ P2P_PD_DURING_FIND,
+
+ /**
+ * P2P_INVITE - Trying to start Invite
+ */
+ P2P_INVITE,
+
+ /**
+ * P2P_INVITE_LISTEN - Listen during Invite
+ */
+ P2P_INVITE_LISTEN,
+ } state;
+
+ /**
+ * min_disc_int - minDiscoverableInterval
+ */
+ int min_disc_int;
+
+ /**
+ * max_disc_int - maxDiscoverableInterval
+ */
+ int max_disc_int;
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+
+ 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;
+ 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;
+
+ /* Requested device types for find/search */
+ unsigned int num_req_dev_types;
+ u8 *req_dev_types;
+
+ 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;
+
+ /**
+ * wps_vendor_ext - WPS Vendor Extensions to add
+ */
+ struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+};
+
+/**
+ * struct p2p_message - Parsed P2P message (or P2P IE)
+ */
+struct p2p_message {
+ struct wpabuf *p2p_attributes;
+ struct wpabuf *wps_attributes;
+
+ 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(const char *country, int reg_class, int channel);
+int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class,
+ u8 *channel);
+void p2p_channels_intersect(const struct p2p_channels *a,
+ const struct p2p_channels *b,
+ struct p2p_channels *res);
+int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
+ u8 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);
+
+
+void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
+void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
+ u8 dialog_token);
+u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf);
+void p2p_buf_add_status(struct wpabuf *buf, u8 status);
+void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
+ struct p2p_device *peer);
+void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr);
+void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len);
+void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab);
+void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent);
+void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
+ u8 reg_class, u8 channel);
+void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
+ u8 reg_class, u8 channel);
+void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
+ struct p2p_channels *chan);
+void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
+ u8 client_timeout);
+void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr);
+void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid);
+void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
+ const u8 *ssid, size_t ssid_len);
+void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags);
+void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
+ struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2);
+void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
+ u16 interval);
+void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 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);
+
+/* 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);
+
+/* 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);
+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);
+
+#endif /* P2P_I_H */
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
new file mode 100644
index 0000000..bb2767d
--- /dev/null
+++ b/src/p2p/p2p_invitation.c
@@ -0,0 +1,489 @@
+/*
+ * Wi-Fi Direct - P2P Invitation procedure
+ * Copyright (c) 2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "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;
+
+ buf = wpabuf_alloc(1000);
+ 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, 100, 20);
+ p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ?
+ P2P_INVITATION_FLAGS_TYPE : 0);
+ 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);
+
+ 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;
+
+ buf = wpabuf_alloc(1000);
+ 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);
+
+ 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));
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request from unknown peer "
+ MACSTR, MAC2STR(sa));
+
+ if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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.
+ */
+ wpa_printf(MSG_DEBUG, "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ 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);
+ }
+
+ if (op_freq) {
+ if (p2p_freq_to_channel(p2p->cfg->country, op_freq,
+ &reg_class, &channel) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown forced freq %d MHz from "
+ "invitation_process()", op_freq);
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+ &intersection);
+ if (!p2p_channels_includes(&intersection, reg_class, channel))
+ {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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 {
+ op_freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->op_reg_class,
+ p2p->cfg->op_channel);
+ if (op_freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown operational channel "
+ "(country=%c%c reg_class=%u channel=%u)",
+ p2p->cfg->country[0], p2p->cfg->country[1],
+ p2p->cfg->op_reg_class, p2p->cfg->op_channel);
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+ &intersection);
+ if (status == P2P_SC_SUCCESS) {
+ reg_class = p2p->cfg->op_reg_class;
+ channel = p2p->cfg->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->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Invitation Response from " MACSTR,
+ MAC2STR(sa));
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore Invitation Response from unknown peer "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ if (dev != p2p->invite_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore unexpected Invitation Response from peer "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Status attribute missing in "
+ "Invitation Response from " MACSTR, MAC2STR(sa));
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (p2p->cfg->invitation_result)
+ p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
+ msg.group_bssid);
+
+ 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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request TX callback: success=%d", success);
+
+ if (p2p->invite_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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, 100000);
+}
+
+
+void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Response TX callback: success=%d", success);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+ if (success && 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)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Request to invite peer " MACSTR " role=%d persistent=%d "
+ "force_freq=%u",
+ MAC2STR(peer), role, persistent_group, force_freq);
+ if (bssid)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation for BSSID " MACSTR, MAC2STR(bssid));
+ if (go_dev_addr) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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, "P2P: Invitation for SSID",
+ ssid, ssid_len);
+
+ dev = p2p_get_device(p2p, peer);
+ if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot invite unknown P2P Device " MACSTR,
+ MAC2STR(peer));
+ return -1;
+ }
+
+ if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+ if (!(dev->info.dev_capab &
+ P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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 (force_freq) {
+ if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
+ &p2p->op_reg_class, &p2p->op_channel) <
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported frequency %u MHz",
+ force_freq);
+ return -1;
+ }
+ 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 {
+ 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));
+ }
+
+ 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/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
new file mode 100644
index 0000000..5c5445a
--- /dev/null
+++ b/src/p2p/p2p_parse.c
@@ -0,0 +1,718 @@
+/*
+ * P2P - IE parser
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "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;
+ }
+
+ 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;
+}
+
+
+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/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
new file mode 100644
index 0000000..e064216
--- /dev/null
+++ b/src/p2p/p2p_pd.c
@@ -0,0 +1,347 @@
+/*
+ * Wi-Fi Direct - P2P provision discovery
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+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;
+
+ buf = wpabuf_alloc(1000);
+ 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, 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);
+
+ return buf;
+}
+
+
+static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
+ u8 dialog_token,
+ u16 config_methods)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(100);
+ 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);
+
+ 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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request from "
+ "unknown peer " MACSTR, MAC2STR(sa));
+ if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request add device "
+ "failed " MACSTR, MAC2STR(sa));
+ }
+ }
+
+ if (!(msg.wps_config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
+ WPS_CONFIG_PUSHBUTTON))) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported "
+ "Config Methods in Provision Discovery Request");
+ goto out;
+ }
+
+ if (dev)
+ dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+ P2P_DEV_PD_PEER_KEYPAD);
+ if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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);
+ if (resp == NULL) {
+ p2p_parse_free(&msg);
+ return;
+ }
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending Provision Discovery Response");
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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);
+
+ }
+ 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;
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Provisioning 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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore Provisioning Discovery Response from "
+ MACSTR " with no pending request", MAC2STR(sa));
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (dev->dialog_token != msg.dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore Provisioning Discovery Response with "
+ "unexpected Dialog Token %u (expected %u)",
+ msg.dialog_token, dev->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (msg.wps_config_methods != dev->req_config_methods) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected "
+ "our Provisioning Discovery Request");
+ p2p_parse_free(&msg);
+ goto out;
+ }
+
+ report_config_methods = dev->req_config_methods;
+ dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+ P2P_DEV_PD_PEER_KEYPAD);
+ if (dev->req_config_methods & WPS_CONFIG_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+ " accepted to write our PIN using keypad",
+ MAC2STR(sa));
+ dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+ }
+ p2p_parse_free(&msg);
+
+out:
+ dev->req_config_methods = 0;
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ if (p2p->cfg->prov_disc_resp)
+ p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
+ report_config_methods);
+}
+
+
+int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
+ int join)
+{
+ struct wpabuf *req;
+ int freq;
+
+ freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+ if (freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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 */
+ }
+
+ dev->dialog_token++;
+ if (dev->dialog_token == 0)
+ dev->dialog_token = 1;
+ req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
+ dev->req_config_methods,
+ join ? dev : NULL);
+ if (req == NULL)
+ return -1;
+
+ 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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ wpabuf_free(req);
+ return -1;
+ }
+
+ wpabuf_free(req);
+ return 0;
+}
+
+
+int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+ u16 config_methods, int join)
+{
+ 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)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision "
+ "Discovery Request destination " MACSTR
+ " not yet known", MAC2STR(peer_addr));
+ return -1;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery "
+ "Request with " MACSTR " (config methods 0x%x)",
+ MAC2STR(peer_addr), config_methods);
+ if (config_methods == 0)
+ return -1;
+
+ 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->go_neg_peer ||
+ (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
+ p2p->state != P2P_LISTEN_ONLY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other "
+ "operations; postpone Provision Discovery Request "
+ "with " MACSTR " (config methods 0x%x)",
+ MAC2STR(peer_addr), config_methods);
+ return 0;
+ }
+
+ return p2p_send_prov_disc_req(p2p, dev, join);
+}
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
new file mode 100644
index 0000000..926fc03
--- /dev/null
+++ b/src/p2p/p2p_sd.c
@@ -0,0 +1,951 @@
+/*
+ * Wi-Fi Direct - P2P service discovery
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
+ struct p2p_device *dev)
+{
+ struct p2p_sd_query *q;
+
+ if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY))
+ return 0; /* peer does not support SD */
+
+ for (q = p2p->sd_queries; q; q = q->next) {
+ 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, *len_pos2;
+
+ buf = wpabuf_alloc(1000 + wpabuf_len(tlvs));
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_REQ);
+ wpabuf_put_u8(buf, 0); /* Dialog Token */
+
+ /* Advertisement Protocol IE */
+ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(buf, 2); /* Length */
+ wpabuf_put_u8(buf, 0); /* QueryRespLenLimit | PAME-BI */
+ wpabuf_put_u8(buf, NATIVE_QUERY_PROTOCOL); /* Advertisement Protocol */
+
+ /* Query Request */
+ len_pos = wpabuf_put(buf, 2); /* Length (to be filled) */
+
+ /* NQP Query Request Frame */
+ wpabuf_put_le16(buf, NQP_VENDOR_SPECIFIC); /* Info ID */
+ len_pos2 = wpabuf_put(buf, 2); /* Length (to be filled) */
+ 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);
+
+ WPA_PUT_LE16(len_pos2, (u8 *) wpabuf_put(buf, 0) - len_pos2 - 2);
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
+
+ return buf;
+}
+
+
+static struct wpabuf * p2p_build_gas_comeback_req(u8 dialog_token)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(3);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, WLAN_PA_GAS_COMEBACK_REQ);
+ wpabuf_put_u8(buf, dialog_token);
+
+ 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 = p2p_build_gas_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)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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, *len_pos2;
+
+ buf = wpabuf_alloc(1000 + (tlvs ? wpabuf_len(tlvs) : 0));
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP);
+ wpabuf_put_u8(buf, dialog_token);
+ wpabuf_put_le16(buf, status_code);
+ wpabuf_put_le16(buf, comeback_delay);
+
+ /* Advertisement Protocol IE */
+ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(buf, 2); /* Length */
+ wpabuf_put_u8(buf, 0x7f); /* QueryRespLenLimit | PAME-BI */
+ wpabuf_put_u8(buf, NATIVE_QUERY_PROTOCOL); /* Advertisement Protocol */
+
+ /* Query Response */
+ len_pos = wpabuf_put(buf, 2); /* Length (to be filled) */
+
+ if (tlvs) {
+ /* NQP Query Response Frame */
+ wpabuf_put_le16(buf, NQP_VENDOR_SPECIFIC); /* Info ID */
+ len_pos2 = wpabuf_put(buf, 2); /* Length (to be filled) */
+ 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);
+
+ WPA_PUT_LE16(len_pos2,
+ (u8 *) wpabuf_put(buf, 0) - len_pos2 - 2);
+ }
+
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
+
+ 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;
+ u8 *len_pos;
+
+ buf = wpabuf_alloc(1000 + len);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, WLAN_PA_GAS_COMEBACK_RESP);
+ wpabuf_put_u8(buf, dialog_token);
+ wpabuf_put_le16(buf, status_code);
+ wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0));
+ wpabuf_put_le16(buf, 0); /* Comeback Delay */
+
+ /* Advertisement Protocol IE */
+ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(buf, 2); /* Length */
+ wpabuf_put_u8(buf, 0x7f); /* QueryRespLenLimit | PAME-BI */
+ wpabuf_put_u8(buf, NATIVE_QUERY_PROTOCOL); /* Advertisement Protocol */
+
+ /* Query Response */
+ len_pos = wpabuf_put(buf, 2); /* Length (to be filled) */
+
+ if (frag_id == 0) {
+ /* NQP Query Response Frame */
+ wpabuf_put_le16(buf, NQP_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);
+
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
+
+ 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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0)
+ return;
+
+ if (len < 1 + 2)
+ return;
+
+ dialog_token = *pos++;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GAS Initial Request from " MACSTR " (dialog token %u, "
+ "freq %d)",
+ MAC2STR(sa), dialog_token, rx_freq);
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected IE in GAS Initial Request: %u", *pos);
+ return;
+ }
+ pos++;
+
+ slen = *pos++;
+ next = pos + slen;
+ if (next > end || slen < 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid IE in GAS Initial Request");
+ return;
+ }
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (*pos != NATIVE_QUERY_PROTOCOL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+
+ /* NQP Query Request */
+ if (pos + 4 > end)
+ return;
+ if (WPA_GET_LE16(pos) != NQP_VENDOR_SPECIFIC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported NQP Info ID %u", WPA_GET_LE16(pos));
+ return;
+ }
+ pos += 2;
+
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 3 + 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid NQP Query Request length");
+ return;
+ }
+
+ if (WPA_GET_BE24(pos) != OUI_WFA) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported NQP OUI %06x", WPA_GET_BE24(pos));
+ return;
+ }
+ pos += 3;
+
+ if (*pos != P2P_OUI_TYPE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported NQP vendor type %u", *pos);
+ return;
+ }
+ pos++;
+
+ if (pos + 2 > end)
+ return;
+ update_indic = WPA_GET_LE16(pos);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop "
+ "previous SD response");
+ wpabuf_free(p2p->sd_resp);
+ }
+ os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN);
+ p2p->sd_resp_dialog_token = dialog_token;
+ p2p->sd_resp = wpabuf_dup(resp_tlvs);
+ 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 {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore unexpected GAS Initial Response from "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_clear_timeout(p2p);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GAS Initial Response from " MACSTR " (len=%d)",
+ MAC2STR(sa), (int) len);
+
+ if (len < 5 + 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: dialog_token=%u status_code=%u comeback_delay=%u",
+ dialog_token, status_code, comeback_delay);
+ if (status_code) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Discovery failed: status code %u",
+ status_code);
+ return;
+ }
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected IE in GAS Initial Response: %u",
+ *pos);
+ return;
+ }
+ pos++;
+
+ slen = *pos++;
+ next = pos + slen;
+ if (next > end || slen < 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid IE in GAS Initial Response");
+ return;
+ }
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (*pos != NATIVE_QUERY_PROTOCOL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported GAS advertisement protocol id %u",
+ *pos);
+ return;
+ }
+
+ pos = next;
+ /* Query Response */
+ if (pos + 2 > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query "
+ "Response");
+ return;
+ }
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d",
+ slen);
+ if (pos + slen > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query "
+ "Response data");
+ return;
+ }
+ end = pos + slen;
+
+ if (comeback_delay) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Fragmented "
+ "response - request fragments");
+ if (p2p->sd_rx_resp) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+ }
+
+ /* NQP Query Response */
+ if (pos + 4 > end)
+ return;
+ if (WPA_GET_LE16(pos) != NQP_VENDOR_SPECIFIC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported NQP Info ID %u", WPA_GET_LE16(pos));
+ return;
+ }
+ pos += 2;
+
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 3 + 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid NQP Query Response length");
+ return;
+ }
+
+ if (WPA_GET_BE24(pos) != OUI_WFA) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported NQP OUI %06x", WPA_GET_BE24(pos));
+ return;
+ }
+ pos += 3;
+
+ if (*pos != P2P_OUI_TYPE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported NQP vendor type %u", *pos);
+ return;
+ }
+ pos++;
+
+ if (pos + 2 > end)
+ return;
+ update_indic = WPA_GET_LE16(pos);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dialog Token: %u",
+ dialog_token);
+ if (dialog_token != p2p->sd_resp_dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+ "response fragment for dialog token %u", dialog_token);
+ return;
+ }
+
+ if (p2p->sd_resp == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+ "response fragment available");
+ return;
+ }
+ if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: %d more bytes "
+ "remain to be sent",
+ (int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos));
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore unexpected GAS Comeback Response from "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_clear_timeout(p2p);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GAS Comeback Response from " MACSTR " (len=%d)",
+ MAC2STR(sa), (int) len);
+
+ if (len < 6 + 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Discovery failed: status code %u",
+ status_code);
+ return;
+ }
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected IE in GAS Comeback Response: %u",
+ *pos);
+ return;
+ }
+ pos++;
+
+ slen = *pos++;
+ next = pos + slen;
+ if (next > end || slen < 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid IE in GAS Comeback Response");
+ return;
+ }
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (*pos != NATIVE_QUERY_PROTOCOL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported GAS advertisement protocol id %u",
+ *pos);
+ return;
+ }
+
+ pos = next;
+ /* Query Response */
+ if (pos + 2 > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query "
+ "Response");
+ return;
+ }
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d",
+ slen);
+ if (pos + slen > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query "
+ "Response data");
+ return;
+ }
+ if (slen == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No Query Response "
+ "data");
+ return;
+ }
+ end = pos + slen;
+
+ if (p2p->sd_rx_resp) {
+ /*
+ * NQP header is only included in the first fragment; rest of
+ * the fragments start with continue TLVs.
+ */
+ goto skip_nqp_header;
+ }
+
+ /* NQP Query Response */
+ if (pos + 4 > end)
+ return;
+ if (WPA_GET_LE16(pos) != NQP_VENDOR_SPECIFIC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported NQP Info ID %u", WPA_GET_LE16(pos));
+ return;
+ }
+ pos += 2;
+
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: NQP Query Response "
+ "length: %u", slen);
+ if (slen < 3 + 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid NQP Query Response length");
+ return;
+ }
+ if (pos + 4 > end)
+ return;
+
+ if (WPA_GET_BE24(pos) != OUI_WFA) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported NQP OUI %06x", WPA_GET_BE24(pos));
+ return;
+ }
+ pos += 3;
+
+ if (*pos != P2P_OUI_TYPE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported NQP vendor type %u", *pos);
+ return;
+ }
+ pos++;
+
+ if (pos + 2 > end)
+ return;
+ p2p->sd_rx_update_indic = WPA_GET_LE16(pos);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Current SD reassembly "
+ "buffer length: %u",
+ (unsigned int) wpabuf_len(p2p->sd_rx_resp));
+
+ if (more_frags) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "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;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q);
+
+ return q;
+}
+
+
+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)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cancel pending SD query %p", req);
+ p2p_free_sd_query(req);
+ return 0;
+ }
+ return -1;
+}
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
new file mode 100644
index 0000000..da4b6ed
--- /dev/null
+++ b/src/p2p/p2p_utils.c
@@ -0,0 +1,271 @@
+/*
+ * P2P - generic helper functions
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "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;
+}
+
+
+static int p2p_channel_to_freq_j4(int reg_class, int channel)
+{
+ /* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */
+ /* TODO: more regulatory classes */
+ switch (reg_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;
+ }
+ return -1;
+}
+
+
+/**
+ * p2p_channel_to_freq - Convert channel info to frequency
+ * @country: Country code
+ * @reg_class: Regulatory class
+ * @channel: Channel number
+ * Returns: Frequency in MHz or -1 if the specified channel is unknown
+ */
+int p2p_channel_to_freq(const char *country, int reg_class, int channel)
+{
+ if (country[2] == 0x04)
+ return p2p_channel_to_freq_j4(reg_class, channel);
+
+ /* These are mainly for backwards compatibility; to be removed */
+ switch (reg_class) {
+ case 1: /* US/1, EU/1, JP/1 = 5 GHz, channels 36,40,44,48 */
+ if (channel < 36 || channel > 48)
+ return -1;
+ return 5000 + 5 * channel;
+ case 3: /* US/3 = 5 GHz, channels 149,153,157,161 */
+ case 5: /* US/5 = 5 GHz, channels 149,153,157,161 */
+ if (channel < 149 || channel > 161)
+ return -1;
+ return 5000 + 5 * channel;
+ case 4: /* EU/4 = 2.407 GHz, channels 1..13 */
+ case 12: /* US/12 = 2.407 GHz, channels 1..11 */
+ case 30: /* JP/30 = 2.407 GHz, channels 1..13 */
+ if (channel < 1 || channel > 13)
+ return -1;
+ return 2407 + 5 * channel;
+ case 31: /* JP/31 = 2.414 GHz, channel 14 */
+ if (channel != 14)
+ return -1;
+ return 2414 + 5 * channel;
+ }
+
+ return -1;
+}
+
+
+/**
+ * p2p_freq_to_channel - Convert frequency into channel info
+ * @country: Country code
+ * @reg_class: Buffer for returning regulatory class
+ * @channel: Buffer for returning channel number
+ * Returns: 0 on success, -1 if the specified frequency is unknown
+ */
+int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class,
+ u8 *channel)
+{
+ /* TODO: more operating classes */
+ if (freq >= 2412 && freq <= 2472) {
+ *reg_class = 81; /* 2.407 GHz, channels 1..13 */
+ *channel = (freq - 2407) / 5;
+ return 0;
+ }
+
+ if (freq == 2484) {
+ *reg_class = 82; /* channel 14 */
+ *channel = 14;
+ return 0;
+ }
+
+ if (freq >= 5180 && freq <= 5240) {
+ *reg_class = 115; /* 5 GHz, channels 36..48 */
+ *channel = (freq - 5000) / 5;
+ return 0;
+ }
+
+ if (freq >= 5745 && freq <= 5805) {
+ *reg_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;
+ }
+ }
+ }
+}
+
+
+/**
+ * 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_supported_freq(struct p2p_data *p2p, unsigned int freq)
+{
+ u8 op_reg_class, op_channel;
+ if (p2p_freq_to_channel(p2p->cfg->country, freq,
+ &op_reg_class, &op_channel) < 0)
+ return 0;
+ return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+ op_channel);
+}
diff --git a/src/radius/.gitignore b/src/radius/.gitignore
new file mode 100644
index 0000000..a89a1f9
--- /dev/null
+++ b/src/radius/.gitignore
@@ -0,0 +1 @@
+libradius.a
diff --git a/src/radius/Makefile b/src/radius/Makefile
new file mode 100644
index 0000000..b199be8
--- /dev/null
+++ b/src/radius/Makefile
@@ -0,0 +1,22 @@
+all: libradius.a
+
+clean:
+ rm -f *~ *.o *.d libradius.a
+
+install:
+ @echo Nothing to be made.
+
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_IPV6
+
+LIB_OBJS= \
+ radius.o \
+ radius_client.o \
+ radius_server.o
+
+libradius.a: $(LIB_OBJS)
+ $(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/radius/radius.c b/src/radius/radius.c
new file mode 100644
index 0000000..70754ef
--- /dev/null
+++ b/src/radius/radius.c
@@ -0,0 +1,1317 @@
+/*
+ * RADIUS message processing
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/wpabuf.h"
+#include "crypto/md5.h"
+#include "crypto/crypto.h"
+#include "radius.h"
+
+
+/**
+ * struct radius_msg - RADIUS message structure for new and parsed messages
+ */
+struct radius_msg {
+ /**
+ * buf - Allocated buffer for RADIUS message
+ */
+ struct wpabuf *buf;
+
+ /**
+ * hdr - Pointer to the RADIUS header in buf
+ */
+ struct radius_hdr *hdr;
+
+ /**
+ * attr_pos - Array of indexes to attributes
+ *
+ * The values are number of bytes from buf to the beginning of
+ * struct radius_attr_hdr.
+ */
+ size_t *attr_pos;
+
+ /**
+ * attr_size - Total size of the attribute pointer array
+ */
+ size_t attr_size;
+
+ /**
+ * attr_used - Total number of attributes in the array
+ */
+ size_t attr_used;
+};
+
+
+struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg)
+{
+ return msg->hdr;
+}
+
+
+struct wpabuf * radius_msg_get_buf(struct radius_msg *msg)
+{
+ return msg->buf;
+}
+
+
+static struct radius_attr_hdr *
+radius_get_attr_hdr(struct radius_msg *msg, int idx)
+{
+ return (struct radius_attr_hdr *)
+ (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]);
+}
+
+
+static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
+{
+ msg->hdr->code = code;
+ msg->hdr->identifier = identifier;
+}
+
+
+static int radius_msg_initialize(struct radius_msg *msg)
+{
+ msg->attr_pos =
+ os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos));
+ if (msg->attr_pos == NULL)
+ return -1;
+
+ msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
+ msg->attr_used = 0;
+
+ return 0;
+}
+
+
+/**
+ * radius_msg_new - Create a new RADIUS message
+ * @code: Code for RADIUS header
+ * @identifier: Identifier for RADIUS header
+ * Returns: Context for RADIUS message or %NULL on failure
+ *
+ * The caller is responsible for freeing the returned data with
+ * radius_msg_free().
+ */
+struct radius_msg * radius_msg_new(u8 code, u8 identifier)
+{
+ struct radius_msg *msg;
+
+ msg = os_zalloc(sizeof(*msg));
+ if (msg == NULL)
+ return NULL;
+
+ msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE);
+ if (msg->buf == NULL || radius_msg_initialize(msg)) {
+ radius_msg_free(msg);
+ return NULL;
+ }
+ msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr));
+
+ radius_msg_set_hdr(msg, code, identifier);
+
+ return msg;
+}
+
+
+/**
+ * radius_msg_free - Free a RADIUS message
+ * @msg: RADIUS message from radius_msg_new() or radius_msg_parse()
+ */
+void radius_msg_free(struct radius_msg *msg)
+{
+ if (msg == NULL)
+ return;
+
+ wpabuf_free(msg->buf);
+ os_free(msg->attr_pos);
+ os_free(msg);
+}
+
+
+static const char *radius_code_string(u8 code)
+{
+ switch (code) {
+ case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request";
+ case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept";
+ case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject";
+ case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request";
+ case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response";
+ case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge";
+ case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
+ case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
+ case RADIUS_CODE_RESERVED: return "Reserved";
+ default: return "?Unknown?";
+ }
+}
+
+
+struct radius_attr_type {
+ u8 type;
+ char *name;
+ enum {
+ RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
+ RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6
+ } data_type;
+};
+
+static struct radius_attr_type radius_attrs[] =
+{
+ { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
+ { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
+ RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
+ RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
+ RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
+ { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
+ RADIUS_ATTR_HEXDUMP },
+ { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
+ RADIUS_ATTR_UNDIST },
+ { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id",
+ RADIUS_ATTR_HEXDUMP },
+ { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
+ RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
+ RADIUS_ATTR_TEXT },
+ { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
+};
+#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
+
+
+static struct radius_attr_type *radius_get_attr_type(u8 type)
+{
+ size_t i;
+
+ for (i = 0; i < RADIUS_ATTRS; i++) {
+ if (type == radius_attrs[i].type)
+ return &radius_attrs[i];
+ }
+
+ return NULL;
+}
+
+
+static void print_char(char c)
+{
+ if (c >= 32 && c < 127)
+ printf("%c", c);
+ else
+ printf("<%02x>", c);
+}
+
+
+static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
+{
+ struct radius_attr_type *attr;
+ int i, len;
+ unsigned char *pos;
+
+ attr = radius_get_attr_type(hdr->type);
+
+ printf(" Attribute %d (%s) length=%d\n",
+ hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
+
+ if (attr == NULL)
+ return;
+
+ len = hdr->length - sizeof(struct radius_attr_hdr);
+ pos = (unsigned char *) (hdr + 1);
+
+ switch (attr->data_type) {
+ case RADIUS_ATTR_TEXT:
+ printf(" Value: '");
+ for (i = 0; i < len; i++)
+ print_char(pos[i]);
+ printf("'\n");
+ break;
+
+ case RADIUS_ATTR_IP:
+ if (len == 4) {
+ struct in_addr addr;
+ os_memcpy(&addr, pos, 4);
+ printf(" Value: %s\n", inet_ntoa(addr));
+ } else
+ printf(" Invalid IP address length %d\n", len);
+ break;
+
+#ifdef CONFIG_IPV6
+ case RADIUS_ATTR_IPV6:
+ if (len == 16) {
+ char buf[128];
+ const char *atxt;
+ struct in6_addr *addr = (struct in6_addr *) pos;
+ atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
+ printf(" Value: %s\n", atxt ? atxt : "?");
+ } else
+ printf(" Invalid IPv6 address length %d\n", len);
+ break;
+#endif /* CONFIG_IPV6 */
+
+ case RADIUS_ATTR_HEXDUMP:
+ case RADIUS_ATTR_UNDIST:
+ printf(" Value:");
+ for (i = 0; i < len; i++)
+ printf(" %02x", pos[i]);
+ printf("\n");
+ break;
+
+ case RADIUS_ATTR_INT32:
+ if (len == 4)
+ printf(" Value: %u\n", WPA_GET_BE32(pos));
+ else
+ printf(" Invalid INT32 length %d\n", len);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+void radius_msg_dump(struct radius_msg *msg)
+{
+ size_t i;
+
+ printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
+ msg->hdr->code, radius_code_string(msg->hdr->code),
+ msg->hdr->identifier, ntohs(msg->hdr->length));
+
+ for (i = 0; i < msg->attr_used; i++) {
+ struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
+ radius_msg_dump_attr(attr);
+ }
+}
+
+
+int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len)
+{
+ if (secret) {
+ u8 auth[MD5_MAC_LEN];
+ struct radius_attr_hdr *attr;
+
+ os_memset(auth, 0, MD5_MAC_LEN);
+ attr = radius_msg_add_attr(msg,
+ RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ auth, MD5_MAC_LEN);
+ if (attr == NULL) {
+ wpa_printf(MSG_WARNING, "RADIUS: Could not add "
+ "Message-Authenticator");
+ return -1;
+ }
+ msg->hdr->length = htons(wpabuf_len(msg->buf));
+ hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf), (u8 *) (attr + 1));
+ } else
+ msg->hdr->length = htons(wpabuf_len(msg->buf));
+
+ if (wpabuf_len(msg->buf) > 0xffff) {
+ wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
+ (unsigned long) wpabuf_len(msg->buf));
+ return -1;
+ }
+ return 0;
+}
+
+
+int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len, const u8 *req_authenticator)
+{
+ u8 auth[MD5_MAC_LEN];
+ struct radius_attr_hdr *attr;
+ const u8 *addr[4];
+ size_t len[4];
+
+ os_memset(auth, 0, MD5_MAC_LEN);
+ attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ auth, MD5_MAC_LEN);
+ if (attr == NULL) {
+ printf("WARNING: Could not add Message-Authenticator\n");
+ return -1;
+ }
+ msg->hdr->length = htons(wpabuf_len(msg->buf));
+ os_memcpy(msg->hdr->authenticator, req_authenticator,
+ sizeof(msg->hdr->authenticator));
+ hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf), (u8 *) (attr + 1));
+
+ /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+ addr[0] = (u8 *) msg->hdr;
+ len[0] = 1 + 1 + 2;
+ addr[1] = req_authenticator;
+ len[1] = MD5_MAC_LEN;
+ addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
+ len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+ addr[3] = secret;
+ len[3] = secret_len;
+ md5_vector(4, addr, len, msg->hdr->authenticator);
+
+ if (wpabuf_len(msg->buf) > 0xffff) {
+ wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
+ (unsigned long) wpabuf_len(msg->buf));
+ return -1;
+ }
+ return 0;
+}
+
+
+void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len)
+{
+ const u8 *addr[2];
+ size_t len[2];
+
+ msg->hdr->length = htons(wpabuf_len(msg->buf));
+ os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
+ addr[0] = wpabuf_head(msg->buf);
+ len[0] = wpabuf_len(msg->buf);
+ addr[1] = secret;
+ len[1] = secret_len;
+ md5_vector(2, addr, len, msg->hdr->authenticator);
+
+ if (wpabuf_len(msg->buf) > 0xffff) {
+ wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
+ (unsigned long) wpabuf_len(msg->buf));
+ }
+}
+
+
+static int radius_msg_add_attr_to_array(struct radius_msg *msg,
+ struct radius_attr_hdr *attr)
+{
+ if (msg->attr_used >= msg->attr_size) {
+ size_t *nattr_pos;
+ int nlen = msg->attr_size * 2;
+
+ nattr_pos = os_realloc(msg->attr_pos,
+ nlen * sizeof(*msg->attr_pos));
+ if (nattr_pos == NULL)
+ return -1;
+
+ msg->attr_pos = nattr_pos;
+ msg->attr_size = nlen;
+ }
+
+ msg->attr_pos[msg->attr_used++] =
+ (unsigned char *) attr - wpabuf_head_u8(msg->buf);
+
+ return 0;
+}
+
+
+struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
+ const u8 *data, size_t data_len)
+{
+ size_t buf_needed;
+ struct radius_attr_hdr *attr;
+
+ if (data_len > RADIUS_MAX_ATTR_LEN) {
+ printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
+ (unsigned long) data_len);
+ return NULL;
+ }
+
+ buf_needed = sizeof(*attr) + data_len;
+
+ if (wpabuf_tailroom(msg->buf) < buf_needed) {
+ /* allocate more space for message buffer */
+ if (wpabuf_resize(&msg->buf, buf_needed) < 0)
+ return NULL;
+ msg->hdr = wpabuf_mhead(msg->buf);
+ }
+
+ attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
+ attr->type = type;
+ attr->length = sizeof(*attr) + data_len;
+ wpabuf_put_data(msg->buf, data, data_len);
+
+ if (radius_msg_add_attr_to_array(msg, attr))
+ return NULL;
+
+ return attr;
+}
+
+
+/**
+ * radius_msg_parse - Parse a RADIUS message
+ * @data: RADIUS message to be parsed
+ * @len: Length of data buffer in octets
+ * Returns: Parsed RADIUS message or %NULL on failure
+ *
+ * This parses a RADIUS message and makes a copy of its data. The caller is
+ * responsible for freeing the returned data with radius_msg_free().
+ */
+struct radius_msg * radius_msg_parse(const u8 *data, size_t len)
+{
+ struct radius_msg *msg;
+ struct radius_hdr *hdr;
+ struct radius_attr_hdr *attr;
+ size_t msg_len;
+ unsigned char *pos, *end;
+
+ if (data == NULL || len < sizeof(*hdr))
+ return NULL;
+
+ hdr = (struct radius_hdr *) data;
+
+ msg_len = ntohs(hdr->length);
+ if (msg_len < sizeof(*hdr) || msg_len > len) {
+ wpa_printf(MSG_INFO, "RADIUS: Invalid message length");
+ return NULL;
+ }
+
+ if (msg_len < len) {
+ wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after "
+ "RADIUS message", (unsigned long) len - msg_len);
+ }
+
+ msg = os_zalloc(sizeof(*msg));
+ if (msg == NULL)
+ return NULL;
+
+ msg->buf = wpabuf_alloc_copy(data, msg_len);
+ if (msg->buf == NULL || radius_msg_initialize(msg)) {
+ radius_msg_free(msg);
+ return NULL;
+ }
+ msg->hdr = wpabuf_mhead(msg->buf);
+
+ /* parse attributes */
+ pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr);
+ end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf);
+ while (pos < end) {
+ if ((size_t) (end - pos) < sizeof(*attr))
+ goto fail;
+
+ attr = (struct radius_attr_hdr *) pos;
+
+ if (pos + attr->length > end || attr->length < sizeof(*attr))
+ goto fail;
+
+ /* TODO: check that attr->length is suitable for attr->type */
+
+ if (radius_msg_add_attr_to_array(msg, attr))
+ goto fail;
+
+ pos += attr->length;
+ }
+
+ return msg;
+
+ fail:
+ radius_msg_free(msg);
+ return NULL;
+}
+
+
+int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
+{
+ const u8 *pos = data;
+ size_t left = data_len;
+
+ while (left > 0) {
+ int len;
+ if (left > RADIUS_MAX_ATTR_LEN)
+ len = RADIUS_MAX_ATTR_LEN;
+ else
+ len = left;
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
+ pos, len))
+ return 0;
+
+ pos += len;
+ left -= len;
+ }
+
+ return 1;
+}
+
+
+u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
+{
+ u8 *eap, *pos;
+ size_t len, i;
+ struct radius_attr_hdr *attr;
+
+ if (msg == NULL)
+ return NULL;
+
+ len = 0;
+ for (i = 0; i < msg->attr_used; i++) {
+ attr = radius_get_attr_hdr(msg, i);
+ if (attr->type == RADIUS_ATTR_EAP_MESSAGE)
+ len += attr->length - sizeof(struct radius_attr_hdr);
+ }
+
+ if (len == 0)
+ return NULL;
+
+ eap = os_malloc(len);
+ if (eap == NULL)
+ return NULL;
+
+ pos = eap;
+ for (i = 0; i < msg->attr_used; i++) {
+ attr = radius_get_attr_hdr(msg, i);
+ if (attr->type == RADIUS_ATTR_EAP_MESSAGE) {
+ int flen = attr->length - sizeof(*attr);
+ os_memcpy(pos, attr + 1, flen);
+ pos += flen;
+ }
+ }
+
+ if (eap_len)
+ *eap_len = len;
+
+ return eap;
+}
+
+
+int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len, const u8 *req_auth)
+{
+ u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
+ u8 orig_authenticator[16];
+ struct radius_attr_hdr *attr = NULL, *tmp;
+ size_t i;
+
+ for (i = 0; i < msg->attr_used; i++) {
+ tmp = radius_get_attr_hdr(msg, i);
+ if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
+ if (attr != NULL) {
+ printf("Multiple Message-Authenticator "
+ "attributes in RADIUS message\n");
+ return 1;
+ }
+ attr = tmp;
+ }
+ }
+
+ if (attr == NULL) {
+ printf("No Message-Authenticator attribute found\n");
+ return 1;
+ }
+
+ os_memcpy(orig, attr + 1, MD5_MAC_LEN);
+ os_memset(attr + 1, 0, MD5_MAC_LEN);
+ if (req_auth) {
+ os_memcpy(orig_authenticator, msg->hdr->authenticator,
+ sizeof(orig_authenticator));
+ os_memcpy(msg->hdr->authenticator, req_auth,
+ sizeof(msg->hdr->authenticator));
+ }
+ hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf), auth);
+ os_memcpy(attr + 1, orig, MD5_MAC_LEN);
+ if (req_auth) {
+ os_memcpy(msg->hdr->authenticator, orig_authenticator,
+ sizeof(orig_authenticator));
+ }
+
+ if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
+ printf("Invalid Message-Authenticator!\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len, struct radius_msg *sent_msg, int auth)
+{
+ const u8 *addr[4];
+ size_t len[4];
+ u8 hash[MD5_MAC_LEN];
+
+ if (sent_msg == NULL) {
+ printf("No matching Access-Request message found\n");
+ return 1;
+ }
+
+ if (auth &&
+ radius_msg_verify_msg_auth(msg, secret, secret_len,
+ sent_msg->hdr->authenticator)) {
+ return 1;
+ }
+
+ /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+ addr[0] = (u8 *) msg->hdr;
+ len[0] = 1 + 1 + 2;
+ addr[1] = sent_msg->hdr->authenticator;
+ len[1] = MD5_MAC_LEN;
+ addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
+ len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+ addr[3] = secret;
+ len[3] = secret_len;
+ md5_vector(4, addr, len, hash);
+ if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
+ printf("Response Authenticator invalid!\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
+ u8 type)
+{
+ struct radius_attr_hdr *attr;
+ size_t i;
+ int count = 0;
+
+ for (i = 0; i < src->attr_used; i++) {
+ attr = radius_get_attr_hdr(src, i);
+ if (attr->type == type) {
+ if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
+ attr->length - sizeof(*attr)))
+ return -1;
+ count++;
+ }
+ }
+
+ return count;
+}
+
+
+/* Create Request Authenticator. The value should be unique over the lifetime
+ * of the shared secret between authenticator and authentication server.
+ * Use one-way MD5 hash calculated from current timestamp and some data given
+ * by the caller. */
+void radius_msg_make_authenticator(struct radius_msg *msg,
+ const u8 *data, size_t len)
+{
+ struct os_time tv;
+ long int l;
+ const u8 *addr[3];
+ size_t elen[3];
+
+ os_get_time(&tv);
+ l = os_random();
+ addr[0] = (u8 *) &tv;
+ elen[0] = sizeof(tv);
+ addr[1] = data;
+ elen[1] = len;
+ addr[2] = (u8 *) &l;
+ elen[2] = sizeof(l);
+ md5_vector(3, addr, elen, msg->hdr->authenticator);
+}
+
+
+/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
+ * Returns the Attribute payload and sets alen to indicate the length of the
+ * payload if a vendor attribute with subtype is found, otherwise returns NULL.
+ * The returned payload is allocated with os_malloc() and caller must free it
+ * by calling os_free().
+ */
+static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
+ u8 subtype, size_t *alen)
+{
+ u8 *data, *pos;
+ size_t i, len;
+
+ if (msg == NULL)
+ return NULL;
+
+ for (i = 0; i < msg->attr_used; i++) {
+ struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
+ size_t left;
+ u32 vendor_id;
+ struct radius_attr_vendor *vhdr;
+
+ if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
+ continue;
+
+ left = attr->length - sizeof(*attr);
+ if (left < 4)
+ continue;
+
+ pos = (u8 *) (attr + 1);
+
+ os_memcpy(&vendor_id, pos, 4);
+ pos += 4;
+ left -= 4;
+
+ if (ntohl(vendor_id) != vendor)
+ continue;
+
+ while (left >= sizeof(*vhdr)) {
+ vhdr = (struct radius_attr_vendor *) pos;
+ if (vhdr->vendor_length > left ||
+ vhdr->vendor_length < sizeof(*vhdr)) {
+ left = 0;
+ break;
+ }
+ if (vhdr->vendor_type != subtype) {
+ pos += vhdr->vendor_length;
+ left -= vhdr->vendor_length;
+ continue;
+ }
+
+ len = vhdr->vendor_length - sizeof(*vhdr);
+ data = os_malloc(len);
+ if (data == NULL)
+ return NULL;
+ os_memcpy(data, pos + sizeof(*vhdr), len);
+ if (alen)
+ *alen = len;
+ return data;
+ }
+ }
+
+ return NULL;
+}
+
+
+static u8 * decrypt_ms_key(const u8 *key, size_t len,
+ const u8 *req_authenticator,
+ const u8 *secret, size_t secret_len, size_t *reslen)
+{
+ u8 *plain, *ppos, *res;
+ const u8 *pos;
+ size_t left, plen;
+ u8 hash[MD5_MAC_LEN];
+ int i, first = 1;
+ const u8 *addr[3];
+ size_t elen[3];
+
+ /* key: 16-bit salt followed by encrypted key info */
+
+ if (len < 2 + 16)
+ return NULL;
+
+ pos = key + 2;
+ left = len - 2;
+ if (left % 16) {
+ printf("Invalid ms key len %lu\n", (unsigned long) left);
+ return NULL;
+ }
+
+ plen = left;
+ ppos = plain = os_malloc(plen);
+ if (plain == NULL)
+ return NULL;
+ plain[0] = 0;
+
+ while (left > 0) {
+ /* b(1) = MD5(Secret + Request-Authenticator + Salt)
+ * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
+
+ addr[0] = secret;
+ elen[0] = secret_len;
+ if (first) {
+ addr[1] = req_authenticator;
+ elen[1] = MD5_MAC_LEN;
+ addr[2] = key;
+ elen[2] = 2; /* Salt */
+ } else {
+ addr[1] = pos - MD5_MAC_LEN;
+ elen[1] = MD5_MAC_LEN;
+ }
+ md5_vector(first ? 3 : 2, addr, elen, hash);
+ first = 0;
+
+ for (i = 0; i < MD5_MAC_LEN; i++)
+ *ppos++ = *pos++ ^ hash[i];
+ left -= MD5_MAC_LEN;
+ }
+
+ if (plain[0] == 0 || plain[0] > plen - 1) {
+ printf("Failed to decrypt MPPE key\n");
+ os_free(plain);
+ return NULL;
+ }
+
+ res = os_malloc(plain[0]);
+ if (res == NULL) {
+ os_free(plain);
+ return NULL;
+ }
+ os_memcpy(res, plain + 1, plain[0]);
+ if (reslen)
+ *reslen = plain[0];
+ os_free(plain);
+ return res;
+}
+
+
+static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
+ const u8 *req_authenticator,
+ const u8 *secret, size_t secret_len,
+ u8 *ebuf, size_t *elen)
+{
+ int i, len, first = 1;
+ u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
+ const u8 *addr[3];
+ size_t _len[3];
+
+ WPA_PUT_BE16(saltbuf, salt);
+
+ len = 1 + key_len;
+ if (len & 0x0f) {
+ len = (len & 0xf0) + 16;
+ }
+ os_memset(ebuf, 0, len);
+ ebuf[0] = key_len;
+ os_memcpy(ebuf + 1, key, key_len);
+
+ *elen = len;
+
+ pos = ebuf;
+ while (len > 0) {
+ /* b(1) = MD5(Secret + Request-Authenticator + Salt)
+ * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
+ addr[0] = secret;
+ _len[0] = secret_len;
+ if (first) {
+ addr[1] = req_authenticator;
+ _len[1] = MD5_MAC_LEN;
+ addr[2] = saltbuf;
+ _len[2] = sizeof(saltbuf);
+ } else {
+ addr[1] = pos - MD5_MAC_LEN;
+ _len[1] = MD5_MAC_LEN;
+ }
+ md5_vector(first ? 3 : 2, addr, _len, hash);
+ first = 0;
+
+ for (i = 0; i < MD5_MAC_LEN; i++)
+ *pos++ ^= hash[i];
+
+ len -= MD5_MAC_LEN;
+ }
+}
+
+
+struct radius_ms_mppe_keys *
+radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+ const u8 *secret, size_t secret_len)
+{
+ u8 *key;
+ size_t keylen;
+ struct radius_ms_mppe_keys *keys;
+
+ if (msg == NULL || sent_msg == NULL)
+ return NULL;
+
+ keys = os_zalloc(sizeof(*keys));
+ if (keys == NULL)
+ return NULL;
+
+ key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
+ RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
+ &keylen);
+ if (key) {
+ keys->send = decrypt_ms_key(key, keylen,
+ sent_msg->hdr->authenticator,
+ secret, secret_len,
+ &keys->send_len);
+ os_free(key);
+ }
+
+ key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
+ RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
+ &keylen);
+ if (key) {
+ keys->recv = decrypt_ms_key(key, keylen,
+ sent_msg->hdr->authenticator,
+ secret, secret_len,
+ &keys->recv_len);
+ os_free(key);
+ }
+
+ return keys;
+}
+
+
+struct radius_ms_mppe_keys *
+radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+ const u8 *secret, size_t secret_len)
+{
+ u8 *key;
+ size_t keylen;
+ struct radius_ms_mppe_keys *keys;
+
+ if (msg == NULL || sent_msg == NULL)
+ return NULL;
+
+ keys = os_zalloc(sizeof(*keys));
+ if (keys == NULL)
+ return NULL;
+
+ key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
+ RADIUS_CISCO_AV_PAIR, &keylen);
+ if (key && keylen == 51 &&
+ os_memcmp(key, "leap:session-key=", 17) == 0) {
+ keys->recv = decrypt_ms_key(key + 17, keylen - 17,
+ sent_msg->hdr->authenticator,
+ secret, secret_len,
+ &keys->recv_len);
+ }
+ os_free(key);
+
+ return keys;
+}
+
+
+int radius_msg_add_mppe_keys(struct radius_msg *msg,
+ const u8 *req_authenticator,
+ const u8 *secret, size_t secret_len,
+ const u8 *send_key, size_t send_key_len,
+ const u8 *recv_key, size_t recv_key_len)
+{
+ struct radius_attr_hdr *attr;
+ u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
+ u8 *buf;
+ struct radius_attr_vendor *vhdr;
+ u8 *pos;
+ size_t elen;
+ int hlen;
+ u16 salt;
+
+ hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
+
+ /* MS-MPPE-Send-Key */
+ buf = os_malloc(hlen + send_key_len + 16);
+ if (buf == NULL) {
+ return 0;
+ }
+ pos = buf;
+ os_memcpy(pos, &vendor_id, sizeof(vendor_id));
+ pos += sizeof(vendor_id);
+ vhdr = (struct radius_attr_vendor *) pos;
+ vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
+ pos = (u8 *) (vhdr + 1);
+ salt = os_random() | 0x8000;
+ WPA_PUT_BE16(pos, salt);
+ pos += 2;
+ encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
+ secret_len, pos, &elen);
+ vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
+
+ attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+ buf, hlen + elen);
+ os_free(buf);
+ if (attr == NULL) {
+ return 0;
+ }
+
+ /* MS-MPPE-Recv-Key */
+ buf = os_malloc(hlen + send_key_len + 16);
+ if (buf == NULL) {
+ return 0;
+ }
+ pos = buf;
+ os_memcpy(pos, &vendor_id, sizeof(vendor_id));
+ pos += sizeof(vendor_id);
+ vhdr = (struct radius_attr_vendor *) pos;
+ vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
+ pos = (u8 *) (vhdr + 1);
+ salt ^= 1;
+ WPA_PUT_BE16(pos, salt);
+ pos += 2;
+ encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
+ secret_len, pos, &elen);
+ vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
+
+ attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+ buf, hlen + elen);
+ os_free(buf);
+ if (attr == NULL) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* Add User-Password attribute to a RADIUS message and encrypt it as specified
+ * in RFC 2865, Chap. 5.2 */
+struct radius_attr_hdr *
+radius_msg_add_attr_user_password(struct radius_msg *msg,
+ const u8 *data, size_t data_len,
+ const u8 *secret, size_t secret_len)
+{
+ u8 buf[128];
+ int padlen, i;
+ size_t buf_len, pos;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 hash[16];
+
+ if (data_len > 128)
+ return NULL;
+
+ os_memcpy(buf, data, data_len);
+ buf_len = data_len;
+
+ padlen = data_len % 16;
+ if (padlen) {
+ padlen = 16 - padlen;
+ os_memset(buf + data_len, 0, padlen);
+ buf_len += padlen;
+ }
+
+ addr[0] = secret;
+ len[0] = secret_len;
+ addr[1] = msg->hdr->authenticator;
+ len[1] = 16;
+ md5_vector(2, addr, len, hash);
+
+ for (i = 0; i < 16; i++)
+ buf[i] ^= hash[i];
+ pos = 16;
+
+ while (pos < buf_len) {
+ addr[0] = secret;
+ len[0] = secret_len;
+ addr[1] = &buf[pos - 16];
+ len[1] = 16;
+ md5_vector(2, addr, len, hash);
+
+ for (i = 0; i < 16; i++)
+ buf[pos + i] ^= hash[i];
+
+ pos += 16;
+ }
+
+ return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
+ buf, buf_len);
+}
+
+
+int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
+{
+ struct radius_attr_hdr *attr = NULL, *tmp;
+ size_t i, dlen;
+
+ for (i = 0; i < msg->attr_used; i++) {
+ tmp = radius_get_attr_hdr(msg, i);
+ if (tmp->type == type) {
+ attr = tmp;
+ break;
+ }
+ }
+
+ if (!attr)
+ return -1;
+
+ dlen = attr->length - sizeof(*attr);
+ if (buf)
+ os_memcpy(buf, (attr + 1), dlen > len ? len : dlen);
+ return dlen;
+}
+
+
+int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
+ size_t *len, const u8 *start)
+{
+ size_t i;
+ struct radius_attr_hdr *attr = NULL, *tmp;
+
+ for (i = 0; i < msg->attr_used; i++) {
+ tmp = radius_get_attr_hdr(msg, i);
+ if (tmp->type == type &&
+ (start == NULL || (u8 *) tmp > start)) {
+ attr = tmp;
+ break;
+ }
+ }
+
+ if (!attr)
+ return -1;
+
+ *buf = (u8 *) (attr + 1);
+ *len = attr->length - sizeof(*attr);
+ return 0;
+}
+
+
+int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len)
+{
+ size_t i;
+ int count;
+
+ for (count = 0, i = 0; i < msg->attr_used; i++) {
+ struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
+ if (attr->type == type &&
+ attr->length >= sizeof(struct radius_attr_hdr) + min_len)
+ count++;
+ }
+
+ return count;
+}
+
+
+struct radius_tunnel_attrs {
+ int tag_used;
+ int type; /* Tunnel-Type */
+ int medium_type; /* Tunnel-Medium-Type */
+ int vlanid;
+};
+
+
+/**
+ * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
+ * @msg: RADIUS message
+ * Returns: VLAN ID for the first tunnel configuration of -1 if none is found
+ */
+int radius_msg_get_vlanid(struct radius_msg *msg)
+{
+ struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun;
+ size_t i;
+ struct radius_attr_hdr *attr = NULL;
+ const u8 *data;
+ char buf[10];
+ size_t dlen;
+
+ os_memset(&tunnel, 0, sizeof(tunnel));
+
+ for (i = 0; i < msg->attr_used; i++) {
+ attr = radius_get_attr_hdr(msg, i);
+ data = (const u8 *) (attr + 1);
+ dlen = attr->length - sizeof(*attr);
+ if (attr->length < 3)
+ continue;
+ if (data[0] >= RADIUS_TUNNEL_TAGS)
+ tun = &tunnel[0];
+ else
+ tun = &tunnel[data[0]];
+
+ switch (attr->type) {
+ case RADIUS_ATTR_TUNNEL_TYPE:
+ if (attr->length != 6)
+ break;
+ tun->tag_used++;
+ tun->type = WPA_GET_BE24(data + 1);
+ break;
+ case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
+ if (attr->length != 6)
+ break;
+ tun->tag_used++;
+ tun->medium_type = WPA_GET_BE24(data + 1);
+ break;
+ case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
+ if (data[0] < RADIUS_TUNNEL_TAGS) {
+ data++;
+ dlen--;
+ }
+ if (dlen >= sizeof(buf))
+ break;
+ os_memcpy(buf, data, dlen);
+ buf[dlen] = '\0';
+ tun->tag_used++;
+ tun->vlanid = atoi(buf);
+ break;
+ }
+ }
+
+ for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) {
+ tun = &tunnel[i];
+ if (tun->tag_used &&
+ tun->type == RADIUS_TUNNEL_TYPE_VLAN &&
+ tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 &&
+ tun->vlanid > 0)
+ return tun->vlanid;
+ }
+
+ return -1;
+}
+
+
+void radius_free_class(struct radius_class_data *c)
+{
+ size_t i;
+ if (c == NULL)
+ return;
+ for (i = 0; i < c->count; i++)
+ os_free(c->attr[i].data);
+ os_free(c->attr);
+ c->attr = NULL;
+ c->count = 0;
+}
+
+
+int radius_copy_class(struct radius_class_data *dst,
+ const struct radius_class_data *src)
+{
+ size_t i;
+
+ if (src->attr == NULL)
+ return 0;
+
+ dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data));
+ if (dst->attr == NULL)
+ return -1;
+
+ dst->count = 0;
+
+ for (i = 0; i < src->count; i++) {
+ dst->attr[i].data = os_malloc(src->attr[i].len);
+ if (dst->attr[i].data == NULL)
+ break;
+ dst->count++;
+ os_memcpy(dst->attr[i].data, src->attr[i].data,
+ src->attr[i].len);
+ dst->attr[i].len = src->attr[i].len;
+ }
+
+ return 0;
+}
diff --git a/src/radius/radius.h b/src/radius/radius.h
new file mode 100644
index 0000000..a3cdac0
--- /dev/null
+++ b/src/radius/radius.h
@@ -0,0 +1,273 @@
+/*
+ * RADIUS message processing
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef RADIUS_H
+#define RADIUS_H
+
+/* RFC 2865 - RADIUS */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct radius_hdr {
+ u8 code;
+ u8 identifier;
+ u16 length; /* including this header */
+ u8 authenticator[16];
+ /* followed by length-20 octets of attributes */
+} STRUCT_PACKED;
+
+enum { RADIUS_CODE_ACCESS_REQUEST = 1,
+ RADIUS_CODE_ACCESS_ACCEPT = 2,
+ RADIUS_CODE_ACCESS_REJECT = 3,
+ RADIUS_CODE_ACCOUNTING_REQUEST = 4,
+ RADIUS_CODE_ACCOUNTING_RESPONSE = 5,
+ RADIUS_CODE_ACCESS_CHALLENGE = 11,
+ RADIUS_CODE_STATUS_SERVER = 12,
+ RADIUS_CODE_STATUS_CLIENT = 13,
+ RADIUS_CODE_RESERVED = 255
+};
+
+struct radius_attr_hdr {
+ u8 type;
+ u8 length; /* including this header */
+ /* followed by length-2 octets of attribute value */
+} STRUCT_PACKED;
+
+#define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr))
+
+enum { RADIUS_ATTR_USER_NAME = 1,
+ RADIUS_ATTR_USER_PASSWORD = 2,
+ RADIUS_ATTR_NAS_IP_ADDRESS = 4,
+ RADIUS_ATTR_NAS_PORT = 5,
+ RADIUS_ATTR_FRAMED_MTU = 12,
+ RADIUS_ATTR_REPLY_MESSAGE = 18,
+ RADIUS_ATTR_STATE = 24,
+ RADIUS_ATTR_CLASS = 25,
+ RADIUS_ATTR_VENDOR_SPECIFIC = 26,
+ RADIUS_ATTR_SESSION_TIMEOUT = 27,
+ RADIUS_ATTR_IDLE_TIMEOUT = 28,
+ RADIUS_ATTR_TERMINATION_ACTION = 29,
+ RADIUS_ATTR_CALLED_STATION_ID = 30,
+ RADIUS_ATTR_CALLING_STATION_ID = 31,
+ RADIUS_ATTR_NAS_IDENTIFIER = 32,
+ RADIUS_ATTR_PROXY_STATE = 33,
+ RADIUS_ATTR_ACCT_STATUS_TYPE = 40,
+ RADIUS_ATTR_ACCT_DELAY_TIME = 41,
+ RADIUS_ATTR_ACCT_INPUT_OCTETS = 42,
+ RADIUS_ATTR_ACCT_OUTPUT_OCTETS = 43,
+ RADIUS_ATTR_ACCT_SESSION_ID = 44,
+ RADIUS_ATTR_ACCT_AUTHENTIC = 45,
+ RADIUS_ATTR_ACCT_SESSION_TIME = 46,
+ RADIUS_ATTR_ACCT_INPUT_PACKETS = 47,
+ RADIUS_ATTR_ACCT_OUTPUT_PACKETS = 48,
+ RADIUS_ATTR_ACCT_TERMINATE_CAUSE = 49,
+ RADIUS_ATTR_ACCT_MULTI_SESSION_ID = 50,
+ RADIUS_ATTR_ACCT_LINK_COUNT = 51,
+ RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52,
+ RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53,
+ RADIUS_ATTR_EVENT_TIMESTAMP = 55,
+ RADIUS_ATTR_NAS_PORT_TYPE = 61,
+ RADIUS_ATTR_TUNNEL_TYPE = 64,
+ RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65,
+ RADIUS_ATTR_CONNECT_INFO = 77,
+ RADIUS_ATTR_EAP_MESSAGE = 79,
+ RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80,
+ RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
+ RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
+ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
+ RADIUS_ATTR_NAS_IPV6_ADDRESS = 95
+};
+
+
+/* Termination-Action */
+#define RADIUS_TERMINATION_ACTION_DEFAULT 0
+#define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1
+
+/* NAS-Port-Type */
+#define RADIUS_NAS_PORT_TYPE_IEEE_802_11 19
+
+/* Acct-Status-Type */
+#define RADIUS_ACCT_STATUS_TYPE_START 1
+#define RADIUS_ACCT_STATUS_TYPE_STOP 2
+#define RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE 3
+#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON 7
+#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF 8
+
+/* Acct-Authentic */
+#define RADIUS_ACCT_AUTHENTIC_RADIUS 1
+#define RADIUS_ACCT_AUTHENTIC_LOCAL 2
+#define RADIUS_ACCT_AUTHENTIC_REMOTE 3
+
+/* Acct-Terminate-Cause */
+#define RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST 1
+#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_CARRIER 2
+#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE 3
+#define RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT 4
+#define RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT 5
+#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET 6
+#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT 7
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_ERROR 8
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_ERROR 9
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REQUEST 10
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT 11
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_UNNEEDED 12
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_PREEMPTED 13
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_SUSPENDED 14
+#define RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAILABLE 15
+#define RADIUS_ACCT_TERMINATE_CAUSE_CALLBACK 16
+#define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR 17
+#define RADIUS_ACCT_TERMINATE_CAUSE_HOST_REQUEST 18
+
+#define RADIUS_TUNNEL_TAGS 32
+
+/* Tunnel-Type */
+#define RADIUS_TUNNEL_TYPE_PPTP 1
+#define RADIUS_TUNNEL_TYPE_L2TP 3
+#define RADIUS_TUNNEL_TYPE_IPIP 7
+#define RADIUS_TUNNEL_TYPE_GRE 10
+#define RADIUS_TUNNEL_TYPE_VLAN 13
+
+/* Tunnel-Medium-Type */
+#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV4 1
+#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV6 2
+#define RADIUS_TUNNEL_MEDIUM_TYPE_802 6
+
+
+struct radius_attr_vendor {
+ u8 vendor_type;
+ u8 vendor_length;
+} STRUCT_PACKED;
+
+#define RADIUS_VENDOR_ID_CISCO 9
+#define RADIUS_CISCO_AV_PAIR 1
+
+/* RFC 2548 - Microsoft Vendor-specific RADIUS Attributes */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+
+enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16,
+ RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17
+};
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+struct radius_ms_mppe_keys {
+ u8 *send;
+ size_t send_len;
+ u8 *recv;
+ size_t recv_len;
+};
+
+
+struct radius_msg;
+
+/* Default size to be allocated for new RADIUS messages */
+#define RADIUS_DEFAULT_MSG_SIZE 1024
+
+/* Default size to be allocated for attribute array */
+#define RADIUS_DEFAULT_ATTR_COUNT 16
+
+
+/* MAC address ASCII format for IEEE 802.1X use
+ * (draft-congdon-radius-8021x-20.txt) */
+#define RADIUS_802_1X_ADDR_FORMAT "%02X-%02X-%02X-%02X-%02X-%02X"
+/* MAC address ASCII format for non-802.1X use */
+#define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x"
+
+struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg);
+struct wpabuf * radius_msg_get_buf(struct radius_msg *msg);
+struct radius_msg * radius_msg_new(u8 code, u8 identifier);
+void radius_msg_free(struct radius_msg *msg);
+void radius_msg_dump(struct radius_msg *msg);
+int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len);
+int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len, const u8 *req_authenticator);
+void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len);
+struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type,
+ const u8 *data, size_t data_len);
+struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
+int radius_msg_add_eap(struct radius_msg *msg, const u8 *data,
+ size_t data_len);
+u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len);
+int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len, struct radius_msg *sent_msg,
+ int auth);
+int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len, const u8 *req_auth);
+int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
+ u8 type);
+void radius_msg_make_authenticator(struct radius_msg *msg,
+ const u8 *data, size_t len);
+struct radius_ms_mppe_keys *
+radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+ const u8 *secret, size_t secret_len);
+struct radius_ms_mppe_keys *
+radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+ const u8 *secret, size_t secret_len);
+int radius_msg_add_mppe_keys(struct radius_msg *msg,
+ const u8 *req_authenticator,
+ const u8 *secret, size_t secret_len,
+ const u8 *send_key, size_t send_key_len,
+ const u8 *recv_key, size_t recv_key_len);
+struct radius_attr_hdr *
+radius_msg_add_attr_user_password(struct radius_msg *msg,
+ const u8 *data, size_t data_len,
+ const u8 *secret, size_t secret_len);
+int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len);
+int radius_msg_get_vlanid(struct radius_msg *msg);
+
+static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type,
+ u32 value)
+{
+ u32 val = htonl(value);
+ return radius_msg_add_attr(msg, type, (u8 *) &val, 4) != NULL;
+}
+
+static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type,
+ u32 *value)
+{
+ u32 val;
+ int res;
+ res = radius_msg_get_attr(msg, type, (u8 *) &val, 4);
+ if (res != 4)
+ return -1;
+
+ *value = ntohl(val);
+ return 0;
+}
+int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
+ size_t *len, const u8 *start);
+int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len);
+
+
+struct radius_attr_data {
+ u8 *data;
+ size_t len;
+};
+
+struct radius_class_data {
+ struct radius_attr_data *attr;
+ size_t count;
+};
+
+void radius_free_class(struct radius_class_data *c);
+int radius_copy_class(struct radius_class_data *dst,
+ const struct radius_class_data *src);
+
+#endif /* RADIUS_H */
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
new file mode 100644
index 0000000..691f77a
--- /dev/null
+++ b/src/radius/radius_client.c
@@ -0,0 +1,1499 @@
+/*
+ * RADIUS client
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "radius.h"
+#include "radius_client.h"
+#include "eloop.h"
+
+/* Defaults for RADIUS retransmit values (exponential backoff) */
+
+/**
+ * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds
+ */
+#define RADIUS_CLIENT_FIRST_WAIT 3
+
+/**
+ * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds
+ */
+#define RADIUS_CLIENT_MAX_WAIT 120
+
+/**
+ * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries
+ *
+ * Maximum number of retransmit attempts before the entry is removed from
+ * retransmit list.
+ */
+#define RADIUS_CLIENT_MAX_RETRIES 10
+
+/**
+ * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages
+ *
+ * Maximum number of entries in retransmit list (oldest entries will be
+ * removed, if this limit is exceeded).
+ */
+#define RADIUS_CLIENT_MAX_ENTRIES 30
+
+/**
+ * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point
+ *
+ * The number of failed retry attempts after which the RADIUS server will be
+ * changed (if one of more backup servers are configured).
+ */
+#define RADIUS_CLIENT_NUM_FAILOVER 4
+
+
+/**
+ * struct radius_rx_handler - RADIUS client RX handler
+ *
+ * This data structure is used internally inside the RADIUS client module to
+ * store registered RX handlers. These handlers are registered by calls to
+ * radius_client_register() and unregistered when the RADIUS client is
+ * deinitialized with a call to radius_client_deinit().
+ */
+struct radius_rx_handler {
+ /**
+ * handler - Received RADIUS message handler
+ */
+ RadiusRxResult (*handler)(struct radius_msg *msg,
+ struct radius_msg *req,
+ const u8 *shared_secret,
+ size_t shared_secret_len,
+ void *data);
+
+ /**
+ * data - Context data for the handler
+ */
+ void *data;
+};
+
+
+/**
+ * struct radius_msg_list - RADIUS client message retransmit list
+ *
+ * This data structure is used internally inside the RADIUS client module to
+ * store pending RADIUS requests that may still need to be retransmitted.
+ */
+struct radius_msg_list {
+ /**
+ * addr - STA/client address
+ *
+ * This is used to find RADIUS messages for the same STA.
+ */
+ u8 addr[ETH_ALEN];
+
+ /**
+ * msg - RADIUS message
+ */
+ struct radius_msg *msg;
+
+ /**
+ * msg_type - Message type
+ */
+ RadiusType msg_type;
+
+ /**
+ * first_try - Time of the first transmission attempt
+ */
+ os_time_t first_try;
+
+ /**
+ * next_try - Time for the next transmission attempt
+ */
+ os_time_t next_try;
+
+ /**
+ * attempts - Number of transmission attempts
+ */
+ int attempts;
+
+ /**
+ * next_wait - Next retransmission wait time in seconds
+ */
+ int next_wait;
+
+ /**
+ * last_attempt - Time of the last transmission attempt
+ */
+ struct os_time last_attempt;
+
+ /**
+ * shared_secret - Shared secret with the target RADIUS server
+ */
+ const u8 *shared_secret;
+
+ /**
+ * shared_secret_len - shared_secret length in octets
+ */
+ size_t shared_secret_len;
+
+ /* TODO: server config with failover to backup server(s) */
+
+ /**
+ * next - Next message in the list
+ */
+ struct radius_msg_list *next;
+};
+
+
+/**
+ * struct radius_client_data - Internal RADIUS client data
+ *
+ * This data structure is used internally inside the RADIUS client module.
+ * External users allocate this by calling radius_client_init() and free it by
+ * calling radius_client_deinit(). The pointer to this opaque data is used in
+ * calls to other functions as an identifier for the RADIUS client instance.
+ */
+struct radius_client_data {
+ /**
+ * ctx - Context pointer for hostapd_logger() callbacks
+ */
+ void *ctx;
+
+ /**
+ * conf - RADIUS client configuration (list of RADIUS servers to use)
+ */
+ struct hostapd_radius_servers *conf;
+
+ /**
+ * auth_serv_sock - IPv4 socket for RADIUS authentication messages
+ */
+ int auth_serv_sock;
+
+ /**
+ * acct_serv_sock - IPv4 socket for RADIUS accounting messages
+ */
+ int acct_serv_sock;
+
+ /**
+ * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages
+ */
+ int auth_serv_sock6;
+
+ /**
+ * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages
+ */
+ int acct_serv_sock6;
+
+ /**
+ * auth_sock - Currently used socket for RADIUS authentication server
+ */
+ int auth_sock;
+
+ /**
+ * acct_sock - Currently used socket for RADIUS accounting server
+ */
+ int acct_sock;
+
+ /**
+ * auth_handlers - Authentication message handlers
+ */
+ struct radius_rx_handler *auth_handlers;
+
+ /**
+ * num_auth_handlers - Number of handlers in auth_handlers
+ */
+ size_t num_auth_handlers;
+
+ /**
+ * acct_handlers - Accounting message handlers
+ */
+ struct radius_rx_handler *acct_handlers;
+
+ /**
+ * num_acct_handlers - Number of handlers in acct_handlers
+ */
+ size_t num_acct_handlers;
+
+ /**
+ * msgs - Pending outgoing RADIUS messages
+ */
+ struct radius_msg_list *msgs;
+
+ /**
+ * num_msgs - Number of pending messages in the msgs list
+ */
+ size_t num_msgs;
+
+ /**
+ * next_radius_identifier - Next RADIUS message identifier to use
+ */
+ u8 next_radius_identifier;
+};
+
+
+static int
+radius_change_server(struct radius_client_data *radius,
+ struct hostapd_radius_server *nserv,
+ struct hostapd_radius_server *oserv,
+ int sock, int sock6, int auth);
+static int radius_client_init_acct(struct radius_client_data *radius);
+static int radius_client_init_auth(struct radius_client_data *radius);
+
+
+static void radius_client_msg_free(struct radius_msg_list *req)
+{
+ radius_msg_free(req->msg);
+ os_free(req);
+}
+
+
+/**
+ * radius_client_register - Register a RADIUS client RX handler
+ * @radius: RADIUS client context from radius_client_init()
+ * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT)
+ * @handler: Handler for received RADIUS messages
+ * @data: Context pointer for handler callbacks
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to register a handler for processing received RADIUS
+ * authentication and accounting messages. The handler() callback function will
+ * be called whenever a RADIUS message is received from the active server.
+ *
+ * There can be multiple registered RADIUS message handlers. The handlers will
+ * be called in order until one of them indicates that it has processed or
+ * queued the message.
+ */
+int radius_client_register(struct radius_client_data *radius,
+ RadiusType msg_type,
+ RadiusRxResult (*handler)(struct radius_msg *msg,
+ struct radius_msg *req,
+ const u8 *shared_secret,
+ size_t shared_secret_len,
+ void *data),
+ void *data)
+{
+ struct radius_rx_handler **handlers, *newh;
+ size_t *num;
+
+ if (msg_type == RADIUS_ACCT) {
+ handlers = &radius->acct_handlers;
+ num = &radius->num_acct_handlers;
+ } else {
+ handlers = &radius->auth_handlers;
+ num = &radius->num_auth_handlers;
+ }
+
+ newh = os_realloc(*handlers,
+ (*num + 1) * sizeof(struct radius_rx_handler));
+ if (newh == NULL)
+ return -1;
+
+ newh[*num].handler = handler;
+ newh[*num].data = data;
+ (*num)++;
+ *handlers = newh;
+
+ return 0;
+}
+
+
+static void radius_client_handle_send_error(struct radius_client_data *radius,
+ int s, RadiusType msg_type)
+{
+#ifndef CONFIG_NATIVE_WINDOWS
+ int _errno = errno;
+ perror("send[RADIUS]");
+ if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
+ _errno == EBADF) {
+ hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "Send failed - maybe interface status changed -"
+ " try to connect again");
+ eloop_unregister_read_sock(s);
+ close(s);
+ if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM)
+ radius_client_init_acct(radius);
+ else
+ radius_client_init_auth(radius);
+ }
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+static int radius_client_retransmit(struct radius_client_data *radius,
+ struct radius_msg_list *entry,
+ os_time_t now)
+{
+ struct hostapd_radius_servers *conf = radius->conf;
+ int s;
+ struct wpabuf *buf;
+
+ if (entry->msg_type == RADIUS_ACCT ||
+ entry->msg_type == RADIUS_ACCT_INTERIM) {
+ s = radius->acct_sock;
+ if (entry->attempts == 0)
+ conf->acct_server->requests++;
+ else {
+ conf->acct_server->timeouts++;
+ conf->acct_server->retransmissions++;
+ }
+ } else {
+ s = radius->auth_sock;
+ if (entry->attempts == 0)
+ conf->auth_server->requests++;
+ else {
+ conf->auth_server->timeouts++;
+ conf->auth_server->retransmissions++;
+ }
+ }
+
+ /* retransmit; remove entry if too many attempts */
+ entry->attempts++;
+ hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
+ radius_msg_get_hdr(entry->msg)->identifier);
+
+ os_get_time(&entry->last_attempt);
+ buf = radius_msg_get_buf(entry->msg);
+ if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0)
+ radius_client_handle_send_error(radius, s, entry->msg_type);
+
+ entry->next_try = now + entry->next_wait;
+ entry->next_wait *= 2;
+ if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
+ entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
+ if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
+ printf("Removing un-ACKed RADIUS message due to too many "
+ "failed retransmit attempts\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct radius_client_data *radius = eloop_ctx;
+ struct hostapd_radius_servers *conf = radius->conf;
+ struct os_time now;
+ os_time_t first;
+ struct radius_msg_list *entry, *prev, *tmp;
+ int auth_failover = 0, acct_failover = 0;
+ char abuf[50];
+
+ entry = radius->msgs;
+ if (!entry)
+ return;
+
+ os_get_time(&now);
+ first = 0;
+
+ prev = NULL;
+ while (entry) {
+ if (now.sec >= entry->next_try &&
+ radius_client_retransmit(radius, entry, now.sec)) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ radius->msgs = entry->next;
+
+ tmp = entry;
+ entry = entry->next;
+ radius_client_msg_free(tmp);
+ radius->num_msgs--;
+ continue;
+ }
+
+ if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
+ if (entry->msg_type == RADIUS_ACCT ||
+ entry->msg_type == RADIUS_ACCT_INTERIM)
+ acct_failover++;
+ else
+ auth_failover++;
+ }
+
+ if (first == 0 || entry->next_try < first)
+ first = entry->next_try;
+
+ prev = entry;
+ entry = entry->next;
+ }
+
+ if (radius->msgs) {
+ if (first < now.sec)
+ first = now.sec;
+ eloop_register_timeout(first - now.sec, 0,
+ radius_client_timer, radius, NULL);
+ hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
+ "retransmit in %ld seconds",
+ (long int) (first - now.sec));
+ }
+
+ if (auth_failover && conf->num_auth_servers > 1) {
+ struct hostapd_radius_server *next, *old;
+ old = conf->auth_server;
+ hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_NOTICE,
+ "No response from Authentication server "
+ "%s:%d - failover",
+ hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
+ old->port);
+
+ for (entry = radius->msgs; entry; entry = entry->next) {
+ if (entry->msg_type == RADIUS_AUTH)
+ old->timeouts++;
+ }
+
+ next = old + 1;
+ if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
+ next = conf->auth_servers;
+ conf->auth_server = next;
+ radius_change_server(radius, next, old,
+ radius->auth_serv_sock,
+ radius->auth_serv_sock6, 1);
+ }
+
+ if (acct_failover && conf->num_acct_servers > 1) {
+ struct hostapd_radius_server *next, *old;
+ old = conf->acct_server;
+ hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_NOTICE,
+ "No response from Accounting server "
+ "%s:%d - failover",
+ hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
+ old->port);
+
+ for (entry = radius->msgs; entry; entry = entry->next) {
+ if (entry->msg_type == RADIUS_ACCT ||
+ entry->msg_type == RADIUS_ACCT_INTERIM)
+ old->timeouts++;
+ }
+
+ next = old + 1;
+ if (next > &conf->acct_servers[conf->num_acct_servers - 1])
+ next = conf->acct_servers;
+ conf->acct_server = next;
+ radius_change_server(radius, next, old,
+ radius->acct_serv_sock,
+ radius->acct_serv_sock6, 0);
+ }
+}
+
+
+static void radius_client_update_timeout(struct radius_client_data *radius)
+{
+ struct os_time now;
+ os_time_t first;
+ struct radius_msg_list *entry;
+
+ eloop_cancel_timeout(radius_client_timer, radius, NULL);
+
+ if (radius->msgs == NULL) {
+ return;
+ }
+
+ first = 0;
+ for (entry = radius->msgs; entry; entry = entry->next) {
+ if (first == 0 || entry->next_try < first)
+ first = entry->next_try;
+ }
+
+ os_get_time(&now);
+ if (first < now.sec)
+ first = now.sec;
+ eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
+ NULL);
+ hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
+ " %ld seconds\n", (long int) (first - now.sec));
+}
+
+
+static void radius_client_list_add(struct radius_client_data *radius,
+ struct radius_msg *msg,
+ RadiusType msg_type,
+ const u8 *shared_secret,
+ size_t shared_secret_len, const u8 *addr)
+{
+ struct radius_msg_list *entry, *prev;
+
+ if (eloop_terminated()) {
+ /* No point in adding entries to retransmit queue since event
+ * loop has already been terminated. */
+ radius_msg_free(msg);
+ return;
+ }
+
+ entry = os_zalloc(sizeof(*entry));
+ if (entry == NULL) {
+ printf("Failed to add RADIUS packet into retransmit list\n");
+ radius_msg_free(msg);
+ return;
+ }
+
+ if (addr)
+ os_memcpy(entry->addr, addr, ETH_ALEN);
+ entry->msg = msg;
+ entry->msg_type = msg_type;
+ entry->shared_secret = shared_secret;
+ entry->shared_secret_len = shared_secret_len;
+ os_get_time(&entry->last_attempt);
+ entry->first_try = entry->last_attempt.sec;
+ entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
+ entry->attempts = 1;
+ entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
+ entry->next = radius->msgs;
+ radius->msgs = entry;
+ radius_client_update_timeout(radius);
+
+ if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
+ printf("Removing the oldest un-ACKed RADIUS packet due to "
+ "retransmit list limits.\n");
+ prev = NULL;
+ while (entry->next) {
+ prev = entry;
+ entry = entry->next;
+ }
+ if (prev) {
+ prev->next = NULL;
+ radius_client_msg_free(entry);
+ }
+ } else
+ radius->num_msgs++;
+}
+
+
+static void radius_client_list_del(struct radius_client_data *radius,
+ RadiusType msg_type, const u8 *addr)
+{
+ struct radius_msg_list *entry, *prev, *tmp;
+
+ if (addr == NULL)
+ return;
+
+ entry = radius->msgs;
+ prev = NULL;
+ while (entry) {
+ if (entry->msg_type == msg_type &&
+ os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ radius->msgs = entry->next;
+ tmp = entry;
+ entry = entry->next;
+ hostapd_logger(radius->ctx, addr,
+ HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG,
+ "Removing matching RADIUS message");
+ radius_client_msg_free(tmp);
+ radius->num_msgs--;
+ continue;
+ }
+ prev = entry;
+ entry = entry->next;
+ }
+}
+
+
+/**
+ * radius_client_send - Send a RADIUS request
+ * @radius: RADIUS client context from radius_client_init()
+ * @msg: RADIUS message to be sent
+ * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM)
+ * @addr: MAC address of the device related to this message or %NULL
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or
+ * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference
+ * between accounting and interim accounting messages is that the interim
+ * message will override any pending interim accounting updates while a new
+ * accounting message does not remove any pending messages.
+ *
+ * The message is added on the retransmission queue and will be retransmitted
+ * automatically until a response is received or maximum number of retries
+ * (RADIUS_CLIENT_MAX_RETRIES) is reached.
+ *
+ * The related device MAC address can be used to identify pending messages that
+ * can be removed with radius_client_flush_auth() or with interim accounting
+ * updates.
+ */
+int radius_client_send(struct radius_client_data *radius,
+ struct radius_msg *msg, RadiusType msg_type,
+ const u8 *addr)
+{
+ struct hostapd_radius_servers *conf = radius->conf;
+ const u8 *shared_secret;
+ size_t shared_secret_len;
+ char *name;
+ int s, res;
+ struct wpabuf *buf;
+
+ if (msg_type == RADIUS_ACCT_INTERIM) {
+ /* Remove any pending interim acct update for the same STA. */
+ radius_client_list_del(radius, msg_type, addr);
+ }
+
+ if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
+ if (conf->acct_server == NULL) {
+ hostapd_logger(radius->ctx, NULL,
+ HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "No accounting server configured");
+ return -1;
+ }
+ shared_secret = conf->acct_server->shared_secret;
+ shared_secret_len = conf->acct_server->shared_secret_len;
+ radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
+ name = "accounting";
+ s = radius->acct_sock;
+ conf->acct_server->requests++;
+ } else {
+ if (conf->auth_server == NULL) {
+ hostapd_logger(radius->ctx, NULL,
+ HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "No authentication server configured");
+ return -1;
+ }
+ shared_secret = conf->auth_server->shared_secret;
+ shared_secret_len = conf->auth_server->shared_secret_len;
+ radius_msg_finish(msg, shared_secret, shared_secret_len);
+ name = "authentication";
+ s = radius->auth_sock;
+ conf->auth_server->requests++;
+ }
+
+ hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
+ "server", name);
+ if (conf->msg_dumps)
+ radius_msg_dump(msg);
+
+ buf = radius_msg_get_buf(msg);
+ res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
+ if (res < 0)
+ radius_client_handle_send_error(radius, s, msg_type);
+
+ radius_client_list_add(radius, msg, msg_type, shared_secret,
+ shared_secret_len, addr);
+
+ return res;
+}
+
+
+static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct radius_client_data *radius = eloop_ctx;
+ struct hostapd_radius_servers *conf = radius->conf;
+ RadiusType msg_type = (RadiusType) sock_ctx;
+ int len, roundtrip;
+ unsigned char buf[3000];
+ struct radius_msg *msg;
+ struct radius_hdr *hdr;
+ struct radius_rx_handler *handlers;
+ size_t num_handlers, i;
+ struct radius_msg_list *req, *prev_req;
+ struct os_time now;
+ struct hostapd_radius_server *rconf;
+ int invalid_authenticator = 0;
+
+ if (msg_type == RADIUS_ACCT) {
+ handlers = radius->acct_handlers;
+ num_handlers = radius->num_acct_handlers;
+ rconf = conf->acct_server;
+ } else {
+ handlers = radius->auth_handlers;
+ num_handlers = radius->num_auth_handlers;
+ rconf = conf->auth_server;
+ }
+
+ len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
+ if (len < 0) {
+ perror("recv[RADIUS]");
+ return;
+ }
+ hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
+ "server", len);
+ if (len == sizeof(buf)) {
+ printf("Possibly too long UDP frame for our buffer - "
+ "dropping it\n");
+ return;
+ }
+
+ msg = radius_msg_parse(buf, len);
+ if (msg == NULL) {
+ printf("Parsing incoming RADIUS frame failed\n");
+ rconf->malformed_responses++;
+ return;
+ }
+ hdr = radius_msg_get_hdr(msg);
+
+ hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
+ if (conf->msg_dumps)
+ radius_msg_dump(msg);
+
+ switch (hdr->code) {
+ case RADIUS_CODE_ACCESS_ACCEPT:
+ rconf->access_accepts++;
+ break;
+ case RADIUS_CODE_ACCESS_REJECT:
+ rconf->access_rejects++;
+ break;
+ case RADIUS_CODE_ACCESS_CHALLENGE:
+ rconf->access_challenges++;
+ break;
+ case RADIUS_CODE_ACCOUNTING_RESPONSE:
+ rconf->responses++;
+ break;
+ }
+
+ prev_req = NULL;
+ req = radius->msgs;
+ while (req) {
+ /* TODO: also match by src addr:port of the packet when using
+ * alternative RADIUS servers (?) */
+ if ((req->msg_type == msg_type ||
+ (req->msg_type == RADIUS_ACCT_INTERIM &&
+ msg_type == RADIUS_ACCT)) &&
+ radius_msg_get_hdr(req->msg)->identifier ==
+ hdr->identifier)
+ break;
+
+ prev_req = req;
+ req = req->next;
+ }
+
+ if (req == NULL) {
+ hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG,
+ "No matching RADIUS request found (type=%d "
+ "id=%d) - dropping packet",
+ msg_type, hdr->identifier);
+ goto fail;
+ }
+
+ os_get_time(&now);
+ roundtrip = (now.sec - req->last_attempt.sec) * 100 +
+ (now.usec - req->last_attempt.usec) / 10000;
+ hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG,
+ "Received RADIUS packet matched with a pending "
+ "request, round trip time %d.%02d sec",
+ roundtrip / 100, roundtrip % 100);
+ rconf->round_trip_time = roundtrip;
+
+ /* Remove ACKed RADIUS packet from retransmit list */
+ if (prev_req)
+ prev_req->next = req->next;
+ else
+ radius->msgs = req->next;
+ radius->num_msgs--;
+
+ for (i = 0; i < num_handlers; i++) {
+ RadiusRxResult res;
+ res = handlers[i].handler(msg, req->msg, req->shared_secret,
+ req->shared_secret_len,
+ handlers[i].data);
+ switch (res) {
+ case RADIUS_RX_PROCESSED:
+ radius_msg_free(msg);
+ /* continue */
+ case RADIUS_RX_QUEUED:
+ radius_client_msg_free(req);
+ return;
+ case RADIUS_RX_INVALID_AUTHENTICATOR:
+ invalid_authenticator++;
+ /* continue */
+ case RADIUS_RX_UNKNOWN:
+ /* continue with next handler */
+ break;
+ }
+ }
+
+ if (invalid_authenticator)
+ rconf->bad_authenticators++;
+ else
+ rconf->unknown_types++;
+ hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
+ "(type=%d code=%d id=%d)%s - dropping packet",
+ msg_type, hdr->code, hdr->identifier,
+ invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
+ "");
+ radius_client_msg_free(req);
+
+ fail:
+ radius_msg_free(msg);
+}
+
+
+/**
+ * radius_client_get_id - Get an identifier for a new RADIUS message
+ * @radius: RADIUS client context from radius_client_init()
+ * Returns: Allocated identifier
+ *
+ * This function is used to fetch a unique (among pending requests) identifier
+ * for a new RADIUS message.
+ */
+u8 radius_client_get_id(struct radius_client_data *radius)
+{
+ struct radius_msg_list *entry, *prev, *_remove;
+ u8 id = radius->next_radius_identifier++;
+
+ /* remove entries with matching id from retransmit list to avoid
+ * using new reply from the RADIUS server with an old request */
+ entry = radius->msgs;
+ prev = NULL;
+ while (entry) {
+ if (radius_msg_get_hdr(entry->msg)->identifier == id) {
+ hostapd_logger(radius->ctx, entry->addr,
+ HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG,
+ "Removing pending RADIUS message, "
+ "since its id (%d) is reused", id);
+ if (prev)
+ prev->next = entry->next;
+ else
+ radius->msgs = entry->next;
+ _remove = entry;
+ } else {
+ _remove = NULL;
+ prev = entry;
+ }
+ entry = entry->next;
+
+ if (_remove)
+ radius_client_msg_free(_remove);
+ }
+
+ return id;
+}
+
+
+/**
+ * radius_client_flush - Flush all pending RADIUS client messages
+ * @radius: RADIUS client context from radius_client_init()
+ * @only_auth: Whether only authentication messages are removed
+ */
+void radius_client_flush(struct radius_client_data *radius, int only_auth)
+{
+ struct radius_msg_list *entry, *prev, *tmp;
+
+ if (!radius)
+ return;
+
+ prev = NULL;
+ entry = radius->msgs;
+
+ while (entry) {
+ if (!only_auth || entry->msg_type == RADIUS_AUTH) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ radius->msgs = entry->next;
+
+ tmp = entry;
+ entry = entry->next;
+ radius_client_msg_free(tmp);
+ radius->num_msgs--;
+ } else {
+ prev = entry;
+ entry = entry->next;
+ }
+ }
+
+ if (radius->msgs == NULL)
+ eloop_cancel_timeout(radius_client_timer, radius, NULL);
+}
+
+
+static void radius_client_update_acct_msgs(struct radius_client_data *radius,
+ const u8 *shared_secret,
+ size_t shared_secret_len)
+{
+ struct radius_msg_list *entry;
+
+ if (!radius)
+ return;
+
+ for (entry = radius->msgs; entry; entry = entry->next) {
+ if (entry->msg_type == RADIUS_ACCT) {
+ entry->shared_secret = shared_secret;
+ entry->shared_secret_len = shared_secret_len;
+ radius_msg_finish_acct(entry->msg, shared_secret,
+ shared_secret_len);
+ }
+ }
+}
+
+
+static int
+radius_change_server(struct radius_client_data *radius,
+ struct hostapd_radius_server *nserv,
+ struct hostapd_radius_server *oserv,
+ int sock, int sock6, int auth)
+{
+ struct sockaddr_in serv, claddr;
+#ifdef CONFIG_IPV6
+ struct sockaddr_in6 serv6, claddr6;
+#endif /* CONFIG_IPV6 */
+ struct sockaddr *addr, *cl_addr;
+ socklen_t addrlen, claddrlen;
+ char abuf[50];
+ int sel_sock;
+ struct radius_msg_list *entry;
+ struct hostapd_radius_servers *conf = radius->conf;
+
+ hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "%s server %s:%d",
+ auth ? "Authentication" : "Accounting",
+ hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
+ nserv->port);
+
+ if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
+ os_memcmp(nserv->shared_secret, oserv->shared_secret,
+ nserv->shared_secret_len) != 0) {
+ /* Pending RADIUS packets used different shared secret, so
+ * they need to be modified. Update accounting message
+ * authenticators here. Authentication messages are removed
+ * since they would require more changes and the new RADIUS
+ * server may not be prepared to receive them anyway due to
+ * missing state information. Client will likely retry
+ * authentication, so this should not be an issue. */
+ if (auth)
+ radius_client_flush(radius, 1);
+ else {
+ radius_client_update_acct_msgs(
+ radius, nserv->shared_secret,
+ nserv->shared_secret_len);
+ }
+ }
+
+ /* Reset retry counters for the new server */
+ for (entry = radius->msgs; entry; entry = entry->next) {
+ if ((auth && entry->msg_type != RADIUS_AUTH) ||
+ (!auth && entry->msg_type != RADIUS_ACCT))
+ continue;
+ entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
+ entry->attempts = 0;
+ entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
+ }
+
+ if (radius->msgs) {
+ eloop_cancel_timeout(radius_client_timer, radius, NULL);
+ eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
+ radius_client_timer, radius, NULL);
+ }
+
+ switch (nserv->addr.af) {
+ case AF_INET:
+ os_memset(&serv, 0, sizeof(serv));
+ serv.sin_family = AF_INET;
+ serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
+ serv.sin_port = htons(nserv->port);
+ addr = (struct sockaddr *) &serv;
+ addrlen = sizeof(serv);
+ sel_sock = sock;
+ break;
+#ifdef CONFIG_IPV6
+ case AF_INET6:
+ os_memset(&serv6, 0, sizeof(serv6));
+ serv6.sin6_family = AF_INET6;
+ os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
+ sizeof(struct in6_addr));
+ serv6.sin6_port = htons(nserv->port);
+ addr = (struct sockaddr *) &serv6;
+ addrlen = sizeof(serv6);
+ sel_sock = sock6;
+ break;
+#endif /* CONFIG_IPV6 */
+ default:
+ return -1;
+ }
+
+ if (conf->force_client_addr) {
+ switch (conf->client_addr.af) {
+ case AF_INET:
+ os_memset(&claddr, 0, sizeof(claddr));
+ claddr.sin_family = AF_INET;
+ claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
+ claddr.sin_port = htons(0);
+ cl_addr = (struct sockaddr *) &claddr;
+ claddrlen = sizeof(claddr);
+ break;
+#ifdef CONFIG_IPV6
+ case AF_INET6:
+ os_memset(&claddr6, 0, sizeof(claddr6));
+ claddr6.sin6_family = AF_INET6;
+ os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
+ sizeof(struct in6_addr));
+ claddr6.sin6_port = htons(0);
+ cl_addr = (struct sockaddr *) &claddr6;
+ claddrlen = sizeof(claddr6);
+ break;
+#endif /* CONFIG_IPV6 */
+ default:
+ return -1;
+ }
+
+ if (bind(sel_sock, cl_addr, claddrlen) < 0) {
+ perror("bind[radius]");
+ return -1;
+ }
+ }
+
+ if (connect(sel_sock, addr, addrlen) < 0) {
+ perror("connect[radius]");
+ return -1;
+ }
+
+#ifndef CONFIG_NATIVE_WINDOWS
+ switch (nserv->addr.af) {
+ case AF_INET:
+ claddrlen = sizeof(claddr);
+ getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen);
+ wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+ inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port));
+ break;
+#ifdef CONFIG_IPV6
+ case AF_INET6: {
+ claddrlen = sizeof(claddr6);
+ getsockname(sel_sock, (struct sockaddr *) &claddr6,
+ &claddrlen);
+ wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+ inet_ntop(AF_INET6, &claddr6.sin6_addr,
+ abuf, sizeof(abuf)),
+ ntohs(claddr6.sin6_port));
+ break;
+ }
+#endif /* CONFIG_IPV6 */
+ }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ if (auth)
+ radius->auth_sock = sel_sock;
+ else
+ radius->acct_sock = sel_sock;
+
+ return 0;
+}
+
+
+static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct radius_client_data *radius = eloop_ctx;
+ struct hostapd_radius_servers *conf = radius->conf;
+ struct hostapd_radius_server *oserv;
+
+ if (radius->auth_sock >= 0 && conf->auth_servers &&
+ conf->auth_server != conf->auth_servers) {
+ oserv = conf->auth_server;
+ conf->auth_server = conf->auth_servers;
+ radius_change_server(radius, conf->auth_server, oserv,
+ radius->auth_serv_sock,
+ radius->auth_serv_sock6, 1);
+ }
+
+ if (radius->acct_sock >= 0 && conf->acct_servers &&
+ conf->acct_server != conf->acct_servers) {
+ oserv = conf->acct_server;
+ conf->acct_server = conf->acct_servers;
+ radius_change_server(radius, conf->acct_server, oserv,
+ radius->acct_serv_sock,
+ radius->acct_serv_sock6, 0);
+ }
+
+ if (conf->retry_primary_interval)
+ eloop_register_timeout(conf->retry_primary_interval, 0,
+ radius_retry_primary_timer, radius,
+ NULL);
+}
+
+
+static int radius_client_disable_pmtu_discovery(int s)
+{
+ int r = -1;
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+ /* Turn off Path MTU discovery on IPv4/UDP sockets. */
+ int action = IP_PMTUDISC_DONT;
+ r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
+ sizeof(action));
+ if (r == -1)
+ wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
+ "%s", strerror(errno));
+#endif
+ return r;
+}
+
+
+static int radius_client_init_auth(struct radius_client_data *radius)
+{
+ struct hostapd_radius_servers *conf = radius->conf;
+ int ok = 0;
+
+ radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (radius->auth_serv_sock < 0)
+ perror("socket[PF_INET,SOCK_DGRAM]");
+ else {
+ radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
+ ok++;
+ }
+
+#ifdef CONFIG_IPV6
+ radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (radius->auth_serv_sock6 < 0)
+ perror("socket[PF_INET6,SOCK_DGRAM]");
+ else
+ ok++;
+#endif /* CONFIG_IPV6 */
+
+ if (ok == 0)
+ return -1;
+
+ radius_change_server(radius, conf->auth_server, NULL,
+ radius->auth_serv_sock, radius->auth_serv_sock6,
+ 1);
+
+ if (radius->auth_serv_sock >= 0 &&
+ eloop_register_read_sock(radius->auth_serv_sock,
+ radius_client_receive, radius,
+ (void *) RADIUS_AUTH)) {
+ printf("Could not register read socket for authentication "
+ "server\n");
+ return -1;
+ }
+
+#ifdef CONFIG_IPV6
+ if (radius->auth_serv_sock6 >= 0 &&
+ eloop_register_read_sock(radius->auth_serv_sock6,
+ radius_client_receive, radius,
+ (void *) RADIUS_AUTH)) {
+ printf("Could not register read socket for authentication "
+ "server\n");
+ return -1;
+ }
+#endif /* CONFIG_IPV6 */
+
+ return 0;
+}
+
+
+static int radius_client_init_acct(struct radius_client_data *radius)
+{
+ struct hostapd_radius_servers *conf = radius->conf;
+ int ok = 0;
+
+ radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (radius->acct_serv_sock < 0)
+ perror("socket[PF_INET,SOCK_DGRAM]");
+ else {
+ radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
+ ok++;
+ }
+
+#ifdef CONFIG_IPV6
+ radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (radius->acct_serv_sock6 < 0)
+ perror("socket[PF_INET6,SOCK_DGRAM]");
+ else
+ ok++;
+#endif /* CONFIG_IPV6 */
+
+ if (ok == 0)
+ return -1;
+
+ radius_change_server(radius, conf->acct_server, NULL,
+ radius->acct_serv_sock, radius->acct_serv_sock6,
+ 0);
+
+ if (radius->acct_serv_sock >= 0 &&
+ eloop_register_read_sock(radius->acct_serv_sock,
+ radius_client_receive, radius,
+ (void *) RADIUS_ACCT)) {
+ printf("Could not register read socket for accounting "
+ "server\n");
+ return -1;
+ }
+
+#ifdef CONFIG_IPV6
+ if (radius->acct_serv_sock6 >= 0 &&
+ eloop_register_read_sock(radius->acct_serv_sock6,
+ radius_client_receive, radius,
+ (void *) RADIUS_ACCT)) {
+ printf("Could not register read socket for accounting "
+ "server\n");
+ return -1;
+ }
+#endif /* CONFIG_IPV6 */
+
+ return 0;
+}
+
+
+/**
+ * radius_client_init - Initialize RADIUS client
+ * @ctx: Callback context to be used in hostapd_logger() calls
+ * @conf: RADIUS client configuration (RADIUS servers)
+ * Returns: Pointer to private RADIUS client context or %NULL on failure
+ *
+ * The caller is responsible for keeping the configuration data available for
+ * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is
+ * called for the returned context pointer.
+ */
+struct radius_client_data *
+radius_client_init(void *ctx, struct hostapd_radius_servers *conf)
+{
+ struct radius_client_data *radius;
+
+ radius = os_zalloc(sizeof(struct radius_client_data));
+ if (radius == NULL)
+ return NULL;
+
+ radius->ctx = ctx;
+ radius->conf = conf;
+ radius->auth_serv_sock = radius->acct_serv_sock =
+ radius->auth_serv_sock6 = radius->acct_serv_sock6 =
+ radius->auth_sock = radius->acct_sock = -1;
+
+ if (conf->auth_server && radius_client_init_auth(radius)) {
+ radius_client_deinit(radius);
+ return NULL;
+ }
+
+ if (conf->acct_server && radius_client_init_acct(radius)) {
+ radius_client_deinit(radius);
+ return NULL;
+ }
+
+ if (conf->retry_primary_interval)
+ eloop_register_timeout(conf->retry_primary_interval, 0,
+ radius_retry_primary_timer, radius,
+ NULL);
+
+ return radius;
+}
+
+
+/**
+ * radius_client_deinit - Deinitialize RADIUS client
+ * @radius: RADIUS client context from radius_client_init()
+ */
+void radius_client_deinit(struct radius_client_data *radius)
+{
+ if (!radius)
+ return;
+
+ if (radius->auth_serv_sock >= 0)
+ eloop_unregister_read_sock(radius->auth_serv_sock);
+ if (radius->acct_serv_sock >= 0)
+ eloop_unregister_read_sock(radius->acct_serv_sock);
+#ifdef CONFIG_IPV6
+ if (radius->auth_serv_sock6 >= 0)
+ eloop_unregister_read_sock(radius->auth_serv_sock6);
+ if (radius->acct_serv_sock6 >= 0)
+ eloop_unregister_read_sock(radius->acct_serv_sock6);
+#endif /* CONFIG_IPV6 */
+
+ eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
+
+ radius_client_flush(radius, 0);
+ os_free(radius->auth_handlers);
+ os_free(radius->acct_handlers);
+ os_free(radius);
+}
+
+
+/**
+ * radius_client_flush_auth - Flush pending RADIUS messages for an address
+ * @radius: RADIUS client context from radius_client_init()
+ * @addr: MAC address of the related device
+ *
+ * This function can be used to remove pending RADIUS authentication messages
+ * that are related to a specific device. The addr parameter is matched with
+ * the one used in radius_client_send() call that was used to transmit the
+ * authentication request.
+ */
+void radius_client_flush_auth(struct radius_client_data *radius,
+ const u8 *addr)
+{
+ struct radius_msg_list *entry, *prev, *tmp;
+
+ prev = NULL;
+ entry = radius->msgs;
+ while (entry) {
+ if (entry->msg_type == RADIUS_AUTH &&
+ os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
+ hostapd_logger(radius->ctx, addr,
+ HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_DEBUG,
+ "Removing pending RADIUS authentication"
+ " message for removed client");
+
+ if (prev)
+ prev->next = entry->next;
+ else
+ radius->msgs = entry->next;
+
+ tmp = entry;
+ entry = entry->next;
+ radius_client_msg_free(tmp);
+ radius->num_msgs--;
+ continue;
+ }
+
+ prev = entry;
+ entry = entry->next;
+ }
+}
+
+
+static int radius_client_dump_auth_server(char *buf, size_t buflen,
+ struct hostapd_radius_server *serv,
+ struct radius_client_data *cli)
+{
+ int pending = 0;
+ struct radius_msg_list *msg;
+ char abuf[50];
+
+ if (cli) {
+ for (msg = cli->msgs; msg; msg = msg->next) {
+ if (msg->msg_type == RADIUS_AUTH)
+ pending++;
+ }
+ }
+
+ return os_snprintf(buf, buflen,
+ "radiusAuthServerIndex=%d\n"
+ "radiusAuthServerAddress=%s\n"
+ "radiusAuthClientServerPortNumber=%d\n"
+ "radiusAuthClientRoundTripTime=%d\n"
+ "radiusAuthClientAccessRequests=%u\n"
+ "radiusAuthClientAccessRetransmissions=%u\n"
+ "radiusAuthClientAccessAccepts=%u\n"
+ "radiusAuthClientAccessRejects=%u\n"
+ "radiusAuthClientAccessChallenges=%u\n"
+ "radiusAuthClientMalformedAccessResponses=%u\n"
+ "radiusAuthClientBadAuthenticators=%u\n"
+ "radiusAuthClientPendingRequests=%u\n"
+ "radiusAuthClientTimeouts=%u\n"
+ "radiusAuthClientUnknownTypes=%u\n"
+ "radiusAuthClientPacketsDropped=%u\n",
+ serv->index,
+ hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
+ serv->port,
+ serv->round_trip_time,
+ serv->requests,
+ serv->retransmissions,
+ serv->access_accepts,
+ serv->access_rejects,
+ serv->access_challenges,
+ serv->malformed_responses,
+ serv->bad_authenticators,
+ pending,
+ serv->timeouts,
+ serv->unknown_types,
+ serv->packets_dropped);
+}
+
+
+static int radius_client_dump_acct_server(char *buf, size_t buflen,
+ struct hostapd_radius_server *serv,
+ struct radius_client_data *cli)
+{
+ int pending = 0;
+ struct radius_msg_list *msg;
+ char abuf[50];
+
+ if (cli) {
+ for (msg = cli->msgs; msg; msg = msg->next) {
+ if (msg->msg_type == RADIUS_ACCT ||
+ msg->msg_type == RADIUS_ACCT_INTERIM)
+ pending++;
+ }
+ }
+
+ return os_snprintf(buf, buflen,
+ "radiusAccServerIndex=%d\n"
+ "radiusAccServerAddress=%s\n"
+ "radiusAccClientServerPortNumber=%d\n"
+ "radiusAccClientRoundTripTime=%d\n"
+ "radiusAccClientRequests=%u\n"
+ "radiusAccClientRetransmissions=%u\n"
+ "radiusAccClientResponses=%u\n"
+ "radiusAccClientMalformedResponses=%u\n"
+ "radiusAccClientBadAuthenticators=%u\n"
+ "radiusAccClientPendingRequests=%u\n"
+ "radiusAccClientTimeouts=%u\n"
+ "radiusAccClientUnknownTypes=%u\n"
+ "radiusAccClientPacketsDropped=%u\n",
+ serv->index,
+ hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
+ serv->port,
+ serv->round_trip_time,
+ serv->requests,
+ serv->retransmissions,
+ serv->responses,
+ serv->malformed_responses,
+ serv->bad_authenticators,
+ pending,
+ serv->timeouts,
+ serv->unknown_types,
+ serv->packets_dropped);
+}
+
+
+/**
+ * radius_client_get_mib - Get RADIUS client MIB information
+ * @radius: RADIUS client context from radius_client_init()
+ * @buf: Buffer for returning MIB data in text format
+ * @buflen: Maximum buf length in octets
+ * Returns: Number of octets written into the buffer
+ */
+int radius_client_get_mib(struct radius_client_data *radius, char *buf,
+ size_t buflen)
+{
+ struct hostapd_radius_servers *conf = radius->conf;
+ int i;
+ struct hostapd_radius_server *serv;
+ int count = 0;
+
+ if (conf->auth_servers) {
+ for (i = 0; i < conf->num_auth_servers; i++) {
+ serv = &conf->auth_servers[i];
+ count += radius_client_dump_auth_server(
+ buf + count, buflen - count, serv,
+ serv == conf->auth_server ?
+ radius : NULL);
+ }
+ }
+
+ if (conf->acct_servers) {
+ for (i = 0; i < conf->num_acct_servers; i++) {
+ serv = &conf->acct_servers[i];
+ count += radius_client_dump_acct_server(
+ buf + count, buflen - count, serv,
+ serv == conf->acct_server ?
+ radius : NULL);
+ }
+ }
+
+ return count;
+}
+
+
+void radius_client_reconfig(struct radius_client_data *radius,
+ struct hostapd_radius_servers *conf)
+{
+ if (radius)
+ radius->conf = conf;
+}
diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
new file mode 100644
index 0000000..18e7290
--- /dev/null
+++ b/src/radius/radius_client.h
@@ -0,0 +1,265 @@
+/*
+ * RADIUS client
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef RADIUS_CLIENT_H
+#define RADIUS_CLIENT_H
+
+#include "ip_addr.h"
+
+struct radius_msg;
+
+/**
+ * struct hostapd_radius_server - RADIUS server information for RADIUS client
+ *
+ * This structure contains information about a RADIUS server. The values are
+ * mainly for MIB information. The MIB variable prefix (radiusAuth or
+ * radiusAcc) depends on whether this is an authentication or accounting
+ * server.
+ *
+ * radiusAuthClientPendingRequests (or radiusAccClientPendingRequests) is the
+ * number struct radius_client_data::msgs for matching msg_type.
+ */
+struct hostapd_radius_server {
+ /**
+ * addr - radiusAuthServerAddress or radiusAccServerAddress
+ */
+ struct hostapd_ip_addr addr;
+
+ /**
+ * port - radiusAuthClientServerPortNumber or radiusAccClientServerPortNumber
+ */
+ int port;
+
+ /**
+ * shared_secret - Shared secret for authenticating RADIUS messages
+ */
+ u8 *shared_secret;
+
+ /**
+ * shared_secret_len - Length of shared_secret in octets
+ */
+ size_t shared_secret_len;
+
+ /* Dynamic (not from configuration file) MIB data */
+
+ /**
+ * index - radiusAuthServerIndex or radiusAccServerIndex
+ */
+ int index;
+
+ /**
+ * round_trip_time - radiusAuthClientRoundTripTime or radiusAccClientRoundTripTime
+ * Round-trip time in hundredths of a second.
+ */
+ int round_trip_time;
+
+ /**
+ * requests - radiusAuthClientAccessRequests or radiusAccClientRequests
+ */
+ u32 requests;
+
+ /**
+ * retransmissions - radiusAuthClientAccessRetransmissions or radiusAccClientRetransmissions
+ */
+ u32 retransmissions;
+
+ /**
+ * access_accepts - radiusAuthClientAccessAccepts
+ */
+ u32 access_accepts;
+
+ /**
+ * access_rejects - radiusAuthClientAccessRejects
+ */
+ u32 access_rejects;
+
+ /**
+ * access_challenges - radiusAuthClientAccessChallenges
+ */
+ u32 access_challenges;
+
+ /**
+ * responses - radiusAccClientResponses
+ */
+ u32 responses;
+
+ /**
+ * malformed_responses - radiusAuthClientMalformedAccessResponses or radiusAccClientMalformedResponses
+ */
+ u32 malformed_responses;
+
+ /**
+ * bad_authenticators - radiusAuthClientBadAuthenticators or radiusAccClientBadAuthenticators
+ */
+ u32 bad_authenticators;
+
+ /**
+ * timeouts - radiusAuthClientTimeouts or radiusAccClientTimeouts
+ */
+ u32 timeouts;
+
+ /**
+ * unknown_types - radiusAuthClientUnknownTypes or radiusAccClientUnknownTypes
+ */
+ u32 unknown_types;
+
+ /**
+ * packets_dropped - radiusAuthClientPacketsDropped or radiusAccClientPacketsDropped
+ */
+ u32 packets_dropped;
+};
+
+/**
+ * struct hostapd_radius_servers - RADIUS servers for RADIUS client
+ */
+struct hostapd_radius_servers {
+ /**
+ * auth_servers - RADIUS Authentication servers in priority order
+ */
+ struct hostapd_radius_server *auth_servers;
+
+ /**
+ * num_auth_servers - Number of auth_servers entries
+ */
+ int num_auth_servers;
+
+ /**
+ * auth_server - The current Authentication server
+ */
+ struct hostapd_radius_server *auth_server;
+
+ /**
+ * acct_servers - RADIUS Accounting servers in priority order
+ */
+ struct hostapd_radius_server *acct_servers;
+
+ /**
+ * num_acct_servers - Number of acct_servers entries
+ */
+ int num_acct_servers;
+
+ /**
+ * acct_server - The current Accounting server
+ */
+ struct hostapd_radius_server *acct_server;
+
+ /**
+ * retry_primary_interval - Retry interval for trying primary server
+ *
+ * This specifies a retry interval in sexconds for trying to return to
+ * the primary RADIUS server. RADIUS client code will automatically try
+ * to use the next server when the current server is not replying to
+ * requests. If this interval is set (non-zero), the primary server
+ * will be retried after the specified number of seconds has passed
+ * even if the current used secondary server is still working.
+ */
+ int retry_primary_interval;
+
+ /**
+ * msg_dumps - Whether RADIUS message details are shown in stdout
+ */
+ int msg_dumps;
+
+ /**
+ * client_addr - Client (local) address to use if force_client_addr
+ */
+ struct hostapd_ip_addr client_addr;
+
+ /**
+ * force_client_addr - Whether to force client (local) address
+ */
+ int force_client_addr;
+};
+
+
+/**
+ * RadiusType - RADIUS server type for RADIUS client
+ */
+typedef enum {
+ /**
+ * RADIUS authentication
+ */
+ RADIUS_AUTH,
+
+ /**
+ * RADIUS_ACCT - RADIUS accounting
+ */
+ RADIUS_ACCT,
+
+ /**
+ * RADIUS_ACCT_INTERIM - RADIUS interim accounting message
+ *
+ * Used only with radius_client_send(). This behaves just like
+ * RADIUS_ACCT, but removes any pending interim RADIUS Accounting
+ * messages for the same STA before sending the new interim update.
+ */
+ RADIUS_ACCT_INTERIM
+} RadiusType;
+
+/**
+ * RadiusRxResult - RADIUS client RX handler result
+ */
+typedef enum {
+ /**
+ * RADIUS_RX_PROCESSED - Message processed
+ *
+ * This stops handler calls and frees the message.
+ */
+ RADIUS_RX_PROCESSED,
+
+ /**
+ * RADIUS_RX_QUEUED - Message has been queued
+ *
+ * This stops handler calls, but does not free the message; the handler
+ * that returned this is responsible for eventually freeing the
+ * message.
+ */
+ RADIUS_RX_QUEUED,
+
+ /**
+ * RADIUS_RX_UNKNOWN - Message is not for this handler
+ */
+ RADIUS_RX_UNKNOWN,
+
+ /**
+ * RADIUS_RX_INVALID_AUTHENTICATOR - Message has invalid Authenticator
+ */
+ RADIUS_RX_INVALID_AUTHENTICATOR
+} RadiusRxResult;
+
+struct radius_client_data;
+
+int radius_client_register(struct radius_client_data *radius,
+ RadiusType msg_type,
+ RadiusRxResult (*handler)
+ (struct radius_msg *msg, struct radius_msg *req,
+ const u8 *shared_secret, size_t shared_secret_len,
+ void *data),
+ void *data);
+int radius_client_send(struct radius_client_data *radius,
+ struct radius_msg *msg,
+ RadiusType msg_type, const u8 *addr);
+u8 radius_client_get_id(struct radius_client_data *radius);
+void radius_client_flush(struct radius_client_data *radius, int only_auth);
+struct radius_client_data *
+radius_client_init(void *ctx, struct hostapd_radius_servers *conf);
+void radius_client_deinit(struct radius_client_data *radius);
+void radius_client_flush_auth(struct radius_client_data *radius,
+ const u8 *addr);
+int radius_client_get_mib(struct radius_client_data *radius, char *buf,
+ size_t buflen);
+void radius_client_reconfig(struct radius_client_data *radius,
+ struct hostapd_radius_servers *conf);
+
+#endif /* RADIUS_CLIENT_H */
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
new file mode 100644
index 0000000..6f1c3a5
--- /dev/null
+++ b/src/radius/radius_server.c
@@ -0,0 +1,1527 @@
+/*
+ * RADIUS authentication server
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <net/if.h>
+
+#include "common.h"
+#include "radius.h"
+#include "eloop.h"
+#include "eap_server/eap.h"
+#include "radius_server.h"
+
+/**
+ * RADIUS_SESSION_TIMEOUT - Session timeout in seconds
+ */
+#define RADIUS_SESSION_TIMEOUT 60
+
+/**
+ * RADIUS_MAX_SESSION - Maximum number of active sessions
+ */
+#define RADIUS_MAX_SESSION 100
+
+/**
+ * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages
+ */
+#define RADIUS_MAX_MSG_LEN 3000
+
+static struct eapol_callbacks radius_server_eapol_cb;
+
+struct radius_client;
+struct radius_server_data;
+
+/**
+ * struct radius_server_counters - RADIUS server statistics counters
+ */
+struct radius_server_counters {
+ u32 access_requests;
+ u32 invalid_requests;
+ u32 dup_access_requests;
+ u32 access_accepts;
+ u32 access_rejects;
+ u32 access_challenges;
+ u32 malformed_access_requests;
+ u32 bad_authenticators;
+ u32 packets_dropped;
+ u32 unknown_types;
+};
+
+/**
+ * struct radius_session - Internal RADIUS server data for a session
+ */
+struct radius_session {
+ struct radius_session *next;
+ struct radius_client *client;
+ struct radius_server_data *server;
+ unsigned int sess_id;
+ struct eap_sm *eap;
+ struct eap_eapol_interface *eap_if;
+
+ struct radius_msg *last_msg;
+ char *last_from_addr;
+ int last_from_port;
+ struct sockaddr_storage last_from;
+ socklen_t last_fromlen;
+ u8 last_identifier;
+ struct radius_msg *last_reply;
+ u8 last_authenticator[16];
+};
+
+/**
+ * struct radius_client - Internal RADIUS server data for a client
+ */
+struct radius_client {
+ struct radius_client *next;
+ struct in_addr addr;
+ struct in_addr mask;
+#ifdef CONFIG_IPV6
+ struct in6_addr addr6;
+ struct in6_addr mask6;
+#endif /* CONFIG_IPV6 */
+ char *shared_secret;
+ int shared_secret_len;
+ struct radius_session *sessions;
+ struct radius_server_counters counters;
+};
+
+/**
+ * struct radius_server_data - Internal RADIUS server data
+ */
+struct radius_server_data {
+ /**
+ * auth_sock - Socket for RADIUS authentication messages
+ */
+ int auth_sock;
+
+ /**
+ * clients - List of authorized RADIUS clients
+ */
+ struct radius_client *clients;
+
+ /**
+ * next_sess_id - Next session identifier
+ */
+ unsigned int next_sess_id;
+
+ /**
+ * conf_ctx - Context pointer for callbacks
+ *
+ * This is used as the ctx argument in get_eap_user() calls.
+ */
+ void *conf_ctx;
+
+ /**
+ * num_sess - Number of active sessions
+ */
+ int num_sess;
+
+ /**
+ * eap_sim_db_priv - EAP-SIM/AKA database context
+ *
+ * This is passed to the EAP-SIM/AKA server implementation as a
+ * callback context.
+ */
+ void *eap_sim_db_priv;
+
+ /**
+ * ssl_ctx - TLS context
+ *
+ * This is passed to the EAP server implementation as a callback
+ * context for TLS operations.
+ */
+ void *ssl_ctx;
+
+ /**
+ * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
+ *
+ * This parameter is used to set a key for EAP-FAST to encrypt the
+ * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
+ * set, must point to a 16-octet key.
+ */
+ u8 *pac_opaque_encr_key;
+
+ /**
+ * eap_fast_a_id - EAP-FAST authority identity (A-ID)
+ *
+ * If EAP-FAST is not used, this can be set to %NULL. In theory, this
+ * is a variable length field, but due to some existing implementations
+ * requiring A-ID to be 16 octets in length, it is recommended to use
+ * that length for the field to provide interoperability with deployed
+ * peer implementations.
+ */
+ u8 *eap_fast_a_id;
+
+ /**
+ * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
+ */
+ size_t eap_fast_a_id_len;
+
+ /**
+ * eap_fast_a_id_info - EAP-FAST authority identifier information
+ *
+ * This A-ID-Info contains a user-friendly name for the A-ID. For
+ * example, this could be the enterprise and server names in
+ * human-readable format. This field is encoded as UTF-8. If EAP-FAST
+ * is not used, this can be set to %NULL.
+ */
+ char *eap_fast_a_id_info;
+
+ /**
+ * eap_fast_prov - EAP-FAST provisioning modes
+ *
+ * 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
+ * 2 = only authenticated provisioning allowed, 3 = both provisioning
+ * modes allowed.
+ */
+ int eap_fast_prov;
+
+ /**
+ * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
+ *
+ * This is the hard limit on how long a provisioned PAC-Key can be
+ * used.
+ */
+ int pac_key_lifetime;
+
+ /**
+ * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
+ *
+ * This is a soft limit on the PAC-Key. The server will automatically
+ * generate a new PAC-Key when this number of seconds (or fewer) of the
+ * lifetime remains.
+ */
+ int pac_key_refresh_time;
+
+ /**
+ * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
+ *
+ * This controls whether the protected success/failure indication
+ * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
+ */
+ int eap_sim_aka_result_ind;
+
+ /**
+ * tnc - Trusted Network Connect (TNC)
+ *
+ * This controls whether TNC is enabled and will be required before the
+ * peer is allowed to connect. Note: This is only used with EAP-TTLS
+ * and EAP-FAST. If any other EAP method is enabled, the peer will be
+ * allowed to connect without TNC.
+ */
+ int tnc;
+
+ /**
+ * 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;
+
+ /**
+ * wps - Wi-Fi Protected Setup context
+ *
+ * If WPS is used with an external RADIUS server (which is quite
+ * unlikely configuration), this is used to provide a pointer to WPS
+ * context data. Normally, this can be set to %NULL.
+ */
+ struct wps_context *wps;
+
+ /**
+ * ipv6 - Whether to enable IPv6 support in the RADIUS server
+ */
+ int ipv6;
+
+ /**
+ * start_time - Timestamp of server start
+ */
+ struct os_time start_time;
+
+ /**
+ * counters - Statistics counters for server operations
+ *
+ * These counters are the sum over all clients.
+ */
+ struct radius_server_counters counters;
+
+ /**
+ * get_eap_user - Callback for fetching EAP user information
+ * @ctx: Context data from conf_ctx
+ * @identity: User identity
+ * @identity_len: identity buffer length in octets
+ * @phase2: Whether this is for Phase 2 identity
+ * @user: Data structure for filling in the user information
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is used to fetch information from user database. The callback
+ * will fill in information about allowed EAP methods and the user
+ * password. The password field will be an allocated copy of the
+ * password data and RADIUS server will free it after use.
+ */
+ int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
+ int phase2, struct eap_user *user);
+
+ /**
+ * eap_req_id_text - Optional data for EAP-Request/Identity
+ *
+ * This can be used to configure an optional, displayable message that
+ * will be sent in EAP-Request/Identity. This string can contain an
+ * ASCII-0 character (nul) to separate network infromation per RFC
+ * 4284. The actual string length is explicit provided in
+ * eap_req_id_text_len since nul character will not be used as a string
+ * terminator.
+ */
+ char *eap_req_id_text;
+
+ /**
+ * eap_req_id_text_len - Length of eap_req_id_text buffer in octets
+ */
+ size_t eap_req_id_text_len;
+
+ /*
+ * msg_ctx - Context data for wpa_msg() calls
+ */
+ void *msg_ctx;
+};
+
+
+extern int wpa_debug_level;
+
+#define RADIUS_DEBUG(args...) \
+wpa_printf(MSG_DEBUG, "RADIUS SRV: " args)
+#define RADIUS_ERROR(args...) \
+wpa_printf(MSG_ERROR, "RADIUS SRV: " args)
+#define RADIUS_DUMP(args...) \
+wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args)
+#define RADIUS_DUMP_ASCII(args...) \
+wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args)
+
+
+static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx);
+static void radius_server_session_remove_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+
+
+static struct radius_client *
+radius_server_get_client(struct radius_server_data *data, struct in_addr *addr,
+ int ipv6)
+{
+ struct radius_client *client = data->clients;
+
+ while (client) {
+#ifdef CONFIG_IPV6
+ if (ipv6) {
+ struct in6_addr *addr6;
+ int i;
+
+ addr6 = (struct in6_addr *) addr;
+ for (i = 0; i < 16; i++) {
+ if ((addr6->s6_addr[i] &
+ client->mask6.s6_addr[i]) !=
+ (client->addr6.s6_addr[i] &
+ client->mask6.s6_addr[i])) {
+ i = 17;
+ break;
+ }
+ }
+ if (i == 16) {
+ break;
+ }
+ }
+#endif /* CONFIG_IPV6 */
+ if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) ==
+ (addr->s_addr & client->mask.s_addr)) {
+ break;
+ }
+
+ client = client->next;
+ }
+
+ return client;
+}
+
+
+static struct radius_session *
+radius_server_get_session(struct radius_client *client, unsigned int sess_id)
+{
+ struct radius_session *sess = client->sessions;
+
+ while (sess) {
+ if (sess->sess_id == sess_id) {
+ break;
+ }
+ sess = sess->next;
+ }
+
+ return sess;
+}
+
+
+static void radius_server_session_free(struct radius_server_data *data,
+ struct radius_session *sess)
+{
+ eloop_cancel_timeout(radius_server_session_timeout, data, sess);
+ eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
+ eap_server_sm_deinit(sess->eap);
+ radius_msg_free(sess->last_msg);
+ os_free(sess->last_from_addr);
+ radius_msg_free(sess->last_reply);
+ os_free(sess);
+ data->num_sess--;
+}
+
+
+static void radius_server_session_remove(struct radius_server_data *data,
+ struct radius_session *sess)
+{
+ struct radius_client *client = sess->client;
+ struct radius_session *session, *prev;
+
+ eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
+
+ prev = NULL;
+ session = client->sessions;
+ while (session) {
+ if (session == sess) {
+ if (prev == NULL) {
+ client->sessions = sess->next;
+ } else {
+ prev->next = sess->next;
+ }
+ radius_server_session_free(data, sess);
+ break;
+ }
+ prev = session;
+ session = session->next;
+ }
+}
+
+
+static void radius_server_session_remove_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct radius_server_data *data = eloop_ctx;
+ struct radius_session *sess = timeout_ctx;
+ RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id);
+ radius_server_session_remove(data, sess);
+}
+
+
+static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct radius_server_data *data = eloop_ctx;
+ struct radius_session *sess = timeout_ctx;
+
+ RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id);
+ radius_server_session_remove(data, sess);
+}
+
+
+static struct radius_session *
+radius_server_new_session(struct radius_server_data *data,
+ struct radius_client *client)
+{
+ struct radius_session *sess;
+
+ if (data->num_sess >= RADIUS_MAX_SESSION) {
+ RADIUS_DEBUG("Maximum number of existing session - no room "
+ "for a new session");
+ return NULL;
+ }
+
+ sess = os_zalloc(sizeof(*sess));
+ if (sess == NULL)
+ return NULL;
+
+ sess->server = data;
+ sess->client = client;
+ sess->sess_id = data->next_sess_id++;
+ sess->next = client->sessions;
+ client->sessions = sess;
+ eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0,
+ radius_server_session_timeout, data, sess);
+ data->num_sess++;
+ return sess;
+}
+
+
+static struct radius_session *
+radius_server_get_new_session(struct radius_server_data *data,
+ struct radius_client *client,
+ struct radius_msg *msg)
+{
+ u8 *user;
+ size_t user_len;
+ int res;
+ struct radius_session *sess;
+ struct eap_config eap_conf;
+
+ RADIUS_DEBUG("Creating a new session");
+
+ user = os_malloc(256);
+ if (user == NULL) {
+ return NULL;
+ }
+ res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256);
+ if (res < 0 || res > 256) {
+ RADIUS_DEBUG("Could not get User-Name");
+ os_free(user);
+ return NULL;
+ }
+ user_len = res;
+ RADIUS_DUMP_ASCII("User-Name", user, user_len);
+
+ res = data->get_eap_user(data->conf_ctx, user, user_len, 0, NULL);
+ os_free(user);
+
+ if (res == 0) {
+ RADIUS_DEBUG("Matching user entry found");
+ sess = radius_server_new_session(data, client);
+ if (sess == NULL) {
+ RADIUS_DEBUG("Failed to create a new session");
+ return NULL;
+ }
+ } else {
+ RADIUS_DEBUG("User-Name not found from user database");
+ return NULL;
+ }
+
+ os_memset(&eap_conf, 0, sizeof(eap_conf));
+ eap_conf.ssl_ctx = data->ssl_ctx;
+ eap_conf.msg_ctx = data->msg_ctx;
+ eap_conf.eap_sim_db_priv = data->eap_sim_db_priv;
+ eap_conf.backend_auth = TRUE;
+ eap_conf.eap_server = 1;
+ eap_conf.pac_opaque_encr_key = data->pac_opaque_encr_key;
+ eap_conf.eap_fast_a_id = data->eap_fast_a_id;
+ eap_conf.eap_fast_a_id_len = data->eap_fast_a_id_len;
+ eap_conf.eap_fast_a_id_info = data->eap_fast_a_id_info;
+ eap_conf.eap_fast_prov = data->eap_fast_prov;
+ eap_conf.pac_key_lifetime = data->pac_key_lifetime;
+ eap_conf.pac_key_refresh_time = data->pac_key_refresh_time;
+ eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind;
+ eap_conf.tnc = data->tnc;
+ eap_conf.wps = data->wps;
+ eap_conf.pwd_group = data->pwd_group;
+ sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
+ &eap_conf);
+ if (sess->eap == NULL) {
+ RADIUS_DEBUG("Failed to initialize EAP state machine for the "
+ "new session");
+ radius_server_session_free(data, sess);
+ return NULL;
+ }
+ sess->eap_if = eap_get_interface(sess->eap);
+ sess->eap_if->eapRestart = TRUE;
+ sess->eap_if->portEnabled = TRUE;
+
+ RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id);
+
+ return sess;
+}
+
+
+static struct radius_msg *
+radius_server_encapsulate_eap(struct radius_server_data *data,
+ struct radius_client *client,
+ struct radius_session *sess,
+ struct radius_msg *request)
+{
+ struct radius_msg *msg;
+ int code;
+ unsigned int sess_id;
+ struct radius_hdr *hdr = radius_msg_get_hdr(request);
+
+ if (sess->eap_if->eapFail) {
+ sess->eap_if->eapFail = FALSE;
+ code = RADIUS_CODE_ACCESS_REJECT;
+ } else if (sess->eap_if->eapSuccess) {
+ sess->eap_if->eapSuccess = FALSE;
+ code = RADIUS_CODE_ACCESS_ACCEPT;
+ } else {
+ sess->eap_if->eapReq = FALSE;
+ code = RADIUS_CODE_ACCESS_CHALLENGE;
+ }
+
+ msg = radius_msg_new(code, hdr->identifier);
+ if (msg == NULL) {
+ RADIUS_DEBUG("Failed to allocate reply message");
+ return NULL;
+ }
+
+ sess_id = htonl(sess->sess_id);
+ if (code == RADIUS_CODE_ACCESS_CHALLENGE &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_STATE,
+ (u8 *) &sess_id, sizeof(sess_id))) {
+ RADIUS_DEBUG("Failed to add State attribute");
+ }
+
+ if (sess->eap_if->eapReqData &&
+ !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData),
+ wpabuf_len(sess->eap_if->eapReqData))) {
+ RADIUS_DEBUG("Failed to add EAP-Message attribute");
+ }
+
+ if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) {
+ int len;
+ if (sess->eap_if->eapKeyDataLen > 64) {
+ len = 32;
+ } else {
+ len = sess->eap_if->eapKeyDataLen / 2;
+ }
+ if (!radius_msg_add_mppe_keys(msg, hdr->authenticator,
+ (u8 *) client->shared_secret,
+ client->shared_secret_len,
+ sess->eap_if->eapKeyData + len,
+ len, sess->eap_if->eapKeyData,
+ len)) {
+ RADIUS_DEBUG("Failed to add MPPE key attributes");
+ }
+ }
+
+ if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
+ RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
+ radius_msg_free(msg);
+ return NULL;
+ }
+
+ if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
+ client->shared_secret_len,
+ hdr->authenticator) < 0) {
+ RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
+ }
+
+ return msg;
+}
+
+
+static int radius_server_reject(struct radius_server_data *data,
+ struct radius_client *client,
+ struct radius_msg *request,
+ struct sockaddr *from, socklen_t fromlen,
+ const char *from_addr, int from_port)
+{
+ struct radius_msg *msg;
+ int ret = 0;
+ struct eap_hdr eapfail;
+ struct wpabuf *buf;
+ struct radius_hdr *hdr = radius_msg_get_hdr(request);
+
+ RADIUS_DEBUG("Reject invalid request from %s:%d",
+ from_addr, from_port);
+
+ msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier);
+ if (msg == NULL) {
+ return -1;
+ }
+
+ os_memset(&eapfail, 0, sizeof(eapfail));
+ eapfail.code = EAP_CODE_FAILURE;
+ eapfail.identifier = 0;
+ eapfail.length = host_to_be16(sizeof(eapfail));
+
+ if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) {
+ RADIUS_DEBUG("Failed to add EAP-Message attribute");
+ }
+
+ if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
+ RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
+ radius_msg_free(msg);
+ return -1;
+ }
+
+ if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
+ client->shared_secret_len,
+ hdr->authenticator) <
+ 0) {
+ RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
+ }
+
+ if (wpa_debug_level <= MSG_MSGDUMP) {
+ radius_msg_dump(msg);
+ }
+
+ data->counters.access_rejects++;
+ client->counters.access_rejects++;
+ buf = radius_msg_get_buf(msg);
+ if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
+ (struct sockaddr *) from, sizeof(*from)) < 0) {
+ perror("sendto[RADIUS SRV]");
+ ret = -1;
+ }
+
+ radius_msg_free(msg);
+
+ return ret;
+}
+
+
+static int radius_server_request(struct radius_server_data *data,
+ struct radius_msg *msg,
+ struct sockaddr *from, socklen_t fromlen,
+ struct radius_client *client,
+ const char *from_addr, int from_port,
+ struct radius_session *force_sess)
+{
+ u8 *eap = NULL;
+ size_t eap_len;
+ int res, state_included = 0;
+ u8 statebuf[4];
+ unsigned int state;
+ struct radius_session *sess;
+ struct radius_msg *reply;
+ int is_complete = 0;
+
+ if (force_sess)
+ sess = force_sess;
+ else {
+ res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf,
+ sizeof(statebuf));
+ state_included = res >= 0;
+ if (res == sizeof(statebuf)) {
+ state = WPA_GET_BE32(statebuf);
+ sess = radius_server_get_session(client, state);
+ } else {
+ sess = NULL;
+ }
+ }
+
+ if (sess) {
+ RADIUS_DEBUG("Request for session 0x%x", sess->sess_id);
+ } else if (state_included) {
+ RADIUS_DEBUG("State attribute included but no session found");
+ radius_server_reject(data, client, msg, from, fromlen,
+ from_addr, from_port);
+ return -1;
+ } else {
+ sess = radius_server_get_new_session(data, client, msg);
+ if (sess == NULL) {
+ RADIUS_DEBUG("Could not create a new session");
+ radius_server_reject(data, client, msg, from, fromlen,
+ from_addr, from_port);
+ return -1;
+ }
+ }
+
+ if (sess->last_from_port == from_port &&
+ sess->last_identifier == radius_msg_get_hdr(msg)->identifier &&
+ os_memcmp(sess->last_authenticator,
+ radius_msg_get_hdr(msg)->authenticator, 16) == 0) {
+ RADIUS_DEBUG("Duplicate message from %s", from_addr);
+ data->counters.dup_access_requests++;
+ client->counters.dup_access_requests++;
+
+ if (sess->last_reply) {
+ struct wpabuf *buf;
+ buf = radius_msg_get_buf(sess->last_reply);
+ res = sendto(data->auth_sock, wpabuf_head(buf),
+ wpabuf_len(buf), 0,
+ (struct sockaddr *) from, fromlen);
+ if (res < 0) {
+ perror("sendto[RADIUS SRV]");
+ }
+ return 0;
+ }
+
+ RADIUS_DEBUG("No previous reply available for duplicate "
+ "message");
+ return -1;
+ }
+
+ eap = radius_msg_get_eap(msg, &eap_len);
+ if (eap == NULL) {
+ RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
+ from_addr);
+ data->counters.packets_dropped++;
+ client->counters.packets_dropped++;
+ return -1;
+ }
+
+ RADIUS_DUMP("Received EAP data", eap, eap_len);
+
+ /* FIX: if Code is Request, Success, or Failure, send Access-Reject;
+ * RFC3579 Sect. 2.6.2.
+ * Include EAP-Response/Nak with no preferred method if
+ * code == request.
+ * If code is not 1-4, discard the packet silently.
+ * Or is this already done by the EAP state machine? */
+
+ wpabuf_free(sess->eap_if->eapRespData);
+ sess->eap_if->eapRespData = wpabuf_alloc_ext_data(eap, eap_len);
+ if (sess->eap_if->eapRespData == NULL)
+ os_free(eap);
+ eap = NULL;
+ sess->eap_if->eapResp = TRUE;
+ eap_server_sm_step(sess->eap);
+
+ if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess ||
+ sess->eap_if->eapFail) && sess->eap_if->eapReqData) {
+ RADIUS_DUMP("EAP data from the state machine",
+ wpabuf_head(sess->eap_if->eapReqData),
+ wpabuf_len(sess->eap_if->eapReqData));
+ } else if (sess->eap_if->eapFail) {
+ RADIUS_DEBUG("No EAP data from the state machine, but eapFail "
+ "set");
+ } else if (eap_sm_method_pending(sess->eap)) {
+ radius_msg_free(sess->last_msg);
+ sess->last_msg = msg;
+ sess->last_from_port = from_port;
+ os_free(sess->last_from_addr);
+ sess->last_from_addr = os_strdup(from_addr);
+ sess->last_fromlen = fromlen;
+ os_memcpy(&sess->last_from, from, fromlen);
+ return -2;
+ } else {
+ RADIUS_DEBUG("No EAP data from the state machine - ignore this"
+ " Access-Request silently (assuming it was a "
+ "duplicate)");
+ data->counters.packets_dropped++;
+ client->counters.packets_dropped++;
+ return -1;
+ }
+
+ if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
+ is_complete = 1;
+
+ reply = radius_server_encapsulate_eap(data, client, sess, msg);
+
+ if (reply) {
+ struct wpabuf *buf;
+ struct radius_hdr *hdr;
+
+ RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port);
+ if (wpa_debug_level <= MSG_MSGDUMP) {
+ radius_msg_dump(reply);
+ }
+
+ switch (radius_msg_get_hdr(reply)->code) {
+ case RADIUS_CODE_ACCESS_ACCEPT:
+ data->counters.access_accepts++;
+ client->counters.access_accepts++;
+ break;
+ case RADIUS_CODE_ACCESS_REJECT:
+ data->counters.access_rejects++;
+ client->counters.access_rejects++;
+ break;
+ case RADIUS_CODE_ACCESS_CHALLENGE:
+ data->counters.access_challenges++;
+ client->counters.access_challenges++;
+ break;
+ }
+ buf = radius_msg_get_buf(reply);
+ res = sendto(data->auth_sock, wpabuf_head(buf),
+ wpabuf_len(buf), 0,
+ (struct sockaddr *) from, fromlen);
+ if (res < 0) {
+ perror("sendto[RADIUS SRV]");
+ }
+ radius_msg_free(sess->last_reply);
+ sess->last_reply = reply;
+ sess->last_from_port = from_port;
+ hdr = radius_msg_get_hdr(msg);
+ sess->last_identifier = hdr->identifier;
+ os_memcpy(sess->last_authenticator, hdr->authenticator, 16);
+ } else {
+ data->counters.packets_dropped++;
+ client->counters.packets_dropped++;
+ }
+
+ if (is_complete) {
+ RADIUS_DEBUG("Removing completed session 0x%x after timeout",
+ sess->sess_id);
+ eloop_cancel_timeout(radius_server_session_remove_timeout,
+ data, sess);
+ eloop_register_timeout(10, 0,
+ radius_server_session_remove_timeout,
+ data, sess);
+ }
+
+ return 0;
+}
+
+
+static void radius_server_receive_auth(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct radius_server_data *data = eloop_ctx;
+ u8 *buf = NULL;
+ union {
+ struct sockaddr_storage ss;
+ struct sockaddr_in sin;
+#ifdef CONFIG_IPV6
+ struct sockaddr_in6 sin6;
+#endif /* CONFIG_IPV6 */
+ } from;
+ socklen_t fromlen;
+ int len;
+ struct radius_client *client = NULL;
+ struct radius_msg *msg = NULL;
+ char abuf[50];
+ int from_port = 0;
+
+ buf = os_malloc(RADIUS_MAX_MSG_LEN);
+ if (buf == NULL) {
+ goto fail;
+ }
+
+ fromlen = sizeof(from);
+ len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
+ (struct sockaddr *) &from.ss, &fromlen);
+ if (len < 0) {
+ perror("recvfrom[radius_server]");
+ goto fail;
+ }
+
+#ifdef CONFIG_IPV6
+ if (data->ipv6) {
+ if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf,
+ sizeof(abuf)) == NULL)
+ abuf[0] = '\0';
+ from_port = ntohs(from.sin6.sin6_port);
+ RADIUS_DEBUG("Received %d bytes from %s:%d",
+ len, abuf, from_port);
+
+ client = radius_server_get_client(data,
+ (struct in_addr *)
+ &from.sin6.sin6_addr, 1);
+ }
+#endif /* CONFIG_IPV6 */
+
+ if (!data->ipv6) {
+ os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+ from_port = ntohs(from.sin.sin_port);
+ RADIUS_DEBUG("Received %d bytes from %s:%d",
+ len, abuf, from_port);
+
+ client = radius_server_get_client(data, &from.sin.sin_addr, 0);
+ }
+
+ RADIUS_DUMP("Received data", buf, len);
+
+ if (client == NULL) {
+ RADIUS_DEBUG("Unknown client %s - packet ignored", abuf);
+ data->counters.invalid_requests++;
+ goto fail;
+ }
+
+ msg = radius_msg_parse(buf, len);
+ if (msg == NULL) {
+ RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
+ data->counters.malformed_access_requests++;
+ client->counters.malformed_access_requests++;
+ goto fail;
+ }
+
+ os_free(buf);
+ buf = NULL;
+
+ if (wpa_debug_level <= MSG_MSGDUMP) {
+ radius_msg_dump(msg);
+ }
+
+ if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) {
+ RADIUS_DEBUG("Unexpected RADIUS code %d",
+ radius_msg_get_hdr(msg)->code);
+ data->counters.unknown_types++;
+ client->counters.unknown_types++;
+ goto fail;
+ }
+
+ data->counters.access_requests++;
+ client->counters.access_requests++;
+
+ if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret,
+ client->shared_secret_len, NULL)) {
+ RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf);
+ data->counters.bad_authenticators++;
+ client->counters.bad_authenticators++;
+ goto fail;
+ }
+
+ if (radius_server_request(data, msg, (struct sockaddr *) &from,
+ fromlen, client, abuf, from_port, NULL) ==
+ -2)
+ return; /* msg was stored with the session */
+
+fail:
+ radius_msg_free(msg);
+ os_free(buf);
+}
+
+
+static int radius_server_disable_pmtu_discovery(int s)
+{
+ int r = -1;
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+ /* Turn off Path MTU discovery on IPv4/UDP sockets. */
+ int action = IP_PMTUDISC_DONT;
+ r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
+ sizeof(action));
+ if (r == -1)
+ wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
+ "%s", strerror(errno));
+#endif
+ return r;
+}
+
+
+static int radius_server_open_socket(int port)
+{
+ int s;
+ struct sockaddr_in addr;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ radius_server_disable_pmtu_discovery(s);
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind");
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+
+#ifdef CONFIG_IPV6
+static int radius_server_open_socket6(int port)
+{
+ int s;
+ struct sockaddr_in6 addr;
+
+ s = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket[IPv6]");
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
+ addr.sin6_port = htons(port);
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind");
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+#endif /* CONFIG_IPV6 */
+
+
+static void radius_server_free_sessions(struct radius_server_data *data,
+ struct radius_session *sessions)
+{
+ struct radius_session *session, *prev;
+
+ session = sessions;
+ while (session) {
+ prev = session;
+ session = session->next;
+ radius_server_session_free(data, prev);
+ }
+}
+
+
+static void radius_server_free_clients(struct radius_server_data *data,
+ struct radius_client *clients)
+{
+ struct radius_client *client, *prev;
+
+ client = clients;
+ while (client) {
+ prev = client;
+ client = client->next;
+
+ radius_server_free_sessions(data, prev->sessions);
+ os_free(prev->shared_secret);
+ os_free(prev);
+ }
+}
+
+
+static struct radius_client *
+radius_server_read_clients(const char *client_file, int ipv6)
+{
+ FILE *f;
+ const int buf_size = 1024;
+ char *buf, *pos;
+ struct radius_client *clients, *tail, *entry;
+ int line = 0, mask, failed = 0, i;
+ struct in_addr addr;
+#ifdef CONFIG_IPV6
+ struct in6_addr addr6;
+#endif /* CONFIG_IPV6 */
+ unsigned int val;
+
+ f = fopen(client_file, "r");
+ if (f == NULL) {
+ RADIUS_ERROR("Could not open client file '%s'", client_file);
+ return NULL;
+ }
+
+ buf = os_malloc(buf_size);
+ if (buf == NULL) {
+ fclose(f);
+ return NULL;
+ }
+
+ clients = tail = NULL;
+ while (fgets(buf, buf_size, f)) {
+ /* Configuration file format:
+ * 192.168.1.0/24 secret
+ * 192.168.1.2 secret
+ * fe80::211:22ff:fe33:4455/64 secretipv6
+ */
+ line++;
+ buf[buf_size - 1] = '\0';
+ pos = buf;
+ while (*pos != '\0' && *pos != '\n')
+ pos++;
+ if (*pos == '\n')
+ *pos = '\0';
+ if (*buf == '\0' || *buf == '#')
+ continue;
+
+ pos = buf;
+ while ((*pos >= '0' && *pos <= '9') || *pos == '.' ||
+ (*pos >= 'a' && *pos <= 'f') || *pos == ':' ||
+ (*pos >= 'A' && *pos <= 'F')) {
+ pos++;
+ }
+
+ if (*pos == '\0') {
+ failed = 1;
+ break;
+ }
+
+ if (*pos == '/') {
+ char *end;
+ *pos++ = '\0';
+ mask = strtol(pos, &end, 10);
+ if ((pos == end) ||
+ (mask < 0 || mask > (ipv6 ? 128 : 32))) {
+ failed = 1;
+ break;
+ }
+ pos = end;
+ } else {
+ mask = ipv6 ? 128 : 32;
+ *pos++ = '\0';
+ }
+
+ if (!ipv6 && inet_aton(buf, &addr) == 0) {
+ failed = 1;
+ break;
+ }
+#ifdef CONFIG_IPV6
+ if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) {
+ if (inet_pton(AF_INET, buf, &addr) <= 0) {
+ failed = 1;
+ break;
+ }
+ /* Convert IPv4 address to IPv6 */
+ if (mask <= 32)
+ mask += (128 - 32);
+ os_memset(addr6.s6_addr, 0, 10);
+ addr6.s6_addr[10] = 0xff;
+ addr6.s6_addr[11] = 0xff;
+ os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr,
+ 4);
+ }
+#endif /* CONFIG_IPV6 */
+
+ while (*pos == ' ' || *pos == '\t') {
+ pos++;
+ }
+
+ if (*pos == '\0') {
+ failed = 1;
+ break;
+ }
+
+ entry = os_zalloc(sizeof(*entry));
+ if (entry == NULL) {
+ failed = 1;
+ break;
+ }
+ entry->shared_secret = os_strdup(pos);
+ if (entry->shared_secret == NULL) {
+ failed = 1;
+ os_free(entry);
+ break;
+ }
+ entry->shared_secret_len = os_strlen(entry->shared_secret);
+ entry->addr.s_addr = addr.s_addr;
+ if (!ipv6) {
+ val = 0;
+ for (i = 0; i < mask; i++)
+ val |= 1 << (31 - i);
+ entry->mask.s_addr = htonl(val);
+ }
+#ifdef CONFIG_IPV6
+ if (ipv6) {
+ int offset = mask / 8;
+
+ os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16);
+ os_memset(entry->mask6.s6_addr, 0xff, offset);
+ val = 0;
+ for (i = 0; i < (mask % 8); i++)
+ val |= 1 << (7 - i);
+ if (offset < 16)
+ entry->mask6.s6_addr[offset] = val;
+ }
+#endif /* CONFIG_IPV6 */
+
+ if (tail == NULL) {
+ clients = tail = entry;
+ } else {
+ tail->next = entry;
+ tail = entry;
+ }
+ }
+
+ if (failed) {
+ RADIUS_ERROR("Invalid line %d in '%s'", line, client_file);
+ radius_server_free_clients(NULL, clients);
+ clients = NULL;
+ }
+
+ os_free(buf);
+ fclose(f);
+
+ return clients;
+}
+
+
+/**
+ * radius_server_init - Initialize RADIUS server
+ * @conf: Configuration for the RADIUS server
+ * Returns: Pointer to private RADIUS server context or %NULL on failure
+ *
+ * This initializes a RADIUS server instance and returns a context pointer that
+ * will be used in other calls to the RADIUS server module. The server can be
+ * deinitialize by calling radius_server_deinit().
+ */
+struct radius_server_data *
+radius_server_init(struct radius_server_conf *conf)
+{
+ struct radius_server_data *data;
+
+#ifndef CONFIG_IPV6
+ if (conf->ipv6) {
+ fprintf(stderr, "RADIUS server compiled without IPv6 "
+ "support.\n");
+ return NULL;
+ }
+#endif /* CONFIG_IPV6 */
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ os_get_time(&data->start_time);
+ data->conf_ctx = conf->conf_ctx;
+ data->eap_sim_db_priv = conf->eap_sim_db_priv;
+ data->ssl_ctx = conf->ssl_ctx;
+ data->msg_ctx = conf->msg_ctx;
+ data->ipv6 = conf->ipv6;
+ if (conf->pac_opaque_encr_key) {
+ data->pac_opaque_encr_key = os_malloc(16);
+ os_memcpy(data->pac_opaque_encr_key, conf->pac_opaque_encr_key,
+ 16);
+ }
+ if (conf->eap_fast_a_id) {
+ data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
+ if (data->eap_fast_a_id) {
+ os_memcpy(data->eap_fast_a_id, conf->eap_fast_a_id,
+ conf->eap_fast_a_id_len);
+ data->eap_fast_a_id_len = conf->eap_fast_a_id_len;
+ }
+ }
+ if (conf->eap_fast_a_id_info)
+ data->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
+ data->eap_fast_prov = conf->eap_fast_prov;
+ data->pac_key_lifetime = conf->pac_key_lifetime;
+ data->pac_key_refresh_time = conf->pac_key_refresh_time;
+ data->get_eap_user = conf->get_eap_user;
+ data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
+ data->tnc = conf->tnc;
+ data->wps = conf->wps;
+ data->pwd_group = conf->pwd_group;
+ if (conf->eap_req_id_text) {
+ data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len);
+ if (data->eap_req_id_text) {
+ os_memcpy(data->eap_req_id_text, conf->eap_req_id_text,
+ conf->eap_req_id_text_len);
+ data->eap_req_id_text_len = conf->eap_req_id_text_len;
+ }
+ }
+
+ data->clients = radius_server_read_clients(conf->client_file,
+ conf->ipv6);
+ if (data->clients == NULL) {
+ printf("No RADIUS clients configured.\n");
+ radius_server_deinit(data);
+ return NULL;
+ }
+
+#ifdef CONFIG_IPV6
+ if (conf->ipv6)
+ data->auth_sock = radius_server_open_socket6(conf->auth_port);
+ else
+#endif /* CONFIG_IPV6 */
+ data->auth_sock = radius_server_open_socket(conf->auth_port);
+ if (data->auth_sock < 0) {
+ printf("Failed to open UDP socket for RADIUS authentication "
+ "server\n");
+ radius_server_deinit(data);
+ return NULL;
+ }
+ if (eloop_register_read_sock(data->auth_sock,
+ radius_server_receive_auth,
+ data, NULL)) {
+ radius_server_deinit(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+/**
+ * radius_server_deinit - Deinitialize RADIUS server
+ * @data: RADIUS server context from radius_server_init()
+ */
+void radius_server_deinit(struct radius_server_data *data)
+{
+ if (data == NULL)
+ return;
+
+ if (data->auth_sock >= 0) {
+ eloop_unregister_read_sock(data->auth_sock);
+ close(data->auth_sock);
+ }
+
+ radius_server_free_clients(data, data->clients);
+
+ os_free(data->pac_opaque_encr_key);
+ os_free(data->eap_fast_a_id);
+ os_free(data->eap_fast_a_id_info);
+ os_free(data->eap_req_id_text);
+ os_free(data);
+}
+
+
+/**
+ * radius_server_get_mib - Get RADIUS server MIB information
+ * @data: RADIUS server context from radius_server_init()
+ * @buf: Buffer for returning the MIB data in text format
+ * @buflen: buf length in octets
+ * Returns: Number of octets written into buf
+ */
+int radius_server_get_mib(struct radius_server_data *data, char *buf,
+ size_t buflen)
+{
+ int ret, uptime;
+ unsigned int idx;
+ char *end, *pos;
+ struct os_time now;
+ struct radius_client *cli;
+
+ /* RFC 2619 - RADIUS Authentication Server MIB */
+
+ if (data == NULL || buflen == 0)
+ return 0;
+
+ pos = buf;
+ end = buf + buflen;
+
+ os_get_time(&now);
+ uptime = (now.sec - data->start_time.sec) * 100 +
+ ((now.usec - data->start_time.usec) / 10000) % 100;
+ ret = os_snprintf(pos, end - pos,
+ "RADIUS-AUTH-SERVER-MIB\n"
+ "radiusAuthServIdent=hostapd\n"
+ "radiusAuthServUpTime=%d\n"
+ "radiusAuthServResetTime=0\n"
+ "radiusAuthServConfigReset=4\n",
+ uptime);
+ if (ret < 0 || ret >= end - pos) {
+ *pos = '\0';
+ return pos - buf;
+ }
+ pos += ret;
+
+ ret = os_snprintf(pos, end - pos,
+ "radiusAuthServTotalAccessRequests=%u\n"
+ "radiusAuthServTotalInvalidRequests=%u\n"
+ "radiusAuthServTotalDupAccessRequests=%u\n"
+ "radiusAuthServTotalAccessAccepts=%u\n"
+ "radiusAuthServTotalAccessRejects=%u\n"
+ "radiusAuthServTotalAccessChallenges=%u\n"
+ "radiusAuthServTotalMalformedAccessRequests=%u\n"
+ "radiusAuthServTotalBadAuthenticators=%u\n"
+ "radiusAuthServTotalPacketsDropped=%u\n"
+ "radiusAuthServTotalUnknownTypes=%u\n",
+ data->counters.access_requests,
+ data->counters.invalid_requests,
+ data->counters.dup_access_requests,
+ data->counters.access_accepts,
+ data->counters.access_rejects,
+ data->counters.access_challenges,
+ data->counters.malformed_access_requests,
+ data->counters.bad_authenticators,
+ data->counters.packets_dropped,
+ data->counters.unknown_types);
+ if (ret < 0 || ret >= end - pos) {
+ *pos = '\0';
+ return pos - buf;
+ }
+ pos += ret;
+
+ for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) {
+ char abuf[50], mbuf[50];
+#ifdef CONFIG_IPV6
+ if (data->ipv6) {
+ if (inet_ntop(AF_INET6, &cli->addr6, abuf,
+ sizeof(abuf)) == NULL)
+ abuf[0] = '\0';
+ if (inet_ntop(AF_INET6, &cli->mask6, abuf,
+ sizeof(mbuf)) == NULL)
+ mbuf[0] = '\0';
+ }
+#endif /* CONFIG_IPV6 */
+ if (!data->ipv6) {
+ os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf));
+ os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf));
+ }
+
+ ret = os_snprintf(pos, end - pos,
+ "radiusAuthClientIndex=%u\n"
+ "radiusAuthClientAddress=%s/%s\n"
+ "radiusAuthServAccessRequests=%u\n"
+ "radiusAuthServDupAccessRequests=%u\n"
+ "radiusAuthServAccessAccepts=%u\n"
+ "radiusAuthServAccessRejects=%u\n"
+ "radiusAuthServAccessChallenges=%u\n"
+ "radiusAuthServMalformedAccessRequests=%u\n"
+ "radiusAuthServBadAuthenticators=%u\n"
+ "radiusAuthServPacketsDropped=%u\n"
+ "radiusAuthServUnknownTypes=%u\n",
+ idx,
+ abuf, mbuf,
+ cli->counters.access_requests,
+ cli->counters.dup_access_requests,
+ cli->counters.access_accepts,
+ cli->counters.access_rejects,
+ cli->counters.access_challenges,
+ cli->counters.malformed_access_requests,
+ cli->counters.bad_authenticators,
+ cli->counters.packets_dropped,
+ cli->counters.unknown_types);
+ if (ret < 0 || ret >= end - pos) {
+ *pos = '\0';
+ return pos - buf;
+ }
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+static int radius_server_get_eap_user(void *ctx, const u8 *identity,
+ size_t identity_len, int phase2,
+ struct eap_user *user)
+{
+ struct radius_session *sess = ctx;
+ struct radius_server_data *data = sess->server;
+
+ return data->get_eap_user(data->conf_ctx, identity, identity_len,
+ phase2, user);
+}
+
+
+static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len)
+{
+ struct radius_session *sess = ctx;
+ struct radius_server_data *data = sess->server;
+ *len = data->eap_req_id_text_len;
+ return data->eap_req_id_text;
+}
+
+
+static struct eapol_callbacks radius_server_eapol_cb =
+{
+ .get_eap_user = radius_server_get_eap_user,
+ .get_eap_req_id_text = radius_server_get_eap_req_id_text,
+};
+
+
+/**
+ * radius_server_eap_pending_cb - Pending EAP data notification
+ * @data: RADIUS server context from radius_server_init()
+ * @ctx: Pending EAP context pointer
+ *
+ * This function is used to notify EAP server module that a pending operation
+ * has been completed and processing of the EAP session can proceed.
+ */
+void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx)
+{
+ struct radius_client *cli;
+ struct radius_session *s, *sess = NULL;
+ struct radius_msg *msg;
+
+ if (data == NULL)
+ return;
+
+ for (cli = data->clients; cli; cli = cli->next) {
+ for (s = cli->sessions; s; s = s->next) {
+ if (s->eap == ctx && s->last_msg) {
+ sess = s;
+ break;
+ }
+ if (sess)
+ break;
+ }
+ if (sess)
+ break;
+ }
+
+ if (sess == NULL) {
+ RADIUS_DEBUG("No session matched callback ctx");
+ return;
+ }
+
+ msg = sess->last_msg;
+ sess->last_msg = NULL;
+ eap_sm_pending_cb(sess->eap);
+ if (radius_server_request(data, msg,
+ (struct sockaddr *) &sess->last_from,
+ sess->last_fromlen, cli,
+ sess->last_from_addr,
+ sess->last_from_port, sess) == -2)
+ return; /* msg was stored with the session */
+
+ radius_msg_free(msg);
+}
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
new file mode 100644
index 0000000..126e314
--- /dev/null
+++ b/src/radius/radius_server.h
@@ -0,0 +1,217 @@
+/*
+ * RADIUS authentication server
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef RADIUS_SERVER_H
+#define RADIUS_SERVER_H
+
+struct radius_server_data;
+struct eap_user;
+
+/**
+ * struct radius_server_conf - RADIUS server configuration
+ */
+struct radius_server_conf {
+ /**
+ * auth_port - UDP port to listen to as an authentication server
+ */
+ int auth_port;
+
+ /**
+ * client_file - RADIUS client configuration file
+ *
+ * This file contains the RADIUS clients and the shared secret to be
+ * used with them in a format where each client is on its own line. The
+ * first item on the line is the IPv4 or IPv6 address of the client
+ * with an optional address mask to allow full network to be specified
+ * (e.g., 192.168.1.2 or 192.168.1.0/24). This is followed by white
+ * space (space or tabulator) and the shared secret. Lines starting
+ * with '#' are skipped and can be used as comments.
+ */
+ char *client_file;
+
+ /**
+ * conf_ctx - Context pointer for callbacks
+ *
+ * This is used as the ctx argument in get_eap_user() calls.
+ */
+ void *conf_ctx;
+
+ /**
+ * eap_sim_db_priv - EAP-SIM/AKA database context
+ *
+ * This is passed to the EAP-SIM/AKA server implementation as a
+ * callback context.
+ */
+ void *eap_sim_db_priv;
+
+ /**
+ * ssl_ctx - TLS context
+ *
+ * This is passed to the EAP server implementation as a callback
+ * context for TLS operations.
+ */
+ void *ssl_ctx;
+
+ /**
+ * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
+ *
+ * This parameter is used to set a key for EAP-FAST to encrypt the
+ * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
+ * set, must point to a 16-octet key.
+ */
+ u8 *pac_opaque_encr_key;
+
+ /**
+ * eap_fast_a_id - EAP-FAST authority identity (A-ID)
+ *
+ * If EAP-FAST is not used, this can be set to %NULL. In theory, this
+ * is a variable length field, but due to some existing implementations
+ * requiring A-ID to be 16 octets in length, it is recommended to use
+ * that length for the field to provide interoperability with deployed
+ * peer implementations.
+ */
+ u8 *eap_fast_a_id;
+
+ /**
+ * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
+ */
+ size_t eap_fast_a_id_len;
+
+ /**
+ * eap_fast_a_id_info - EAP-FAST authority identifier information
+ *
+ * This A-ID-Info contains a user-friendly name for the A-ID. For
+ * example, this could be the enterprise and server names in
+ * human-readable format. This field is encoded as UTF-8. If EAP-FAST
+ * is not used, this can be set to %NULL.
+ */
+ char *eap_fast_a_id_info;
+
+ /**
+ * eap_fast_prov - EAP-FAST provisioning modes
+ *
+ * 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
+ * 2 = only authenticated provisioning allowed, 3 = both provisioning
+ * modes allowed.
+ */
+ int eap_fast_prov;
+
+ /**
+ * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
+ *
+ * This is the hard limit on how long a provisioned PAC-Key can be
+ * used.
+ */
+ int pac_key_lifetime;
+
+ /**
+ * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
+ *
+ * This is a soft limit on the PAC-Key. The server will automatically
+ * generate a new PAC-Key when this number of seconds (or fewer) of the
+ * lifetime remains.
+ */
+ int pac_key_refresh_time;
+
+ /**
+ * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
+ *
+ * This controls whether the protected success/failure indication
+ * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
+ */
+ int eap_sim_aka_result_ind;
+
+ /**
+ * tnc - Trusted Network Connect (TNC)
+ *
+ * This controls whether TNC is enabled and will be required before the
+ * peer is allowed to connect. Note: This is only used with EAP-TTLS
+ * and EAP-FAST. If any other EAP method is enabled, the peer will be
+ * allowed to connect without TNC.
+ */
+ int tnc;
+
+ /**
+ * pwd_group - EAP-pwd D-H group
+ *
+ * This is used to select which D-H group to use with EAP-pwd.
+ */
+ u16 pwd_group;
+
+ /**
+ * wps - Wi-Fi Protected Setup context
+ *
+ * If WPS is used with an external RADIUS server (which is quite
+ * unlikely configuration), this is used to provide a pointer to WPS
+ * context data. Normally, this can be set to %NULL.
+ */
+ struct wps_context *wps;
+
+ /**
+ * ipv6 - Whether to enable IPv6 support in the RADIUS server
+ */
+ int ipv6;
+
+ /**
+ * get_eap_user - Callback for fetching EAP user information
+ * @ctx: Context data from conf_ctx
+ * @identity: User identity
+ * @identity_len: identity buffer length in octets
+ * @phase2: Whether this is for Phase 2 identity
+ * @user: Data structure for filling in the user information
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is used to fetch information from user database. The callback
+ * will fill in information about allowed EAP methods and the user
+ * password. The password field will be an allocated copy of the
+ * password data and RADIUS server will free it after use.
+ */
+ int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
+ int phase2, struct eap_user *user);
+
+ /**
+ * eap_req_id_text - Optional data for EAP-Request/Identity
+ *
+ * This can be used to configure an optional, displayable message that
+ * will be sent in EAP-Request/Identity. This string can contain an
+ * ASCII-0 character (nul) to separate network infromation per RFC
+ * 4284. The actual string length is explicit provided in
+ * eap_req_id_text_len since nul character will not be used as a string
+ * terminator.
+ */
+ const char *eap_req_id_text;
+
+ /**
+ * eap_req_id_text_len - Length of eap_req_id_text buffer in octets
+ */
+ size_t eap_req_id_text_len;
+
+ /*
+ * msg_ctx - Context data for wpa_msg() calls
+ */
+ void *msg_ctx;
+};
+
+
+struct radius_server_data *
+radius_server_init(struct radius_server_conf *conf);
+
+void radius_server_deinit(struct radius_server_data *data);
+
+int radius_server_get_mib(struct radius_server_data *data, char *buf,
+ size_t buflen);
+
+void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx);
+
+#endif /* RADIUS_SERVER_H */
diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/rsn_supp/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
new file mode 100644
index 0000000..2b3332e
--- /dev/null
+++ b/src/rsn_supp/peerkey.c
@@ -0,0 +1,1186 @@
+/*
+ * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
+ * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#ifdef CONFIG_PEERKEY
+
+#include "common.h"
+#include "eloop.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "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 = ie.pairwise_cipher & sm->allowed_pairwise_cipher;
+ if (cipher & WPA_CIPHER_CCMP) {
+ wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
+ cipher = WPA_CIPHER_CCMP;
+ } else if (cipher & WPA_CIPHER_TKIP) {
+ wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
+ cipher = WPA_CIPHER_TKIP;
+ } else {
+ wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2");
+ wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr,
+ STK_MUI_SMK, STK_ERR_CPHR_NS,
+ ver);
+ return -1;
+ }
+
+ /* TODO: find existing entry and if found, use that instead of adding
+ * a new one; how to handle the case where both ends initiate at the
+ * same time? */
+ peerkey = os_zalloc(sizeof(*peerkey));
+ if (peerkey == NULL)
+ return -1;
+ os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN);
+ os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
+ os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
+ peerkey->rsnie_i_len = kde.rsn_ie_len;
+ peerkey->cipher = cipher;
+#ifdef CONFIG_IEEE80211W
+ if (ie.key_mgmt & (WPA_KEY_MGMT_IEEE8021X_SHA256 |
+ WPA_KEY_MGMT_PSK_SHA256))
+ peerkey->use_sha256 = 1;
+#endif /* CONFIG_IEEE80211W */
+
+ if (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;
+ if (cipher == WPA_CIPHER_CCMP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ else if (cipher == WPA_CIPHER_TKIP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ pos += RSN_SELECTOR_LEN;
+
+ hdr->len = (pos - peerkey->rsnie_p) - 2;
+ peerkey->rsnie_p_len = pos - peerkey->rsnie_p;
+ wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
+ peerkey->rsnie_p, peerkey->rsnie_p_len);
+
+ wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey);
+
+ peerkey->next = sm->peerkey;
+ sm->peerkey = peerkey;
+
+ return 0;
+}
+
+
+/**
+ * rsn_smkid - Derive SMK identifier
+ * @smk: Station master key (32 bytes)
+ * @pnonce: Peer Nonce
+ * @mac_p: Peer MAC address
+ * @inonce: Initiator Nonce
+ * @mac_i: Initiator MAC address
+ * @use_sha256: Whether to use SHA256-based KDF
+ *
+ * 8.5.1.4 Station to station (STK) key hierarchy
+ * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I)
+ */
+static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p,
+ const u8 *inonce, const u8 *mac_i, u8 *smkid,
+ int use_sha256)
+{
+ char *title = "SMK Name";
+ const u8 *addr[5];
+ const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN,
+ ETH_ALEN };
+ unsigned char hash[SHA256_MAC_LEN];
+
+ addr[0] = (u8 *) title;
+ addr[1] = pnonce;
+ addr[2] = mac_p;
+ addr[3] = inonce;
+ addr[4] = mac_i;
+
+#ifdef CONFIG_IEEE80211W
+ if (use_sha256)
+ hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash);
+ else
+#endif /* CONFIG_IEEE80211W */
+ hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash);
+ os_memcpy(smkid, hash, PMKID_LEN);
+}
+
+
+static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey)
+{
+ size_t mlen;
+ struct wpa_eapol_key *msg;
+ u8 *mbuf;
+ size_t kde_len;
+ u16 key_info, ver;
+
+ kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+
+ mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*msg) + kde_len, &mlen,
+ (void *) &msg);
+ if (mbuf == NULL)
+ return;
+
+ msg->type = EAPOL_KEY_TYPE_RSN;
+
+ if (peerkey->cipher == WPA_CIPHER_CCMP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK;
+ WPA_PUT_BE16(msg->key_info, key_info);
+
+ if (peerkey->cipher == WPA_CIPHER_CCMP)
+ WPA_PUT_BE16(msg->key_length, 16);
+ else
+ WPA_PUT_BE16(msg->key_length, 32);
+
+ os_memcpy(msg->replay_counter, peerkey->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+ WPA_PUT_BE16(msg->key_data_length, kde_len);
+ wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID,
+ peerkey->smkid, PMKID_LEN);
+
+ if (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_CCMP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK |
+ WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+ WPA_PUT_BE16(msg->key_info, key_info);
+
+ if (peerkey->cipher == WPA_CIPHER_CCMP)
+ WPA_PUT_BE16(msg->key_length, 16);
+ else
+ WPA_PUT_BE16(msg->key_length, 32);
+
+ os_memcpy(msg->replay_counter, peerkey->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+ WPA_PUT_BE16(msg->key_data_length, kde_len);
+ pos = (u8 *) (msg + 1);
+ pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
+ lifetime = host_to_be32(peerkey->lifetime);
+ wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+ (u8 *) &lifetime, sizeof(lifetime));
+
+ os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+
+ wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR,
+ MAC2STR(peerkey->addr));
+ wpa_eapol_key_send(sm, peerkey->stk.kck, ver, peerkey->addr,
+ ETH_P_EAPOL, mbuf, mlen, msg->key_mic);
+}
+
+
+static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey,
+ struct wpa_eapol_ie_parse *kde)
+{
+ wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")",
+ MAC2STR(kde->mac_addr));
+
+ if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0)
+ {
+ wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not "
+ "match with the one used in SMK M3");
+ return -1;
+ }
+
+ if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not "
+ "match with the one received in SMK M2");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ int ver,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_ie_parse *kde)
+{
+ int cipher;
+ struct wpa_ie_data ie;
+
+ wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")",
+ MAC2STR(kde->mac_addr));
+ if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN ||
+ wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) {
+ wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5");
+ /* TODO: abort negotiation */
+ return -1;
+ }
+
+ if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does "
+ "not match with INonce used in SMK M1");
+ return -1;
+ }
+
+ if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0)
+ {
+ wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not "
+ "match with the one used in SMK M1");
+ return -1;
+ }
+
+ os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len);
+ peerkey->rsnie_p_len = kde->rsn_ie_len;
+ os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN);
+
+ cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher;
+ if (cipher & WPA_CIPHER_CCMP) {
+ wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
+ peerkey->cipher = WPA_CIPHER_CCMP;
+ } else if (cipher & WPA_CIPHER_TKIP) {
+ wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
+ peerkey->cipher = WPA_CIPHER_TKIP;
+ } else {
+ wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected "
+ "unacceptable cipher", MAC2STR(kde->mac_addr));
+ wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr,
+ STK_MUI_SMK, STK_ERR_CPHR_NS,
+ ver);
+ /* TODO: abort negotiation */
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m45(
+ struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key, size_t extra_len, int ver)
+{
+ struct wpa_peerkey *peerkey;
+ struct wpa_eapol_ie_parse kde;
+ u32 lifetime;
+ struct os_time now;
+
+ if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+ wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
+ "the current network");
+ return -1;
+ }
+
+ if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+ 0) {
+ wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5");
+ return -1;
+ }
+
+ if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
+ kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN ||
+ kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN ||
+ kde.lifetime == NULL || kde.lifetime_len < 4) {
+ wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or "
+ "Lifetime KDE in SMK M4/M5");
+ return -1;
+ }
+
+ for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+ if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 &&
+ os_memcmp(peerkey->initiator ? peerkey->inonce :
+ peerkey->pnonce,
+ key->key_nonce, WPA_NONCE_LEN) == 0)
+ break;
+ }
+ if (peerkey == NULL) {
+ wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found "
+ "for SMK M4/M5: peer " MACSTR,
+ MAC2STR(kde.mac_addr));
+ return -1;
+ }
+
+ if (peerkey->initiator) {
+ if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver,
+ peerkey, &kde) < 0)
+ return -1;
+ } else {
+ if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0)
+ return -1;
+ }
+
+ os_memcpy(peerkey->smk, kde.smk, PMK_LEN);
+ peerkey->smk_complete = 1;
+ wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN);
+ lifetime = WPA_GET_BE32(kde.lifetime);
+ wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime);
+ if (lifetime > 1000000000)
+ lifetime = 1000000000; /* avoid overflowing expiration time */
+ peerkey->lifetime = lifetime;
+ os_get_time(&now);
+ peerkey->expiration = now.sec + lifetime;
+ eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
+ sm, peerkey);
+
+ if (peerkey->initiator) {
+ rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr,
+ peerkey->inonce, sm->own_addr, peerkey->smkid,
+ peerkey->use_sha256);
+ wpa_supplicant_send_stk_1_of_4(sm, peerkey);
+ } else {
+ rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr,
+ peerkey->inonce, peerkey->addr, peerkey->smkid,
+ peerkey->use_sha256);
+ }
+ wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_process_smk_error(
+ struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key, size_t extra_len)
+{
+ struct wpa_eapol_ie_parse kde;
+ struct rsn_error_kde error;
+ u8 peer[ETH_ALEN];
+ u16 error_type;
+
+ wpa_printf(MSG_DEBUG, "RSN: Received SMK Error");
+
+ if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+ wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
+ "the current network");
+ return -1;
+ }
+
+ if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+ 0) {
+ wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
+ return -1;
+ }
+
+ if (kde.error == NULL || kde.error_len < sizeof(error)) {
+ wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error");
+ return -1;
+ }
+
+ if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN)
+ os_memcpy(peer, kde.mac_addr, ETH_ALEN);
+ else
+ os_memset(peer, 0, ETH_ALEN);
+ os_memcpy(&error, kde.error, sizeof(error));
+ error_type = be_to_host16(error.error_type);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: SMK Error KDE received: MUI %d error_type %d peer "
+ MACSTR,
+ be_to_host16(error.mui), error_type,
+ MAC2STR(peer));
+
+ if (kde.mac_addr &&
+ (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN ||
+ error_type == STK_ERR_CPHR_NS)) {
+ struct wpa_peerkey *peerkey;
+
+ for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+ if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) ==
+ 0)
+ break;
+ }
+ if (peerkey == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake "
+ "found for SMK Error");
+ return -1;
+ }
+ /* TODO: abort SMK/STK handshake and remove all related keys */
+ }
+
+ return 0;
+}
+
+
+static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ const struct wpa_eapol_key *key,
+ u16 ver)
+{
+ struct wpa_eapol_ie_parse ie;
+ const u8 *kde;
+ size_t len, kde_buf_len;
+ struct wpa_ptk *stk;
+ u8 buf[8], *kde_buf, *pos;
+ be32 lifetime;
+
+ wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+ os_memset(&ie, 0, sizeof(ie));
+
+ /* RSN: msg 1/4 should contain SMKID for the selected SMK */
+ kde = (const u8 *) (key + 1);
+ len = WPA_GET_BE16(key->key_data_length);
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", kde, len);
+ if (wpa_supplicant_parse_ies(kde, len, &ie) < 0 || ie.pmkid == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4");
+ return;
+ }
+ if (os_memcmp(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4",
+ ie.pmkid, PMKID_LEN);
+ return;
+ }
+
+ if (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_CCMP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ if (wpa_sm_get_bssid(sm, bssid) < 0) {
+ wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
+ "SMK M1");
+ return -1;
+ }
+
+ /* TODO: find existing entry and if found, use that instead of adding
+ * a new one */
+ peerkey = os_zalloc(sizeof(*peerkey));
+ if (peerkey == NULL)
+ return -1;
+ peerkey->initiator = 1;
+ os_memcpy(peerkey->addr, peer, ETH_ALEN);
+#ifdef CONFIG_IEEE80211W
+ if (wpa_key_mgmt_sha256(sm->key_mgmt))
+ peerkey->use_sha256 = 1;
+#endif /* CONFIG_IEEE80211W */
+
+ /* SMK M1:
+ * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
+ * MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE))
+ */
+
+ hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+ /* Group Suite can be anything for SMK RSN IE; receiver will just
+ * ignore it. */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ count_pos = pos;
+ pos += 2;
+
+ count = 0;
+ if (sm->allowed_pairwise_cipher & WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ count++;
+ }
+ if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ pos += RSN_SELECTOR_LEN;
+ count++;
+ }
+ WPA_PUT_LE16(count_pos, count);
+
+ hdr->len = (pos - peerkey->rsnie_i) - 2;
+ peerkey->rsnie_i_len = pos - peerkey->rsnie_i;
+ wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
+ peerkey->rsnie_i, peerkey->rsnie_i_len);
+
+ kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*req) + kde_len, &rlen,
+ (void *) &req);
+ if (rbuf == NULL) {
+ wpa_supplicant_peerkey_free(sm, peerkey);
+ return -1;
+ }
+
+ req->type = EAPOL_KEY_TYPE_RSN;
+ key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver;
+ WPA_PUT_BE16(req->key_info, key_info);
+ WPA_PUT_BE16(req->key_length, 0);
+ os_memcpy(req->replay_counter, sm->request_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+ if (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/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h
new file mode 100644
index 0000000..2613127
--- /dev/null
+++ b/src/rsn_supp/peerkey.h
@@ -0,0 +1,87 @@
+/*
+ * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
+ * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PEERKEY_H
+#define PEERKEY_H
+
+#define PEERKEY_MAX_IE_LEN 80
+struct wpa_peerkey {
+ struct wpa_peerkey *next;
+ int initiator; /* whether this end was initator for SMK handshake */
+ u8 addr[ETH_ALEN]; /* other end MAC address */
+ u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
+ u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */
+ u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */
+ size_t rsnie_i_len;
+ u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */
+ size_t rsnie_p_len;
+ u8 smk[PMK_LEN];
+ int smk_complete;
+ u8 smkid[PMKID_LEN];
+ u32 lifetime;
+ os_time_t expiration;
+ int cipher; /* Selected cipher (WPA_CIPHER_*) */
+ u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+ int replay_counter_set;
+ int use_sha256; /* whether AKMP indicate SHA256-based derivations */
+
+ struct wpa_ptk stk, tstk;
+ int stk_set, tstk_set;
+};
+
+
+#ifdef CONFIG_PEERKEY
+
+int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 ver,
+ const u8 *buf, size_t len);
+void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 key_info, u16 ver);
+void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
+ struct wpa_eapol_key *key, size_t extra_len,
+ u16 key_info, u16 ver);
+void peerkey_deinit(struct wpa_sm *sm);
+
+#else /* CONFIG_PEERKEY */
+
+static inline int
+peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 ver,
+ const u8 *buf, size_t len)
+{
+ return -1;
+}
+
+static inline void
+peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 key_info, u16 ver)
+{
+}
+
+static inline void
+peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
+ struct wpa_eapol_key *key, size_t extra_len,
+ u16 key_info, u16 ver)
+{
+}
+
+static inline void peerkey_deinit(struct wpa_sm *sm)
+{
+}
+
+#endif /* CONFIG_PEERKEY */
+
+#endif /* PEERKEY_H */
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
new file mode 100644
index 0000000..cac8c83
--- /dev/null
+++ b/src/rsn_supp/pmksa_cache.c
@@ -0,0 +1,476 @@
+/*
+ * WPA Supplicant - RSN PMKSA cache
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "wpa.h"
+#include "wpa_i.h"
+#include "pmksa_cache.h"
+
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
+
+static const int pmksa_cache_max_entries = 32;
+
+struct rsn_pmksa_cache {
+ struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
+ int pmksa_count; /* number of entries in PMKSA cache */
+ struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
+
+ void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
+ int replace);
+ void *ctx;
+};
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
+
+
+static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
+{
+ os_free(entry);
+}
+
+
+static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry,
+ int replace)
+{
+ pmksa->pmksa_count--;
+ pmksa->free_cb(entry, pmksa->ctx, replace);
+ _pmksa_cache_free_entry(entry);
+}
+
+
+static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+ struct rsn_pmksa_cache *pmksa = eloop_ctx;
+ struct os_time now;
+
+ os_get_time(&now);
+ while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+ pmksa->pmksa = entry->next;
+ wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
+ MACSTR, MAC2STR(entry->aa));
+ pmksa_cache_free_entry(pmksa, entry, 0);
+ }
+
+ pmksa_cache_set_expiration(pmksa);
+}
+
+
+static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
+{
+ struct rsn_pmksa_cache *pmksa = eloop_ctx;
+ pmksa->sm->cur_pmksa = NULL;
+ eapol_sm_request_reauth(pmksa->sm->eapol);
+}
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
+{
+ int sec;
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_time now;
+
+ eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+ eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
+ if (pmksa->pmksa == NULL)
+ return;
+ os_get_time(&now);
+ sec = pmksa->pmksa->expiration - now.sec;
+ if (sec < 0)
+ sec = 0;
+ eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
+
+ entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
+ pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL);
+ if (entry) {
+ sec = pmksa->pmksa->reauth_time - now.sec;
+ if (sec < 0)
+ sec = 0;
+ eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
+ NULL);
+ }
+}
+
+
+/**
+ * pmksa_cache_add - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @network_ctx: Network configuration context for this PMK
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
+ * cache. If an old entry is already in the cache for the same Authenticator,
+ * this entry will be replaced with the new entry. PMKID will be calculated
+ * based on the PMK and the driver interface is notified of the new PMKID.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+ const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
+{
+ struct rsn_pmksa_cache_entry *entry, *pos, *prev;
+ struct os_time now;
+
+ if (pmk_len > PMK_LEN)
+ return NULL;
+
+ entry = os_zalloc(sizeof(*entry));
+ if (entry == NULL)
+ return NULL;
+ os_memcpy(entry->pmk, pmk, pmk_len);
+ entry->pmk_len = pmk_len;
+ rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
+ wpa_key_mgmt_sha256(akmp));
+ os_get_time(&now);
+ entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
+ entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
+ pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
+ entry->akmp = akmp;
+ os_memcpy(entry->aa, aa, ETH_ALEN);
+ entry->network_ctx = network_ctx;
+
+ /* Replace an old entry for the same Authenticator (if found) with the
+ * new entry */
+ pos = pmksa->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
+ if (pos->pmk_len == pmk_len &&
+ os_memcmp(pos->pmk, pmk, pmk_len) == 0 &&
+ os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) ==
+ 0) {
+ wpa_printf(MSG_DEBUG, "WPA: reusing previous "
+ "PMKSA entry");
+ os_free(entry);
+ return pos;
+ }
+ if (prev == NULL)
+ pmksa->pmksa = pos->next;
+ else
+ prev->next = pos->next;
+ if (pos == pmksa->sm->cur_pmksa) {
+ /* We are about to replace the current PMKSA
+ * cache entry. This happens when the PMKSA
+ * caching attempt fails, so we don't want to
+ * force pmksa_cache_free_entry() to disconnect
+ * at this point. Let's just make sure the old
+ * PMKSA cache entry will not be used in the
+ * future.
+ */
+ wpa_printf(MSG_DEBUG, "RSN: replacing current "
+ "PMKSA entry");
+ pmksa->sm->cur_pmksa = NULL;
+ }
+ wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
+ "the current AP");
+ pmksa_cache_free_entry(pmksa, pos, 1);
+ break;
+ }
+ prev = pos;
+ pos = pos->next;
+ }
+
+ if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
+ /* Remove the oldest entry to make room for the new entry */
+ pos = pmksa->pmksa;
+ pmksa->pmksa = pos->next;
+ wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
+ "entry (for " MACSTR ") to make room for new one",
+ MAC2STR(pos->aa));
+ wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid);
+ pmksa_cache_free_entry(pmksa, pos, 0);
+ }
+
+ /* Add the new entry; order by expiration time */
+ pos = pmksa->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (pos->expiration > entry->expiration)
+ break;
+ prev = pos;
+ pos = pos->next;
+ }
+ if (prev == NULL) {
+ entry->next = pmksa->pmksa;
+ pmksa->pmksa = entry;
+ pmksa_cache_set_expiration(pmksa);
+ } else {
+ entry->next = prev->next;
+ prev->next = entry;
+ }
+ pmksa->pmksa_count++;
+ wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
+ MAC2STR(entry->aa));
+ wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
+
+ return entry;
+}
+
+
+/**
+ * pmksa_cache_deinit - Free all entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ */
+void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+ struct rsn_pmksa_cache_entry *entry, *prev;
+
+ if (pmksa == NULL)
+ return;
+
+ entry = pmksa->pmksa;
+ pmksa->pmksa = NULL;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ os_free(prev);
+ }
+ pmksa_cache_set_expiration(pmksa);
+ os_free(pmksa);
+}
+
+
+/**
+ * pmksa_cache_get - Fetch a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @aa: Authenticator address or %NULL to match any
+ * @pmkid: PMKID or %NULL to match any
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
+ const u8 *aa, const u8 *pmkid)
+{
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+ while (entry) {
+ if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
+ (pmkid == NULL ||
+ os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
+ return entry;
+ entry = entry->next;
+ }
+ return NULL;
+}
+
+
+/**
+ * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ *
+ * Clear references to old data structures when wpa_supplicant is reconfigured.
+ */
+void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
+{
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+ while (entry) {
+ entry->network_ctx = NULL;
+ entry = entry->next;
+ }
+}
+
+
+static struct rsn_pmksa_cache_entry *
+pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
+ const struct rsn_pmksa_cache_entry *old_entry,
+ const u8 *aa)
+{
+ struct rsn_pmksa_cache_entry *new_entry;
+
+ new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
+ aa, pmksa->sm->own_addr,
+ old_entry->network_ctx, old_entry->akmp);
+ if (new_entry == NULL)
+ return NULL;
+
+ /* TODO: reorder entries based on expiration time? */
+ new_entry->expiration = old_entry->expiration;
+ new_entry->opportunistic = 1;
+
+ return new_entry;
+}
+
+
+/**
+ * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @network_ctx: Network configuration context
+ * @aa: Authenticator address for the new AP
+ * Returns: Pointer to a new PMKSA cache entry or %NULL if not available
+ *
+ * Try to create a new PMKSA cache entry opportunistically by guessing that the
+ * new AP is sharing the same PMK as another AP that has the same SSID and has
+ * already an entry in PMKSA cache.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+ const u8 *aa)
+{
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+
+ if (network_ctx == NULL)
+ return NULL;
+ while (entry) {
+ if (entry->network_ctx == network_ctx) {
+ entry = pmksa_cache_clone_entry(pmksa, entry, aa);
+ if (entry) {
+ wpa_printf(MSG_DEBUG, "RSN: added "
+ "opportunistic PMKSA cache entry "
+ "for " MACSTR, MAC2STR(aa));
+ }
+ return entry;
+ }
+ entry = entry->next;
+ }
+ return NULL;
+}
+
+
+/**
+ * pmksa_cache_get_current - Get the current used PMKSA entry
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: Pointer to the current PMKSA cache entry or %NULL if not available
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return NULL;
+ return sm->cur_pmksa;
+}
+
+
+/**
+ * pmksa_cache_clear_current - Clear the current PMKSA entry selection
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void pmksa_cache_clear_current(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ sm->cur_pmksa = NULL;
+}
+
+
+/**
+ * pmksa_cache_set_current - Set the current PMKSA entry selection
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @pmkid: PMKID for selecting PMKSA or %NULL if not used
+ * @bssid: BSSID for PMKSA or %NULL if not used
+ * @network_ctx: Network configuration context
+ * @try_opportunistic: Whether to allow opportunistic PMKSA caching
+ * Returns: 0 if PMKSA was found or -1 if no matching entry was found
+ */
+int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+ const u8 *bssid, void *network_ctx,
+ int try_opportunistic)
+{
+ struct rsn_pmksa_cache *pmksa = sm->pmksa;
+ sm->cur_pmksa = NULL;
+ if (pmkid)
+ sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid);
+ if (sm->cur_pmksa == NULL && bssid)
+ sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL);
+ if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
+ sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
+ network_ctx,
+ bssid);
+ if (sm->cur_pmksa) {
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
+ sm->cur_pmksa->pmkid, PMKID_LEN);
+ return 0;
+ }
+ return -1;
+}
+
+
+/**
+ * pmksa_cache_list - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA command.
+ */
+int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+{
+ int i, ret;
+ char *pos = buf;
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_time now;
+
+ os_get_time(&now);
+ ret = os_snprintf(pos, buf + len - pos,
+ "Index / AA / PMKID / expiration (in seconds) / "
+ "opportunistic\n");
+ if (ret < 0 || ret >= buf + len - pos)
+ return pos - buf;
+ pos += ret;
+ i = 0;
+ entry = pmksa->pmksa;
+ while (entry) {
+ i++;
+ ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
+ i, MAC2STR(entry->aa));
+ if (ret < 0 || ret >= buf + len - pos)
+ return pos - buf;
+ pos += ret;
+ pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
+ PMKID_LEN);
+ ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+ (int) (entry->expiration - now.sec),
+ entry->opportunistic);
+ if (ret < 0 || ret >= buf + len - pos)
+ return pos - buf;
+ pos += ret;
+ entry = entry->next;
+ }
+ return pos - buf;
+}
+
+
+/**
+ * pmksa_cache_init - Initialize PMKSA cache
+ * @free_cb: Callback function to be called when a PMKSA cache entry is freed
+ * @ctx: Context pointer for free_cb function
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: Pointer to PMKSA cache data or %NULL on failure
+ */
+struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, int replace),
+ void *ctx, struct wpa_sm *sm)
+{
+ struct rsn_pmksa_cache *pmksa;
+
+ pmksa = os_zalloc(sizeof(*pmksa));
+ if (pmksa) {
+ pmksa->free_cb = free_cb;
+ pmksa->ctx = ctx;
+ pmksa->sm = sm;
+ }
+
+ return pmksa;
+}
+
+#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
new file mode 100644
index 0000000..a1447e5
--- /dev/null
+++ b/src/rsn_supp/pmksa_cache.h
@@ -0,0 +1,127 @@
+/*
+ * wpa_supplicant - WPA2/RSN PMKSA cache functions
+ * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PMKSA_CACHE_H
+#define PMKSA_CACHE_H
+
+/**
+ * struct rsn_pmksa_cache_entry - PMKSA cache entry
+ */
+struct rsn_pmksa_cache_entry {
+ struct rsn_pmksa_cache_entry *next;
+ u8 pmkid[PMKID_LEN];
+ u8 pmk[PMK_LEN];
+ size_t pmk_len;
+ os_time_t expiration;
+ int akmp; /* WPA_KEY_MGMT_* */
+ u8 aa[ETH_ALEN];
+
+ os_time_t reauth_time;
+
+ /**
+ * network_ctx - Network configuration context
+ *
+ * This field is only used to match PMKSA cache entries to a specific
+ * network configuration (e.g., a specific SSID and security policy).
+ * This can be a pointer to the configuration entry, but PMKSA caching
+ * code does not dereference the value and this could be any kind of
+ * identifier.
+ */
+ void *network_ctx;
+ int opportunistic;
+};
+
+struct rsn_pmksa_cache;
+
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
+
+struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, int replace),
+ void *ctx, struct wpa_sm *sm);
+void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
+ const u8 *aa, const u8 *pmkid);
+int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+ const u8 *aa, const u8 *spa, void *network_ctx, int akmp);
+void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
+void pmksa_cache_clear_current(struct wpa_sm *sm);
+int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+ const u8 *bssid, void *network_ctx,
+ int try_opportunistic);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
+ void *network_ctx, const u8 *aa);
+
+#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
+
+static inline struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, int replace),
+ void *ctx, struct wpa_sm *sm)
+{
+ return (void *) -1;
+}
+
+static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid)
+{
+ return NULL;
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_get_current(struct wpa_sm *sm)
+{
+ return NULL;
+}
+
+static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf,
+ size_t len)
+{
+ return -1;
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+ const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
+{
+ return NULL;
+}
+
+static inline void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
+{
+}
+
+static inline void pmksa_cache_clear_current(struct wpa_sm *sm)
+{
+}
+
+static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+ const u8 *bssid,
+ void *network_ctx,
+ int try_opportunistic)
+{
+ return -1;
+}
+
+#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
+
+#endif /* PMKSA_CACHE_H */
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
new file mode 100644
index 0000000..6109f5e
--- /dev/null
+++ b/src/rsn_supp/preauth.c
@@ -0,0 +1,518 @@
+/*
+ * RSN pre-authentication (supplicant)
+ * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa.h"
+#include "eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "preauth.h"
+#include "pmksa_cache.h"
+#include "wpa_i.h"
+#include "common/ieee802_11_defs.h"
+
+
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
+
+#define PMKID_CANDIDATE_PRIO_SCAN 1000
+
+
+struct rsn_pmksa_candidate {
+ struct dl_list list;
+ u8 bssid[ETH_ALEN];
+ int priority;
+};
+
+
+/**
+ * pmksa_candidate_free - Free all entries in PMKSA candidate list
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void pmksa_candidate_free(struct wpa_sm *sm)
+{
+ struct rsn_pmksa_candidate *entry, *n;
+
+ if (sm == NULL)
+ return;
+
+ dl_list_for_each_safe(entry, n, &sm->pmksa_candidates,
+ struct rsn_pmksa_candidate, list) {
+ dl_list_del(&entry->list);
+ os_free(entry);
+ }
+}
+
+
+static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_sm *sm = ctx;
+
+ wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr));
+ wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len);
+
+ if (sm->preauth_eapol == NULL ||
+ is_zero_ether_addr(sm->preauth_bssid) ||
+ os_memcmp(sm->preauth_bssid, src_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_WARNING, "RSN pre-auth frame received from "
+ "unexpected source " MACSTR " - dropped",
+ MAC2STR(src_addr));
+ return;
+ }
+
+ eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len);
+}
+
+
+static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success,
+ void *ctx)
+{
+ struct wpa_sm *sm = ctx;
+ u8 pmk[PMK_LEN];
+
+ if (success) {
+ int res, pmk_len;
+ pmk_len = PMK_LEN;
+ res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
+ if (res) {
+ /*
+ * EAP-LEAP is an exception from other EAP methods: it
+ * uses only 16-byte PMK.
+ */
+ res = eapol_sm_get_key(eapol, pmk, 16);
+ pmk_len = 16;
+ }
+ if (res == 0) {
+ wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth",
+ pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ pmksa_cache_add(sm->pmksa, pmk, pmk_len,
+ sm->preauth_bssid, sm->own_addr,
+ sm->network_ctx,
+ WPA_KEY_MGMT_IEEE8021X);
+ } else {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: failed to get master session key from "
+ "pre-auth EAPOL state machines");
+ success = 0;
+ }
+ }
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
+ MACSTR " %s", MAC2STR(sm->preauth_bssid),
+ success ? "completed successfully" : "failed");
+
+ rsn_preauth_deinit(sm);
+ rsn_preauth_candidate_process(sm);
+}
+
+
+static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_sm *sm = eloop_ctx;
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
+ MACSTR " timed out", MAC2STR(sm->preauth_bssid));
+ rsn_preauth_deinit(sm);
+ rsn_preauth_candidate_process(sm);
+}
+
+
+static int rsn_preauth_eapol_send(void *ctx, int type, const u8 *buf,
+ size_t len)
+{
+ struct wpa_sm *sm = ctx;
+ u8 *msg;
+ size_t msglen;
+ int res;
+
+ /* TODO: could add l2_packet_sendmsg that allows fragments to avoid
+ * extra copy here */
+
+ if (sm->l2_preauth == NULL)
+ return -1;
+
+ msg = wpa_sm_alloc_eapol(sm, type, buf, len, &msglen, NULL);
+ if (msg == NULL)
+ return -1;
+
+ wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen);
+ res = l2_packet_send(sm->l2_preauth, sm->preauth_bssid,
+ ETH_P_RSN_PREAUTH, msg, msglen);
+ os_free(msg);
+ return res;
+}
+
+
+/**
+ * rsn_preauth_init - Start new RSN pre-authentication
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Authenticator address (BSSID) with which to preauthenticate
+ * @eap_conf: Current EAP configuration
+ * Returns: 0 on success, -1 on another pre-authentication is in progress,
+ * -2 on layer 2 packet initialization failure, -3 on EAPOL state machine
+ * initialization failure, -4 on memory allocation failure
+ *
+ * This function request an RSN pre-authentication with a given destination
+ * address. This is usually called for PMKSA candidates found from scan results
+ * or from driver reports. In addition, ctrl_iface PREAUTH command can trigger
+ * pre-authentication.
+ */
+int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+ struct eap_peer_config *eap_conf)
+{
+ struct eapol_config eapol_conf;
+ struct eapol_ctx *ctx;
+
+ if (sm->preauth_eapol)
+ return -1;
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: starting pre-authentication with " MACSTR, MAC2STR(dst));
+
+ sm->l2_preauth = l2_packet_init(sm->ifname, sm->own_addr,
+ ETH_P_RSN_PREAUTH,
+ rsn_preauth_receive, sm, 0);
+ if (sm->l2_preauth == NULL) {
+ wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet "
+ "processing for pre-authentication");
+ return -2;
+ }
+
+ if (sm->bridge_ifname) {
+ sm->l2_preauth_br = l2_packet_init(sm->bridge_ifname,
+ sm->own_addr,
+ ETH_P_RSN_PREAUTH,
+ rsn_preauth_receive, sm, 0);
+ if (sm->l2_preauth_br == NULL) {
+ wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 "
+ "packet processing (bridge) for "
+ "pre-authentication");
+ return -2;
+ }
+ }
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context.");
+ return -4;
+ }
+ ctx->ctx = sm->ctx->ctx;
+ ctx->msg_ctx = sm->ctx->ctx;
+ ctx->preauth = 1;
+ ctx->cb = rsn_preauth_eapol_cb;
+ ctx->cb_ctx = sm;
+ ctx->scard_ctx = sm->scard_ctx;
+ ctx->eapol_send = rsn_preauth_eapol_send;
+ ctx->eapol_send_ctx = sm;
+ ctx->set_config_blob = sm->ctx->set_config_blob;
+ ctx->get_config_blob = sm->ctx->get_config_blob;
+
+ sm->preauth_eapol = eapol_sm_init(ctx);
+ if (sm->preauth_eapol == NULL) {
+ os_free(ctx);
+ wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL "
+ "state machines for pre-authentication");
+ return -3;
+ }
+ os_memset(&eapol_conf, 0, sizeof(eapol_conf));
+ eapol_conf.accept_802_1x_keys = 0;
+ eapol_conf.required_keys = 0;
+ eapol_conf.fast_reauth = sm->fast_reauth;
+ eapol_conf.workaround = sm->eap_workaround;
+ eapol_sm_notify_config(sm->preauth_eapol, eap_conf, &eapol_conf);
+ /*
+ * Use a shorter startPeriod with preauthentication since the first
+ * preauth EAPOL-Start frame may end up being dropped due to race
+ * condition in the AP between the data receive and key configuration
+ * after the 4-Way Handshake.
+ */
+ eapol_sm_configure(sm->preauth_eapol, -1, -1, 5, 6);
+ os_memcpy(sm->preauth_bssid, dst, ETH_ALEN);
+
+ eapol_sm_notify_portValid(sm->preauth_eapol, TRUE);
+ /* 802.1X::portControl = Auto */
+ eapol_sm_notify_portEnabled(sm->preauth_eapol, TRUE);
+
+ eloop_register_timeout(sm->dot11RSNAConfigSATimeout, 0,
+ rsn_preauth_timeout, sm, NULL);
+
+ return 0;
+}
+
+
+/**
+ * rsn_preauth_deinit - Abort RSN pre-authentication
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * This function aborts the current RSN pre-authentication (if one is started)
+ * and frees resources allocated for it.
+ */
+void rsn_preauth_deinit(struct wpa_sm *sm)
+{
+ if (sm == NULL || !sm->preauth_eapol)
+ return;
+
+ eloop_cancel_timeout(rsn_preauth_timeout, sm, NULL);
+ eapol_sm_deinit(sm->preauth_eapol);
+ sm->preauth_eapol = NULL;
+ os_memset(sm->preauth_bssid, 0, ETH_ALEN);
+
+ l2_packet_deinit(sm->l2_preauth);
+ sm->l2_preauth = NULL;
+ if (sm->l2_preauth_br) {
+ l2_packet_deinit(sm->l2_preauth_br);
+ sm->l2_preauth_br = NULL;
+ }
+}
+
+
+/**
+ * rsn_preauth_candidate_process - Process PMKSA candidates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Go through the PMKSA candidates and start pre-authentication if a candidate
+ * without an existing PMKSA cache entry is found. Processed candidates will be
+ * removed from the list.
+ */
+void rsn_preauth_candidate_process(struct wpa_sm *sm)
+{
+ struct rsn_pmksa_candidate *candidate, *n;
+
+ if (dl_list_empty(&sm->pmksa_candidates))
+ return;
+
+ /* TODO: drop priority for old candidate entries */
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: processing PMKSA candidate "
+ "list");
+ if (sm->preauth_eapol ||
+ sm->proto != WPA_PROTO_RSN ||
+ wpa_sm_get_state(sm) != WPA_COMPLETED ||
+ (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X &&
+ sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable "
+ "state for new pre-authentication");
+ return; /* invalid state for new pre-auth */
+ }
+
+ dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates,
+ struct rsn_pmksa_candidate, list) {
+ struct rsn_pmksa_cache_entry *p = NULL;
+ p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL);
+ if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 &&
+ (p == NULL || p->opportunistic)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA "
+ "candidate " MACSTR
+ " selected for pre-authentication",
+ MAC2STR(candidate->bssid));
+ dl_list_del(&candidate->list);
+ rsn_preauth_init(sm, candidate->bssid,
+ sm->eap_conf_ctx);
+ os_free(candidate);
+ return;
+ }
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA candidate "
+ MACSTR " does not need pre-authentication anymore",
+ MAC2STR(candidate->bssid));
+ /* Some drivers (e.g., NDIS) expect to get notified about the
+ * PMKIDs again, so report the existing data now. */
+ if (p) {
+ wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid);
+ }
+
+ dl_list_del(&candidate->list);
+ os_free(candidate);
+ }
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no more pending PMKSA "
+ "candidates");
+}
+
+
+/**
+ * pmksa_candidate_add - Add a new PMKSA candidate
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @bssid: BSSID (authenticator address) of the candidate
+ * @prio: Priority (the smaller number, the higher priority)
+ * @preauth: Whether the candidate AP advertises support for pre-authentication
+ *
+ * This function is used to add PMKSA candidates for RSN pre-authentication. It
+ * is called from scan result processing and from driver events for PMKSA
+ * candidates, i.e., EVENT_PMKID_CANDIDATE events to wpa_supplicant_event().
+ */
+void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
+ int prio, int preauth)
+{
+ struct rsn_pmksa_candidate *cand, *pos;
+
+ if (sm->network_ctx && sm->proactive_key_caching)
+ pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx,
+ bssid);
+
+ if (!preauth) {
+ wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without "
+ "preauth flag");
+ return;
+ }
+
+ /* If BSSID already on candidate list, update the priority of the old
+ * entry. Do not override priority based on normal scan results. */
+ cand = NULL;
+ dl_list_for_each(pos, &sm->pmksa_candidates,
+ struct rsn_pmksa_candidate, list) {
+ if (os_memcmp(pos->bssid, bssid, ETH_ALEN) == 0) {
+ cand = pos;
+ break;
+ }
+ }
+
+ if (cand) {
+ dl_list_del(&cand->list);
+ if (prio < PMKID_CANDIDATE_PRIO_SCAN)
+ cand->priority = prio;
+ } else {
+ cand = os_zalloc(sizeof(*cand));
+ if (cand == NULL)
+ return;
+ os_memcpy(cand->bssid, bssid, ETH_ALEN);
+ cand->priority = prio;
+ }
+
+ /* Add candidate to the list; order by increasing priority value. i.e.,
+ * highest priority (smallest value) first. */
+ dl_list_for_each(pos, &sm->pmksa_candidates,
+ struct rsn_pmksa_candidate, list) {
+ if (cand->priority <= pos->priority) {
+ dl_list_add(pos->list.prev, &cand->list);
+ cand = NULL;
+ break;
+ }
+ }
+ if (cand)
+ dl_list_add_tail(&sm->pmksa_candidates, &cand->list);
+
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: added PMKSA cache "
+ "candidate " MACSTR " prio %d", MAC2STR(bssid), prio);
+ rsn_preauth_candidate_process(sm);
+}
+
+
+/* TODO: schedule periodic scans if current AP supports preauth */
+
+/**
+ * rsn_preauth_scan_results - Start processing scan results for canditates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: 0 if ready to process results or -1 to skip processing
+ *
+ * This functions is used to notify RSN code about start of new scan results
+ * processing. The actual scan results will be provided by calling
+ * rsn_preauth_scan_result() for each BSS if this function returned 0.
+ */
+int rsn_preauth_scan_results(struct wpa_sm *sm)
+{
+ if (sm->ssid_len == 0)
+ return -1;
+
+ /*
+ * TODO: is it ok to free all candidates? What about the entries
+ * received from EVENT_PMKID_CANDIDATE?
+ */
+ pmksa_candidate_free(sm);
+
+ return 0;
+}
+
+
+/**
+ * rsn_preauth_scan_result - Processing scan result for PMKSA canditates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Add all suitable APs (Authenticators) from scan results into PMKSA
+ * candidate list.
+ */
+void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *ssid, const u8 *rsn)
+{
+ struct wpa_ie_data ie;
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ if (ssid[1] != sm->ssid_len ||
+ os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0)
+ return; /* Not for the current SSID */
+
+ if (os_memcmp(bssid, sm->bssid, ETH_ALEN) == 0)
+ return; /* Ignore current AP */
+
+ if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
+ return;
+
+ pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL);
+ if (pmksa && (!pmksa->opportunistic ||
+ !(ie.capabilities & WPA_CAPABILITY_PREAUTH)))
+ return;
+
+ /* Give less priority to candidates found from normal scan results. */
+ pmksa_candidate_add(sm, bssid, PMKID_CANDIDATE_PRIO_SCAN,
+ ie.capabilities & WPA_CAPABILITY_PREAUTH);
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+/**
+ * rsn_preauth_get_status - Get pre-authentication status
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query WPA2 pre-authentication for status information. This function fills in
+ * a text area with current status information. If the buffer (buf) is not
+ * large enough, status information will be truncated to fit the buffer.
+ */
+int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+ int verbose)
+{
+ char *pos = buf, *end = buf + buflen;
+ int res, ret;
+
+ if (sm->preauth_eapol) {
+ ret = os_snprintf(pos, end - pos, "Pre-authentication "
+ "EAPOL state machines:\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ res = eapol_sm_get_status(sm->preauth_eapol,
+ pos, end - pos, verbose);
+ if (res >= 0)
+ pos += res;
+ }
+
+ return pos - buf;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+/**
+ * rsn_preauth_in_progress - Verify whether pre-authentication is in progress
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+int rsn_preauth_in_progress(struct wpa_sm *sm)
+{
+ return sm->preauth_eapol != NULL;
+}
+
+#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
diff --git a/src/rsn_supp/preauth.h b/src/rsn_supp/preauth.h
new file mode 100644
index 0000000..f8240ab
--- /dev/null
+++ b/src/rsn_supp/preauth.h
@@ -0,0 +1,85 @@
+/*
+ * wpa_supplicant - WPA2/RSN pre-authentication functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PREAUTH_H
+#define PREAUTH_H
+
+struct wpa_scan_results;
+
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
+
+void pmksa_candidate_free(struct wpa_sm *sm);
+int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+ struct eap_peer_config *eap_conf);
+void rsn_preauth_deinit(struct wpa_sm *sm);
+int rsn_preauth_scan_results(struct wpa_sm *sm);
+void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *ssid, const u8 *rsn);
+void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
+ int prio, int preauth);
+void rsn_preauth_candidate_process(struct wpa_sm *sm);
+int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+ int verbose);
+int rsn_preauth_in_progress(struct wpa_sm *sm);
+
+#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
+
+static inline void pmksa_candidate_free(struct wpa_sm *sm)
+{
+}
+
+static inline void rsn_preauth_candidate_process(struct wpa_sm *sm)
+{
+}
+
+static inline int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+ struct eap_peer_config *eap_conf)
+{
+ return -1;
+}
+
+static inline void rsn_preauth_deinit(struct wpa_sm *sm)
+{
+}
+
+static inline int rsn_preauth_scan_results(struct wpa_sm *sm)
+{
+ return -1;
+}
+
+static inline void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *ssid, const u8 *rsn)
+{
+}
+
+static inline void pmksa_candidate_add(struct wpa_sm *sm,
+ const u8 *bssid,
+ int prio, int preauth)
+{
+}
+
+static inline int rsn_preauth_get_status(struct wpa_sm *sm, char *buf,
+ size_t buflen, int verbose)
+{
+ return 0;
+}
+
+static inline int rsn_preauth_in_progress(struct wpa_sm *sm)
+{
+ return 0;
+}
+
+#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
+
+#endif /* PREAUTH_H */
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
new file mode 100644
index 0000000..e751867
--- /dev/null
+++ b/src/rsn_supp/tdls.c
@@ -0,0 +1,2069 @@
+/*
+ * wpa_supplicant - TDLS
+ * Copyright (c) 2010-2011, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "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_RETRY_COUNT 3
+#define TPK_TIMEOUT 5000 /* 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);
+
+
+#define TDLS_MAX_IE_LEN 80
+struct wpa_tdls_peer {
+ struct wpa_tdls_peer *next;
+ 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;
+
+ 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;
+};
+
+
+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)
+ 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);
+
+ peer->sm_tmr.count = TPK_RETRY_COUNT;
+ peer->sm_tmr.timer = TPK_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, 0,
+ wpa_tdls_tpk_retry_timeout, sm, peer);
+ return 0;
+}
+
+
+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--;
+ peer->sm_tmr.timer = TPK_TIMEOUT;
+
+ 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, 0,
+ wpa_tdls_tpk_retry_timeout, sm, peer);
+ } else {
+ wpa_printf(MSG_INFO, "Sending Tear_Down Request");
+ wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
+
+ wpa_printf(MSG_INFO, "Clearing SM: Peerkey(" MACSTR ")",
+ MAC2STR(peer->addr));
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+ /* clear the Peerkey statemachine */
+ wpa_tdls_peer_free(sm, peer);
+ }
+}
+
+
+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_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
+ }
+}
+
+
+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->initiator = 0;
+ os_free(peer->sm_tmr.buf);
+ peer->sm_tmr.buf = 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_recv_teardown_notify(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)
+ 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 */
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake",
+ (u8 *) ftie, sizeof(*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,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, rbuf,
+ pos - rbuf);
+ os_free(rbuf);
+
+ /* clear the Peerkey statemachine */
+ wpa_tdls_peer_free(sm, peer);
+
+ return 0;
+}
+
+
+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_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+
+ /* clear the Peerkey statemachine */
+ wpa_tdls_peer_free(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 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;
+
+ 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));
+
+ wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 0, 0,
+ rbuf, pos - rbuf);
+ os_free(rbuf);
+
+ return 0;
+}
+
+
+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;
+
+ 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:
+ wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
+ rbuf, pos - rbuf);
+ os_free(rbuf);
+
+ return 0;
+}
+
+
+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;
+
+ 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:
+ wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0,
+ rbuf, pos - rbuf);
+ os_free(rbuf);
+
+ 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;
+
+ 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);
+
+ cpos += 2; /* capability information */
+
+ 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));
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (peer == NULL) {
+ peer = os_zalloc(sizeof(*peer));
+ if (peer == NULL)
+ goto error;
+ os_memcpy(peer->addr, src_addr, ETH_ALEN);
+ peer->next = sm->tdls;
+ sm->tdls = peer;
+ }
+ 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:
+ /* 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? */
+ 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 "
+ "peer, creating one for " MACSTR,
+ MAC2STR(src_addr));
+ peer = os_malloc(sizeof(*peer));
+ if (peer == NULL)
+ goto error;
+ os_memset(peer, 0, sizeof(*peer));
+ os_memcpy(peer->addr, src_addr, ETH_ALEN);
+ peer->next = sm->tdls;
+ sm->tdls = peer;
+ } else {
+ if (peer->tpk_success) {
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
+ "direct link is enabled - tear down the "
+ "old link first");
+#if 0
+ /* TODO: Disabling the link would be more proper
+ * operation here, but it seems to trigger a race with
+ * some drivers handling the new request frame. */
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+#else
+ wpa_tdls_del_key(sm, peer);
+#endif
+ wpa_tdls_peer_free(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_peer_free(sm, peer);
+ }
+ }
+ }
+
+ 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->inonce, ftie->Snonce, WPA_NONCE_LEN);
+ 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_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:
+ wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
+ wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer);
+
+ return 0;
+
+error:
+ wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken,
+ status);
+ return -1;
+}
+
+
+static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+ peer->tpk_success = 1;
+ 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 */
+ }
+ 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;
+
+ 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;
+ }
+ wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
+
+ if (len < 3 + 2 + 1)
+ 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);
+ 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)
+ return -1;
+ pos += 2; /* capability information */
+
+ 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 (!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_peer_free(sm, peer);
+ return -1;
+ }
+
+ wpa_tdls_set_key(sm, peer);
+
+skip_rsn:
+ peer->dtoken = dtoken;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
+ "TPK Handshake Message 3");
+ wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer);
+
+ wpa_tdls_enable_link(sm, peer);
+
+ return 0;
+
+error:
+ wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken,
+ status);
+ 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;
+
+ 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)
+ return -1;
+ 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);
+ return -1;
+ }
+ 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");
+ return -1;
+ }
+
+ if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+ wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3");
+ return -1;
+ }
+ 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");
+ return -1;
+ }
+
+ 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");
+ return -1;
+ }
+ 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");
+ return -1;
+ }
+ 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");
+ return -1;
+ }
+
+ 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");
+ return -1;
+ }
+
+ 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");
+ return -1;
+ }
+
+ if (kde.key_lifetime == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
+ return -1;
+ }
+ 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);
+ return -1;
+ }
+
+ if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
+ (u8 *) timeoutie, ftie) < 0) {
+ wpa_tdls_del_key(sm, peer);
+ wpa_tdls_peer_free(sm, peer);
+ return -1;
+ }
+
+ if (wpa_tdls_set_key(sm, peer) < 0)
+ return -1;
+
+skip_rsn:
+ wpa_tdls_enable_link(sm, peer);
+
+ return 0;
+}
+
+
+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)
+ 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;
+ }
+
+ /* Find existing entry and if found, use that instead of adding
+ * a new one */
+ 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 "
+ "peer, creating one for " MACSTR, MAC2STR(addr));
+ peer = os_malloc(sizeof(*peer));
+ if (peer == NULL)
+ return -1;
+ os_memset(peer, 0, sizeof(*peer));
+ os_memcpy(peer->addr, addr, ETH_ALEN);
+ peer->next = sm->tdls;
+ sm->tdls = peer;
+ }
+
+ peer->initiator = 1;
+
+ return wpa_tdls_send_tpk_m1(sm, peer);
+}
+
+
+int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr)
+{
+ struct wpa_tdls_peer *peer;
+
+ if (sm->tdls_disabled)
+ return -1;
+
+ 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 -1;
+
+ return wpa_tdls_start(sm, addr);
+}
+
+
+/**
+ * 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) {
+ wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled");
+ 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;
+ 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->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;
+ }
+
+ return 0;
+}
+
+
+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;
+}
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
new file mode 100644
index 0000000..01a46dc
--- /dev/null
+++ b/src/rsn_supp/wpa.c
@@ -0,0 +1,2644 @@
+/*
+ * WPA Supplicant - WPA state machine and EAPOL-Key processing
+ * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "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_CCMP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ if (wpa_sm_get_bssid(sm, bssid) < 0) {
+ wpa_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);
+ 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) {
+ wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
+ "machines", sm->pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ if (sm->proto == WPA_PROTO_RSN) {
+ pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len,
+ src_addr, sm->own_addr,
+ sm->network_ctx, sm->key_mgmt);
+ }
+ if (!sm->cur_pmksa && pmkid &&
+ pmksa_cache_get(sm->pmksa, src_addr, pmkid)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: the new PMK matches with the "
+ "PMKID");
+ abort_cached = 0;
+ }
+ } else {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to get master session key from "
+ "EAPOL state machines - 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)) {
+ /* 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_CCMP ? 48 : 64;
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->key_mgmt))
+ return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len);
+#endif /* CONFIG_IEEE80211R */
+
+ wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
+ sm->own_addr, sm->bssid, sm->snonce, key->key_nonce,
+ (u8 *) ptk, ptk_len,
+ wpa_key_mgmt_sha256(sm->key_mgmt));
+ return 0;
+}
+
+
+static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ u16 ver)
+{
+ struct wpa_eapol_ie_parse ie;
+ struct wpa_ptk *ptk;
+ u8 buf[8];
+ int res;
+
+ if (wpa_sm_get_network_ctx(sm) == NULL) {
+ wpa_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));
+
+#ifndef CONFIG_NO_WPA2
+ if (sm->proto == WPA_PROTO_RSN) {
+ /* RSN: msg 1/4 should contain PMKID for the selected PMK */
+ const u8 *_buf = (const u8 *) (key + 1);
+ size_t len = WPA_GET_BE16(key->key_data_length);
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len);
+ wpa_supplicant_parse_ies(_buf, len, &ie);
+ if (ie.pmkid) {
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
+ "Authenticator", ie.pmkid, PMKID_LEN);
+ }
+ }
+#endif /* CONFIG_NO_WPA2 */
+
+ res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
+ if (res == -2) {
+ wpa_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
+ * likelyhood of the first preauth EAPOL-Start frame getting to
+ * the target AP.
+ */
+ eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL);
+ }
+
+ if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) {
+ wpa_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");
+
+ switch (sm->pairwise_cipher) {
+ case WPA_CIPHER_CCMP:
+ alg = WPA_ALG_CCMP;
+ keylen = 16;
+ rsclen = 6;
+ break;
+ case WPA_CIPHER_TKIP:
+ alg = WPA_ALG_TKIP;
+ keylen = 32;
+ rsclen = 6;
+ break;
+ case WPA_CIPHER_NONE:
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher "
+ "Suite: NONE - do not use pairwise keys");
+ return 0;
+ default:
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported pairwise cipher %d",
+ sm->pairwise_cipher);
+ return -1;
+ }
+
+ if (sm->proto == WPA_PROTO_RSN) {
+ key_rsc = null_rsc;
+ } else {
+ key_rsc = key->key_rsc;
+ wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
+ }
+
+ if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
+ (u8 *) sm->ptk.tk1, keylen) < 0) {
+ wpa_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 ret = 0;
+
+ switch (group_cipher) {
+ case WPA_CIPHER_CCMP:
+ if (keylen != 16 || maxkeylen < 16) {
+ ret = -1;
+ break;
+ }
+ *key_rsc_len = 6;
+ *alg = WPA_ALG_CCMP;
+ break;
+ case WPA_CIPHER_TKIP:
+ if (keylen != 32 || maxkeylen < 32) {
+ ret = -1;
+ break;
+ }
+ *key_rsc_len = 6;
+ *alg = WPA_ALG_TKIP;
+ break;
+ case WPA_CIPHER_WEP104:
+ if (keylen != 13 || maxkeylen < 13) {
+ ret = -1;
+ break;
+ }
+ *key_rsc_len = 0;
+ *alg = WPA_ALG_WEP;
+ break;
+ case WPA_CIPHER_WEP40:
+ if (keylen != 5 || maxkeylen < 5) {
+ ret = -1;
+ break;
+ }
+ *key_rsc_len = 0;
+ *alg = WPA_ALG_WEP;
+ break;
+ default:
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported Group Cipher %d",
+ group_cipher);
+ return -1;
+ }
+
+ if (ret < 0 ) {
+ 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 ret;
+}
+
+
+struct wpa_gtk_data {
+ enum wpa_alg alg;
+ int tx, key_rsc_len, keyidx;
+ u8 gtk[32];
+ int gtk_len;
+};
+
+
+static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
+ const struct wpa_gtk_data *gd,
+ const u8 *key_rsc)
+{
+ const u8 *_gtk = gd->gtk;
+ u8 gtk_buf[32];
+
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
+ wpa_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)
+{
+#ifndef CONFIG_NO_WPA2
+ struct wpa_gtk_data gd;
+
+ /*
+ * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x
+ * GTK KDE format:
+ * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7]
+ * Reserved [bits 0-7]
+ * GTK
+ */
+
+ os_memset(&gd, 0, sizeof(gd));
+ wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake",
+ gtk, gtk_len);
+
+ if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk))
+ return -1;
+
+ gd.keyidx = gtk[0] & 0x3;
+ gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+ !!(gtk[0] & BIT(2)));
+ gtk += 2;
+ gtk_len -= 2;
+
+ os_memcpy(gd.gtk, gtk, gtk_len);
+ gd.gtk_len = gtk_len;
+
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+ gtk_len, gtk_len,
+ &gd.key_rsc_len, &gd.alg) ||
+ wpa_supplicant_install_gtk(sm, &gd, key->key_rsc)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Failed to install GTK");
+ return -1;
+ }
+
+ wpa_supplicant_key_neg_complete(sm, sm->bssid,
+ key_info & WPA_KEY_INFO_SECURE);
+ return 0;
+#else /* CONFIG_NO_WPA2 */
+ return -1;
+#endif /* CONFIG_NO_WPA2 */
+}
+
+
+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_disassociate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+static int ft_validate_mdie(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ struct wpa_eapol_ie_parse *ie,
+ const u8 *assoc_resp_mdie)
+{
+ struct rsn_mdie *mdie;
+
+ mdie = (struct rsn_mdie *) (ie->mdie + 2);
+ if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) ||
+ os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_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);
+ wpa_supplicant_parse_ies(pos, len, &ie);
+ 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);
+ switch (sm->pairwise_cipher) {
+ case WPA_CIPHER_CCMP:
+ if (keylen != 16) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid CCMP key length %d (src=" MACSTR
+ ")", keylen, MAC2STR(sm->bssid));
+ goto failed;
+ }
+ break;
+ case WPA_CIPHER_TKIP:
+ if (keylen != 32) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid TKIP key length %d (src=" MACSTR
+ ")", keylen, MAC2STR(sm->bssid));
+ goto failed;
+ }
+ break;
+ }
+
+ if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
+ NULL, 0, &sm->ptk)) {
+ goto failed;
+ }
+
+ /* SNonce was successfully used in msg 3/4, so mark it to be renewed
+ * for the next 4-Way Handshake. If msg 3 is received again, the old
+ * SNonce will still be used to avoid changing PTK. */
+ sm->renew_snonce = 1;
+
+ if (key_info & WPA_KEY_INFO_INSTALL) {
+ if (wpa_supplicant_install_ptk(sm, key))
+ goto failed;
+ }
+
+ if (key_info & WPA_KEY_INFO_SECURE) {
+ wpa_sm_mlme_setprotection(
+ sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX,
+ MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+ eapol_sm_notify_portValid(sm->eapol, TRUE);
+ }
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+ if (ie.gtk &&
+ wpa_supplicant_pairwise_gtk(sm, key,
+ ie.gtk, ie.gtk_len, key_info) < 0) {
+ wpa_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;
+ }
+
+ return;
+
+failed:
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
+ const u8 *keydata,
+ size_t keydatalen,
+ u16 key_info,
+ struct wpa_gtk_data *gd)
+{
+ int maxkeylen;
+ struct wpa_eapol_ie_parse ie;
+
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen);
+ wpa_supplicant_parse_ies(keydata, keydatalen, &ie);
+ if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_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);
+ }
+ 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;
+ }
+
+#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 int wpa_cipher_bits(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP:
+ return 128;
+ case WPA_CIPHER_TKIP:
+ return 256;
+ case WPA_CIPHER_WEP104:
+ return 104;
+ case WPA_CIPHER_WEP40:
+ return 40;
+ default:
+ return 0;
+ }
+}
+
+
+static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
+{
+ switch (sm->key_mgmt) {
+ case WPA_KEY_MGMT_IEEE8021X:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_AUTH_KEY_MGMT_UNSPEC_802_1X :
+ WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+ case WPA_KEY_MGMT_PSK:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X :
+ WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+#ifdef CONFIG_IEEE80211R
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ return RSN_AUTH_KEY_MGMT_FT_802_1X;
+ case WPA_KEY_MGMT_FT_PSK:
+ return RSN_AUTH_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ case WPA_KEY_MGMT_IEEE8021X_SHA256:
+ return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+ case WPA_KEY_MGMT_PSK_SHA256:
+ return RSN_AUTH_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+ case WPA_KEY_MGMT_WPA_NONE:
+ return WPA_AUTH_KEY_MGMT_NONE;
+ default:
+ return 0;
+ }
+}
+
+
+static u32 wpa_cipher_suite(struct wpa_sm *sm, int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
+ case WPA_CIPHER_TKIP:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
+ case WPA_CIPHER_WEP104:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
+ case WPA_CIPHER_WEP40:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
+ case WPA_CIPHER_NONE:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
+ default:
+ return 0;
+ }
+}
+
+
+#define RSN_SUITE "%02x-%02x-%02x-%d"
+#define RSN_SUITE_ARG(s) \
+((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+
+/**
+ * wpa_sm_get_mib - Dump text list of MIB entries
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for the list
+ * @buflen: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used fetch dot11 MIB variables.
+ */
+int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+{
+ char pmkid_txt[PMKID_LEN * 2 + 1];
+ int rsna, ret;
+ size_t len;
+
+ if (sm->cur_pmksa) {
+ wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
+ sm->cur_pmksa->pmkid, PMKID_LEN);
+ } else
+ pmkid_txt[0] = '\0';
+
+ if ((wpa_key_mgmt_wpa_psk(sm->key_mgmt) ||
+ wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) &&
+ sm->proto == WPA_PROTO_RSN)
+ rsna = 1;
+ else
+ rsna = 0;
+
+ ret = os_snprintf(buf, buflen,
+ "dot11RSNAOptionImplemented=TRUE\n"
+ "dot11RSNAPreauthenticationImplemented=TRUE\n"
+ "dot11RSNAEnabled=%s\n"
+ "dot11RSNAPreauthenticationEnabled=%s\n"
+ "dot11RSNAConfigVersion=%d\n"
+ "dot11RSNAConfigPairwiseKeysSupported=5\n"
+ "dot11RSNAConfigGroupCipherSize=%d\n"
+ "dot11RSNAConfigPMKLifetime=%d\n"
+ "dot11RSNAConfigPMKReauthThreshold=%d\n"
+ "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n"
+ "dot11RSNAConfigSATimeout=%d\n",
+ rsna ? "TRUE" : "FALSE",
+ rsna ? "TRUE" : "FALSE",
+ RSN_VERSION,
+ wpa_cipher_bits(sm->group_cipher),
+ sm->dot11RSNAConfigPMKLifetime,
+ sm->dot11RSNAConfigPMKReauthThreshold,
+ sm->dot11RSNAConfigSATimeout);
+ if (ret < 0 || (size_t) ret >= buflen)
+ return 0;
+ len = ret;
+
+ ret = os_snprintf(
+ buf + len, buflen - len,
+ "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
+ "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
+ "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
+ "dot11RSNAPMKIDUsed=%s\n"
+ "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
+ "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
+ "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
+ "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n"
+ "dot11RSNA4WayHandshakeFailures=%u\n",
+ RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
+ RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
+ pmkid_txt,
+ RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
+ RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
+ sm->dot11RSNA4WayHandshakeFailures);
+ if (ret >= 0 && (size_t) ret < buflen)
+ len += ret;
+
+ return (int) len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, int replace)
+{
+ struct wpa_sm *sm = ctx;
+
+ if (sm->cur_pmksa == entry ||
+ (sm->pmk_len == entry->pmk_len &&
+ os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: removed current PMKSA entry");
+ sm->cur_pmksa = NULL;
+
+ if (replace) {
+ /* A new entry is being added, so no need to
+ * deauthenticate in this case. This happens when EAP
+ * authentication is completed again (reauth or failed
+ * PMKSA caching attempt). */
+ return;
+ }
+
+ os_memset(sm->pmk, 0, sizeof(sm->pmk));
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+ }
+}
+
+
+/**
+ * wpa_sm_init - Initialize WPA state machine
+ * @ctx: Context pointer for callbacks; this needs to be an allocated buffer
+ * Returns: Pointer to the allocated WPA state machine data
+ *
+ * This function is used to allocate a new WPA state machine and the returned
+ * value is passed to all WPA state machine calls.
+ */
+struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
+{
+ struct wpa_sm *sm;
+
+ sm = os_zalloc(sizeof(*sm));
+ if (sm == NULL)
+ return NULL;
+ dl_list_init(&sm->pmksa_candidates);
+ sm->renew_snonce = 1;
+ sm->ctx = ctx;
+
+ sm->dot11RSNAConfigPMKLifetime = 43200;
+ sm->dot11RSNAConfigPMKReauthThreshold = 70;
+ sm->dot11RSNAConfigSATimeout = 60;
+
+ sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm);
+ if (sm->pmksa == NULL) {
+ wpa_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);
+ 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;
+ }
+ if (config == NULL || config->network_ctx != sm->network_ctx)
+ pmksa_cache_notify_reconfig(sm->pmksa);
+}
+
+
+/**
+ * wpa_sm_set_own_addr - Set own MAC address
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @addr: Own MAC address
+ */
+void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
+{
+ if (sm)
+ os_memcpy(sm->own_addr, addr, ETH_ALEN);
+}
+
+
+/**
+ * wpa_sm_set_ifname - Set network interface name
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ifname: Interface name
+ * @bridge_ifname: Optional bridge interface name (for pre-auth)
+ */
+void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+ const char *bridge_ifname)
+{
+ if (sm) {
+ sm->ifname = ifname;
+ sm->bridge_ifname = bridge_ifname;
+ }
+}
+
+
+/**
+ * wpa_sm_set_eapol - Set EAPOL state machine pointer
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ */
+void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
+{
+ if (sm)
+ sm->eapol = eapol;
+}
+
+
+/**
+ * wpa_sm_set_param - Set WPA state machine parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @param: Parameter field
+ * @value: Parameter value
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
+ unsigned int value)
+{
+ int ret = 0;
+
+ if (sm == NULL)
+ return -1;
+
+ switch (param) {
+ case RSNA_PMK_LIFETIME:
+ if (value > 0)
+ sm->dot11RSNAConfigPMKLifetime = value;
+ else
+ ret = -1;
+ break;
+ case RSNA_PMK_REAUTH_THRESHOLD:
+ if (value > 0 && value <= 100)
+ sm->dot11RSNAConfigPMKReauthThreshold = value;
+ else
+ ret = -1;
+ break;
+ case RSNA_SA_TIMEOUT:
+ if (value > 0)
+ sm->dot11RSNAConfigSATimeout = value;
+ else
+ ret = -1;
+ break;
+ case WPA_PARAM_PROTO:
+ sm->proto = value;
+ break;
+ case WPA_PARAM_PAIRWISE:
+ sm->pairwise_cipher = value;
+ break;
+ case WPA_PARAM_GROUP:
+ sm->group_cipher = value;
+ break;
+ case WPA_PARAM_KEY_MGMT:
+ sm->key_mgmt = value;
+ break;
+#ifdef CONFIG_IEEE80211W
+ case WPA_PARAM_MGMT_GROUP:
+ sm->mgmt_group_cipher = value;
+ break;
+#endif /* CONFIG_IEEE80211W */
+ case WPA_PARAM_RSN_ENABLED:
+ sm->rsn_enabled = value;
+ break;
+ case WPA_PARAM_MFP:
+ sm->mfp = value;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+
+/**
+ * wpa_sm_get_param - Get WPA state machine parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @param: Parameter field
+ * Returns: Parameter value
+ */
+unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param)
+{
+ if (sm == NULL)
+ return 0;
+
+ switch (param) {
+ case RSNA_PMK_LIFETIME:
+ return sm->dot11RSNAConfigPMKLifetime;
+ case RSNA_PMK_REAUTH_THRESHOLD:
+ return sm->dot11RSNAConfigPMKReauthThreshold;
+ case RSNA_SA_TIMEOUT:
+ return sm->dot11RSNAConfigSATimeout;
+ case WPA_PARAM_PROTO:
+ return sm->proto;
+ case WPA_PARAM_PAIRWISE:
+ return sm->pairwise_cipher;
+ case WPA_PARAM_GROUP:
+ return sm->group_cipher;
+ case WPA_PARAM_KEY_MGMT:
+ return sm->key_mgmt;
+#ifdef CONFIG_IEEE80211W
+ case WPA_PARAM_MGMT_GROUP:
+ return sm->mgmt_group_cipher;
+#endif /* CONFIG_IEEE80211W */
+ case WPA_PARAM_RSN_ENABLED:
+ return sm->rsn_enabled;
+ default:
+ return 0;
+ }
+}
+
+
+/**
+ * wpa_sm_get_status - Get WPA state machine
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query WPA state machine for status information. This function fills in
+ * a text area with current status information. If the buffer (buf) is not
+ * large enough, status information will be truncated to fit the buffer.
+ */
+int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+ int verbose)
+{
+ char *pos = buf, *end = buf + buflen;
+ int ret;
+
+ ret = os_snprintf(pos, end - pos,
+ "pairwise_cipher=%s\n"
+ "group_cipher=%s\n"
+ "key_mgmt=%s\n",
+ wpa_cipher_txt(sm->pairwise_cipher),
+ wpa_cipher_txt(sm->group_cipher),
+ wpa_key_mgmt_txt(sm->key_mgmt, sm->proto));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ return pos - buf;
+}
+
+
+/**
+ * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @wpa_ie: Pointer to buffer for WPA/RSN IE
+ * @wpa_ie_len: Pointer to the length of the wpa_ie buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
+ size_t *wpa_ie_len)
+{
+ int res;
+
+ if (sm == NULL)
+ return -1;
+
+ res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);
+ if (res < 0)
+ return -1;
+ *wpa_ie_len = res;
+
+ wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default",
+ wpa_ie, *wpa_ie_len);
+
+ if (sm->assoc_wpa_ie == NULL) {
+ /*
+ * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets
+ * the correct version of the IE even if PMKSA caching is
+ * aborted (which would remove PMKID from IE generation).
+ */
+ sm->assoc_wpa_ie = os_malloc(*wpa_ie_len);
+ if (sm->assoc_wpa_ie == NULL)
+ return -1;
+
+ os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
+ sm->assoc_wpa_ie_len = *wpa_ie_len;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the WPA/RSN IE used in (Re)Association
+ * Request frame. The IE will be used to override the default value generated
+ * with wpa_sm_set_assoc_wpa_ie_default().
+ */
+int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+ if (sm == NULL)
+ return -1;
+
+ os_free(sm->assoc_wpa_ie);
+ if (ie == NULL || len == 0) {
+ wpa_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)
+{
+#ifndef CONFIG_NO_WPA2
+ return pmksa_cache_list(sm->pmksa, buf, len);
+#else /* CONFIG_NO_WPA2 */
+ return -1;
+#endif /* CONFIG_NO_WPA2 */
+}
+
+
+void wpa_sm_drop_sa(struct wpa_sm *sm)
+{
+ wpa_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;
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
new file mode 100644
index 0000000..111597c
--- /dev/null
+++ b/src/rsn_supp/wpa.h
@@ -0,0 +1,351 @@
+/*
+ * wpa_supplicant - WPA definitions
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_H
+#define WPA_H
+
+#include "common/defs.h"
+#include "common/eapol_common.h"
+#include "common/wpa_common.h"
+
+struct wpa_sm;
+struct eapol_sm;
+struct wpa_config_blob;
+
+struct wpa_sm_ctx {
+ void *ctx; /* pointer to arbitrary upper level context */
+ void *msg_ctx; /* upper level context for wpa_msg() calls */
+
+ void (*set_state)(void *ctx, enum wpa_states state);
+ enum wpa_states (*get_state)(void *ctx);
+ void (*deauthenticate)(void * ctx, int reason_code);
+ void (*disassociate)(void *ctx, int reason_code);
+ int (*set_key)(void *ctx, enum wpa_alg alg,
+ const u8 *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len);
+ void * (*get_network_ctx)(void *ctx);
+ int (*get_bssid)(void *ctx, u8 *bssid);
+ int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
+ size_t len);
+ int (*get_beacon_ie)(void *ctx);
+ void (*cancel_auth_timeout)(void *ctx);
+ u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len,
+ size_t *msg_len, void **data_pos);
+ int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
+ int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
+ void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+ const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+ const char *name);
+ int (*mlme_setprotection)(void *ctx, const u8 *addr,
+ int protection_type, int key_type);
+ int (*update_ft_ies)(void *ctx, const u8 *md, const u8 *ies,
+ size_t ies_len);
+ int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap,
+ const u8 *ies, size_t ies_len);
+ int (*mark_authenticated)(void *ctx, const u8 *target_ap);
+#ifdef CONFIG_TDLS
+ 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);
+#endif /* CONFIG_TDLS */
+};
+
+
+enum wpa_sm_conf_params {
+ RSNA_PMK_LIFETIME /* dot11RSNAConfigPMKLifetime */,
+ RSNA_PMK_REAUTH_THRESHOLD /* dot11RSNAConfigPMKReauthThreshold */,
+ RSNA_SA_TIMEOUT /* dot11RSNAConfigSATimeout */,
+ WPA_PARAM_PROTO,
+ WPA_PARAM_PAIRWISE,
+ WPA_PARAM_GROUP,
+ WPA_PARAM_KEY_MGMT,
+ WPA_PARAM_MGMT_GROUP,
+ WPA_PARAM_RSN_ENABLED,
+ WPA_PARAM_MFP
+};
+
+struct rsn_supp_config {
+ void *network_ctx;
+ int peerkey_enabled;
+ int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
+ int proactive_key_caching;
+ int eap_workaround;
+ void *eap_conf_ctx;
+ const u8 *ssid;
+ size_t ssid_len;
+ int wpa_ptk_rekey;
+};
+
+#ifndef CONFIG_NO_WPA
+
+struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx);
+void wpa_sm_deinit(struct wpa_sm *sm);
+void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid);
+void wpa_sm_notify_disassoc(struct wpa_sm *sm);
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len);
+void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm);
+void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth);
+void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx);
+void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config);
+void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr);
+void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+ const char *bridge_ifname);
+void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol);
+int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
+ size_t *wpa_ie_len);
+int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen);
+
+int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
+ unsigned int value);
+unsigned int wpa_sm_get_param(struct wpa_sm *sm,
+ enum wpa_sm_conf_params param);
+
+int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+ int verbose);
+
+void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise);
+
+int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data);
+
+void wpa_sm_aborted_cached(struct wpa_sm *sm);
+int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len);
+int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data);
+int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
+void wpa_sm_drop_sa(struct wpa_sm *sm);
+int wpa_sm_has_ptk(struct wpa_sm *sm);
+
+#else /* CONFIG_NO_WPA */
+
+static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
+{
+ return (struct wpa_sm *) 1;
+}
+
+static inline void wpa_sm_deinit(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
+{
+}
+
+static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk,
+ size_t pmk_len)
+{
+}
+
+static inline void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth)
+{
+}
+
+static inline void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx)
+{
+}
+
+static inline void wpa_sm_set_config(struct wpa_sm *sm,
+ struct rsn_supp_config *config)
+{
+}
+
+static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
+{
+}
+
+static inline void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+ const char *bridge_ifname)
+{
+}
+
+static inline void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
+{
+}
+
+static inline int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie,
+ size_t len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm,
+ u8 *wpa_ie,
+ size_t *wpa_ie_len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie,
+ size_t len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie,
+ size_t len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+{
+ return 0;
+}
+
+static inline int wpa_sm_set_param(struct wpa_sm *sm,
+ enum wpa_sm_conf_params param,
+ unsigned int value)
+{
+ return -1;
+}
+
+static inline unsigned int wpa_sm_get_param(struct wpa_sm *sm,
+ enum wpa_sm_conf_params param)
+{
+ return 0;
+}
+
+static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf,
+ size_t buflen, int verbose)
+{
+ return 0;
+}
+
+static inline void wpa_sm_key_request(struct wpa_sm *sm, int error,
+ int pairwise)
+{
+}
+
+static inline int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data)
+{
+ return -1;
+}
+
+static inline void wpa_sm_aborted_cached(struct wpa_sm *sm)
+{
+}
+
+static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm,
+ struct wpa_ie_data *data)
+{
+ return -1;
+}
+
+static inline int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf,
+ size_t len)
+{
+ return -1;
+}
+
+static inline void wpa_sm_drop_sa(struct wpa_sm *sm)
+{
+}
+
+static inline int wpa_sm_has_ptk(struct wpa_sm *sm)
+{
+ return 0;
+}
+
+#endif /* CONFIG_NO_WPA */
+
+#ifdef CONFIG_PEERKEY
+int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer);
+#else /* CONFIG_PEERKEY */
+static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
+{
+ return -1;
+}
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211R
+
+int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len);
+int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie);
+int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+ int ft_action, const u8 *target_ap,
+ const u8 *ric_ies, size_t ric_ies_len);
+int wpa_ft_is_completed(struct wpa_sm *sm);
+int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
+ size_t ies_len, const u8 *src_addr);
+int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
+ const u8 *mdie);
+
+#else /* CONFIG_IEEE80211R */
+
+static inline int
+wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
+{
+ return 0;
+}
+
+static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm,
+ const u8 *mdie)
+{
+ return 0;
+}
+
+static inline int
+wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+ int ft_action, const u8 *target_ap)
+{
+ return 0;
+}
+
+static inline int wpa_ft_is_completed(struct wpa_sm *sm)
+{
+ return 0;
+}
+
+static inline int
+wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+ const u8 *src_addr)
+{
+ return -1;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+/* 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);
+int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_recv_teardown_notify(struct wpa_sm *sm, const u8 *addr,
+ u16 reason_code);
+int wpa_tdls_init(struct wpa_sm *sm);
+void wpa_tdls_deinit(struct wpa_sm *sm);
+void wpa_tdls_enable(struct wpa_sm *sm, int enabled);
+
+#endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
new file mode 100644
index 0000000..da6e966
--- /dev/null
+++ b/src/rsn_supp/wpa_ft.c
@@ -0,0 +1,1039 @@
+/*
+ * WPA Supplicant - IEEE 802.11r - Fast BSS Transition
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wpa.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+
+#ifdef CONFIG_IEEE80211R
+
+struct wpa_ft_ies {
+ const u8 *mdie;
+ size_t mdie_len;
+ const u8 *ftie;
+ size_t ftie_len;
+ const u8 *r1kh_id;
+ const u8 *gtk;
+ size_t gtk_len;
+ const u8 *r0kh_id;
+ size_t r0kh_id_len;
+ const u8 *rsn;
+ size_t rsn_len;
+ const u8 *rsn_pmkid;
+ const u8 *tie;
+ size_t tie_len;
+ const u8 *igtk;
+ size_t igtk_len;
+ const u8 *ric;
+ size_t ric_len;
+};
+
+static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+ struct wpa_ft_ies *parse);
+
+
+int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ struct wpa_ptk *ptk, size_t ptk_len)
+{
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ const u8 *anonce = key->key_nonce;
+
+ if (sm->xxkey_len == 0) {
+ wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
+ "derivation");
+ return -1;
+ }
+
+ wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid,
+ sm->ssid_len, sm->mobility_domain,
+ sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
+ sm->pmk_r0, sm->pmk_r0_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name",
+ sm->pmk_r0_name, WPA_PMK_NAME_LEN);
+ wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
+ sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN);
+ wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr,
+ sm->bssid, sm->pmk_r1_name,
+ (u8 *) ptk, ptk_len, ptk_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+ return 0;
+}
+
+
+/**
+ * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ies: Association Response IEs or %NULL to clear FT parameters
+ * @ies_len: Length of ies buffer in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
+{
+ struct wpa_ft_ies ft;
+
+ if (sm == NULL)
+ return 0;
+
+ if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0)
+ return -1;
+
+ if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
+ return -1;
+
+ if (ft.mdie) {
+ wpa_hexdump(MSG_DEBUG, "FT: Mobility domain",
+ ft.mdie, MOBILITY_DOMAIN_ID_LEN);
+ os_memcpy(sm->mobility_domain, ft.mdie,
+ MOBILITY_DOMAIN_ID_LEN);
+ sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN];
+ wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x",
+ sm->mdie_ft_capab);
+ } else
+ os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN);
+
+ if (ft.r0kh_id) {
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID",
+ ft.r0kh_id, ft.r0kh_id_len);
+ os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len);
+ sm->r0kh_id_len = ft.r0kh_id_len;
+ } else {
+ /* FIX: When should R0KH-ID be cleared? We need to keep the
+ * old R0KH-ID in order to be able to use this during FT. */
+ /*
+ * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN);
+ * sm->r0kh_id_len = 0;
+ */
+ }
+
+ if (ft.r1kh_id) {
+ wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID",
+ ft.r1kh_id, FT_R1KH_ID_LEN);
+ os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN);
+ } else
+ os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN);
+
+ os_free(sm->assoc_resp_ies);
+ sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2);
+ if (sm->assoc_resp_ies) {
+ u8 *pos = sm->assoc_resp_ies;
+ if (ft.mdie) {
+ os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2);
+ pos += ft.mdie_len + 2;
+ }
+ if (ft.ftie) {
+ os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2);
+ pos += ft.ftie_len + 2;
+ }
+ sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies;
+ wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from "
+ "(Re)Association Response",
+ sm->assoc_resp_ies, sm->assoc_resp_ies_len);
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @len: Buffer for returning the length of the IEs
+ * @anonce: ANonce or %NULL if not yet available
+ * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List
+ * @kck: 128-bit KCK for MIC or %NULL if no MIC is used
+ * @target_ap: Target AP address
+ * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL
+ * @ric_ies_len: Length of ric_ies buffer in octets
+ * @ap_mdie: Mobility Domain IE from the target AP
+ * Returns: Pointer to buffer with IEs or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free();
+ */
+static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
+ const u8 *anonce, const u8 *pmk_name,
+ const u8 *kck, const u8 *target_ap,
+ const u8 *ric_ies, size_t ric_ies_len,
+ const u8 *ap_mdie)
+{
+ size_t buf_len;
+ u8 *buf, *pos, *ftie_len, *ftie_pos;
+ struct rsn_mdie *mdie;
+ struct rsn_ftie *ftie;
+ struct rsn_ie_hdr *rsnie;
+ u16 capab;
+
+ sm->ft_completed = 0;
+
+ buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+ 2 + sm->r0kh_id_len + ric_ies_len + 100;
+ buf = os_zalloc(buf_len);
+ if (buf == NULL)
+ return NULL;
+ pos = buf;
+
+ /* RSNIE[PMKR0Name/PMKR1Name] */
+ rsnie = (struct rsn_ie_hdr *) pos;
+ rsnie->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(rsnie->version, RSN_VERSION);
+ pos = (u8 *) (rsnie + 1);
+
+ /* Group Suite Selector */
+ if (sm->group_cipher == WPA_CIPHER_CCMP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ else if (sm->group_cipher == WPA_CIPHER_TKIP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ else {
+ wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
+ sm->group_cipher);
+ os_free(buf);
+ return NULL;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ /* Pairwise Suite Count */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+
+ /* Pairwise Suite List */
+ if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ else if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ else {
+ wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
+ sm->pairwise_cipher);
+ os_free(buf);
+ return NULL;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ /* Authenticated Key Management Suite Count */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+
+ /* Authenticated Key Management Suite List */
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+ else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+ else {
+ wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)",
+ sm->key_mgmt);
+ os_free(buf);
+ return NULL;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+#ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+ capab |= WPA_CAPABILITY_MFPC;
+#endif /* CONFIG_IEEE80211W */
+ WPA_PUT_LE16(pos, capab);
+ pos += 2;
+
+ /* PMKID Count */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+
+ /* PMKID List [PMKR0Name/PMKR1Name] */
+ os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN);
+ pos += WPA_PMK_NAME_LEN;
+
+#ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+ /* Management Group Cipher Suite */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ pos += RSN_SELECTOR_LEN;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ rsnie->len = (pos - (u8 *) rsnie) - 2;
+
+ /* MDIE */
+ *pos++ = WLAN_EID_MOBILITY_DOMAIN;
+ *pos++ = sizeof(*mdie);
+ mdie = (struct rsn_mdie *) pos;
+ pos += sizeof(*mdie);
+ os_memcpy(mdie->mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+ mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] :
+ sm->mdie_ft_capab;
+
+ /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */
+ ftie_pos = pos;
+ *pos++ = WLAN_EID_FAST_BSS_TRANSITION;
+ ftie_len = pos++;
+ ftie = (struct rsn_ftie *) pos;
+ pos += sizeof(*ftie);
+ os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
+ if (anonce)
+ os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+ if (kck) {
+ /* R1KH-ID sub-element in third FT message */
+ *pos++ = FTIE_SUBELEM_R1KH_ID;
+ *pos++ = FT_R1KH_ID_LEN;
+ os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN);
+ pos += FT_R1KH_ID_LEN;
+ }
+ /* R0KH-ID sub-element */
+ *pos++ = FTIE_SUBELEM_R0KH_ID;
+ *pos++ = sm->r0kh_id_len;
+ os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len);
+ pos += sm->r0kh_id_len;
+ *ftie_len = pos - ftie_len - 1;
+
+ if (ric_ies) {
+ /* RIC Request */
+ os_memcpy(pos, ric_ies, ric_ies_len);
+ pos += ric_ies_len;
+ }
+
+ if (kck) {
+ /*
+ * IEEE Std 802.11r-2008, 11A.8.4
+ * MIC shall be calculated over:
+ * non-AP STA MAC address
+ * Target AP MAC address
+ * Transaction seq number (5 for ReassocReq, 3 otherwise)
+ * RSN IE
+ * MDIE
+ * FTIE (with MIC field set to 0)
+ * RIC-Request (if present)
+ */
+ /* Information element count */
+ ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies,
+ ric_ies_len);
+ if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5,
+ ((u8 *) mdie) - 2, 2 + sizeof(*mdie),
+ ftie_pos, 2 + *ftie_len,
+ (u8 *) rsnie, 2 + rsnie->len, ric_ies,
+ ric_ies_len, ftie->mic) < 0) {
+ wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
+ os_free(buf);
+ return NULL;
+ }
+ }
+
+ *len = pos - buf;
+
+ return buf;
+}
+
+
+static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
+ struct wpa_ft_ies *parse)
+{
+ const u8 *end, *pos;
+
+ parse->ftie = ie;
+ parse->ftie_len = ie_len;
+
+ pos = ie + sizeof(struct rsn_ftie);
+ end = ie + ie_len;
+
+ while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+ switch (pos[0]) {
+ case FTIE_SUBELEM_R1KH_ID:
+ if (pos[1] != FT_R1KH_ID_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
+ "length in FTIE: %d", pos[1]);
+ return -1;
+ }
+ parse->r1kh_id = pos + 2;
+ break;
+ case FTIE_SUBELEM_GTK:
+ parse->gtk = pos + 2;
+ parse->gtk_len = pos[1];
+ break;
+ case FTIE_SUBELEM_R0KH_ID:
+ if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
+ "length in FTIE: %d", pos[1]);
+ return -1;
+ }
+ parse->r0kh_id = pos + 2;
+ parse->r0kh_id_len = pos[1];
+ break;
+#ifdef CONFIG_IEEE80211W
+ case FTIE_SUBELEM_IGTK:
+ parse->igtk = pos + 2;
+ parse->igtk_len = pos[1];
+ break;
+#endif /* CONFIG_IEEE80211W */
+ }
+
+ pos += 2 + pos[1];
+ }
+
+ return 0;
+}
+
+
+static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+ struct wpa_ft_ies *parse)
+{
+ const u8 *end, *pos;
+ struct wpa_ie_data data;
+ int ret;
+ const struct rsn_ftie *ftie;
+ int prot_ie_count = 0;
+
+ os_memset(parse, 0, sizeof(*parse));
+ if (ies == NULL)
+ return 0;
+
+ pos = ies;
+ end = ies + ies_len;
+ while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+ switch (pos[0]) {
+ case WLAN_EID_RSN:
+ parse->rsn = pos + 2;
+ parse->rsn_len = pos[1];
+ ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
+ parse->rsn_len + 2,
+ &data);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse "
+ "RSN IE: %d", ret);
+ return -1;
+ }
+ if (data.num_pmkid == 1 && data.pmkid)
+ parse->rsn_pmkid = data.pmkid;
+ break;
+ case WLAN_EID_MOBILITY_DOMAIN:
+ parse->mdie = pos + 2;
+ parse->mdie_len = pos[1];
+ break;
+ case WLAN_EID_FAST_BSS_TRANSITION:
+ if (pos[1] < sizeof(*ftie))
+ return -1;
+ ftie = (const struct rsn_ftie *) (pos + 2);
+ prot_ie_count = ftie->mic_control[1];
+ if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+ return -1;
+ break;
+ case WLAN_EID_TIMEOUT_INTERVAL:
+ parse->tie = pos + 2;
+ parse->tie_len = pos[1];
+ break;
+ case WLAN_EID_RIC_DATA:
+ if (parse->ric == NULL)
+ parse->ric = pos;
+ }
+
+ pos += 2 + pos[1];
+ }
+
+ if (prot_ie_count == 0)
+ return 0; /* no MIC */
+
+ /*
+ * Check that the protected IE count matches with IEs included in the
+ * frame.
+ */
+ if (parse->rsn)
+ prot_ie_count--;
+ if (parse->mdie)
+ prot_ie_count--;
+ if (parse->ftie)
+ prot_ie_count--;
+ if (parse->tie)
+ prot_ie_count--;
+ if (prot_ie_count < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
+ "the protected IE count");
+ return -1;
+ }
+
+ if (prot_ie_count == 0 && parse->ric) {
+ wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
+ "included in protected IE count");
+ return -1;
+ }
+
+ /* Determine the end of the RIC IE(s) */
+ pos = parse->ric;
+ while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
+ prot_ie_count) {
+ prot_ie_count--;
+ pos += 2 + pos[1];
+ }
+ parse->ric_len = pos - parse->ric;
+ if (prot_ie_count) {
+ wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
+ "frame", (int) prot_ie_count);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
+{
+ int keylen;
+ enum wpa_alg alg;
+ u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 };
+
+ wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver.");
+
+ switch (sm->pairwise_cipher) {
+ case WPA_CIPHER_CCMP:
+ alg = WPA_ALG_CCMP;
+ keylen = 16;
+ break;
+ case WPA_CIPHER_TKIP:
+ alg = WPA_ALG_TKIP;
+ keylen = 32;
+ break;
+ default:
+ wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d",
+ sm->pairwise_cipher);
+ return -1;
+ }
+
+ if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc,
+ sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) {
+ wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_ft_prepare_auth_request - Generate over-the-air auth request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @mdie: Target AP MDIE
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie)
+{
+ u8 *ft_ies;
+ size_t ft_ies_len;
+
+ /* Generate a new SNonce */
+ if (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_CCMP ? 48 : 64;
+ wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr,
+ bssid, sm->pmk_r1_name,
+ (u8 *) &sm->ptk, ptk_len, ptk_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
+ (u8 *) &sm->ptk, ptk_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+ ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce,
+ sm->pmk_r1_name, sm->ptk.kck, bssid,
+ ric_ies, ric_ies_len,
+ parse.mdie ? parse.mdie - 2 : NULL);
+ if (ft_ies) {
+ wpa_sm_update_ft_ies(sm, sm->mobility_domain,
+ ft_ies, ft_ies_len);
+ os_free(ft_ies);
+ }
+
+ wpa_sm_mark_authenticated(sm, bssid);
+ ret = wpa_ft_install_ptk(sm, bssid);
+ if (ret) {
+ /*
+ * Some drivers do not support key configuration when we are
+ * not associated with the target AP. Work around this by
+ * trying again after the following reassociation gets
+ * completed.
+ */
+ wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to "
+ "association - try again after reassociation");
+ sm->set_ptk_after_assoc = 1;
+ } else
+ sm->set_ptk_after_assoc = 0;
+
+ sm->ft_completed = 1;
+ if (ft_action) {
+ /*
+ * The caller is expected trigger re-association with the
+ * Target AP.
+ */
+ os_memcpy(sm->bssid, target_ap, ETH_ALEN);
+ }
+
+ return 0;
+}
+
+
+int wpa_ft_is_completed(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return 0;
+
+ if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
+ sm->key_mgmt != WPA_KEY_MGMT_FT_PSK)
+ return 0;
+
+ return sm->ft_completed;
+}
+
+
+static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
+ size_t gtk_elem_len)
+{
+ u8 gtk[32];
+ int keyidx;
+ enum wpa_alg alg;
+ size_t gtk_len, keylen, rsc_len;
+
+ if (gtk_elem == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE");
+ return 0;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp",
+ gtk_elem, gtk_elem_len);
+
+ if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 ||
+ gtk_elem_len - 19 > sizeof(gtk)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem "
+ "length %lu", (unsigned long) gtk_elem_len);
+ return -1;
+ }
+ gtk_len = gtk_elem_len - 19;
+ if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 11, gtk)) {
+ wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
+ "decrypt GTK");
+ return -1;
+ }
+
+ switch (sm->group_cipher) {
+ case WPA_CIPHER_CCMP:
+ keylen = 16;
+ rsc_len = 6;
+ alg = WPA_ALG_CCMP;
+ break;
+ case WPA_CIPHER_TKIP:
+ keylen = 32;
+ rsc_len = 6;
+ alg = WPA_ALG_TKIP;
+ break;
+ case WPA_CIPHER_WEP104:
+ keylen = 13;
+ rsc_len = 0;
+ alg = WPA_ALG_WEP;
+ break;
+ case WPA_CIPHER_WEP40:
+ keylen = 5;
+ rsc_len = 0;
+ alg = WPA_ALG_WEP;
+ break;
+ default:
+ wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
+ sm->group_cipher);
+ return -1;
+ }
+
+ if (gtk_len < keylen) {
+ wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE");
+ return -1;
+ }
+
+ /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */
+
+ keyidx = WPA_GET_LE16(gtk_elem) & 0x03;
+
+ if (gtk_elem[2] != keylen) {
+ wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d "
+ "negotiated %lu",
+ gtk_elem[2], (unsigned long) keylen);
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
+ if (wpa_sm_set_key(sm, alg, 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.tie)
+ count++;
+ if (ftie->mic_control[1] != count) {
+ wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
+ "Control: received %u expected %u",
+ ftie->mic_control[1], count);
+ return -1;
+ }
+
+ if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, src_addr, 6,
+ parse.mdie - 2, parse.mdie_len + 2,
+ parse.ftie - 2, parse.ftie_len + 2,
+ parse.rsn - 2, parse.rsn_len + 2,
+ parse.ric, parse.ric_len,
+ mic) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+ return -1;
+ }
+
+ if (os_memcmp(mic, ftie->mic, 16) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+ return -1;
+ }
+
+ if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0)
+ return -1;
+
+#ifdef CONFIG_IEEE80211W
+ if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0)
+ return -1;
+#endif /* CONFIG_IEEE80211W */
+
+ if (sm->set_ptk_after_assoc) {
+ wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we "
+ "are associated");
+ if (wpa_ft_install_ptk(sm, src_addr) < 0)
+ return -1;
+ sm->set_ptk_after_assoc = 0;
+ }
+
+ if (parse.ric) {
+ wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response",
+ parse.ric, parse.ric_len);
+ /* TODO: parse response and inform driver about results */
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_ft_start_over_ds - Generate over-the-DS auth request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @target_ap: Target AP Address
+ * @mdie: Mobility Domain IE from the target AP
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
+ const u8 *mdie)
+{
+ u8 *ft_ies;
+ size_t ft_ies_len;
+
+ wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR,
+ MAC2STR(target_ap));
+
+ /* Generate a new SNonce */
+ if (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/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
new file mode 100644
index 0000000..09a2e4f
--- /dev/null
+++ b/src/rsn_supp/wpa_i.h
@@ -0,0 +1,290 @@
+/*
+ * Internal WPA/RSN supplicant state machine definitions
+ * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_I_H
+#define WPA_I_H
+
+#include "utils/list.h"
+
+struct wpa_peerkey;
+struct wpa_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;
+#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 void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code)
+{
+ WPA_ASSERT(sm->ctx->disassociate);
+ sm->ctx->disassociate(sm->ctx->ctx, reason_code);
+}
+
+static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg,
+ const u8 *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ WPA_ASSERT(sm->ctx->set_key);
+ return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx,
+ seq, seq_len, key, key_len);
+}
+
+static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->get_network_ctx);
+ return sm->ctx->get_network_ctx(sm->ctx->ctx);
+}
+
+static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid)
+{
+ WPA_ASSERT(sm->ctx->get_bssid);
+ return sm->ctx->get_bssid(sm->ctx->ctx, bssid);
+}
+
+static inline int wpa_sm_ether_send(struct wpa_sm *sm, const u8 *dest,
+ u16 proto, const u8 *buf, size_t len)
+{
+ WPA_ASSERT(sm->ctx->ether_send);
+ return sm->ctx->ether_send(sm->ctx->ctx, dest, proto, buf, len);
+}
+
+static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->get_beacon_ie);
+ return sm->ctx->get_beacon_ie(sm->ctx->ctx);
+}
+
+static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->cancel_auth_timeout);
+ sm->ctx->cancel_auth_timeout(sm->ctx->ctx);
+}
+
+static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type,
+ const void *data, u16 data_len,
+ size_t *msg_len, void **data_pos)
+{
+ WPA_ASSERT(sm->ctx->alloc_eapol);
+ return sm->ctx->alloc_eapol(sm->ctx->ctx, type, data, data_len,
+ msg_len, data_pos);
+}
+
+static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *pmkid)
+{
+ WPA_ASSERT(sm->ctx->add_pmkid);
+ return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid);
+}
+
+static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *pmkid)
+{
+ WPA_ASSERT(sm->ctx->remove_pmkid);
+ return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid);
+}
+
+static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr,
+ int protect_type, int key_type)
+{
+ WPA_ASSERT(sm->ctx->mlme_setprotection);
+ return sm->ctx->mlme_setprotection(sm->ctx->ctx, addr, protect_type,
+ key_type);
+}
+
+static inline int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md,
+ const u8 *ies, size_t ies_len)
+{
+ if (sm->ctx->update_ft_ies)
+ return sm->ctx->update_ft_ies(sm->ctx->ctx, md, ies, ies_len);
+ return -1;
+}
+
+static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action,
+ const u8 *target_ap,
+ const u8 *ies, size_t ies_len)
+{
+ if (sm->ctx->send_ft_action)
+ return sm->ctx->send_ft_action(sm->ctx->ctx, action, target_ap,
+ ies, ies_len);
+ return -1;
+}
+
+static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm,
+ const u8 *target_ap)
+{
+ if (sm->ctx->mark_authenticated)
+ return sm->ctx->mark_authenticated(sm->ctx->ctx, target_ap);
+ return -1;
+}
+
+#ifdef CONFIG_TDLS
+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;
+}
+#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/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
new file mode 100644
index 0000000..654cc1f
--- /dev/null
+++ b/src/rsn_supp/wpa_ie.c
@@ -0,0 +1,447 @@
+/*
+ * wpa_supplicant - WPA/RSN IE and KDE processing
+ * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa.h"
+#include "pmksa_cache.h"
+#include "common/ieee802_11_defs.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+
+
+/**
+ * wpa_parse_wpa_ie - Parse WPA/RSN IE
+ * @wpa_ie: Pointer to WPA or RSN IE
+ * @wpa_ie_len: Length of the WPA/RSN IE
+ * @data: Pointer to data area for parsing results
+ * Returns: 0 on success, -1 on failure
+ *
+ * Parse the contents of WPA or RSN IE and write the parsed data into data.
+ */
+int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data)
+{
+ if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN)
+ return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
+ else
+ return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
+}
+
+
+static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
+ int pairwise_cipher, int group_cipher,
+ int key_mgmt)
+{
+ u8 *pos;
+ struct wpa_ie_hdr *hdr;
+
+ if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
+ 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
+ return -1;
+
+ hdr = (struct wpa_ie_hdr *) wpa_ie;
+ hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
+ RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
+ WPA_PUT_LE16(hdr->version, WPA_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ if (group_cipher == WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+ } else if (group_cipher == WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+ } else if (group_cipher == WPA_CIPHER_WEP104) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104);
+ } else if (group_cipher == WPA_CIPHER_WEP40) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+ group_cipher);
+ return -1;
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (pairwise_cipher == WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+ } else if (pairwise_cipher == WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+ } else if (pairwise_cipher == WPA_CIPHER_NONE) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+ pairwise_cipher);
+ return -1;
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_PSK) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+ key_mgmt);
+ return -1;
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ /* WPA Capabilities; use defaults, so no need to include it */
+
+ hdr->len = (pos - wpa_ie) - 2;
+
+ WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
+
+ return pos - wpa_ie;
+}
+
+
+static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
+ int pairwise_cipher, int group_cipher,
+ int key_mgmt, int mgmt_group_cipher,
+ struct wpa_sm *sm)
+{
+#ifndef CONFIG_NO_WPA2
+ u8 *pos;
+ struct rsn_ie_hdr *hdr;
+ u16 capab;
+
+ if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
+ 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
+ (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) {
+ wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)",
+ (unsigned long) rsn_ie_len);
+ return -1;
+ }
+
+ hdr = (struct rsn_ie_hdr *) rsn_ie;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ if (group_cipher == WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ } else if (group_cipher == WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ } else if (group_cipher == WPA_CIPHER_WEP104) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104);
+ } else if (group_cipher == WPA_CIPHER_WEP40) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+ group_cipher);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (pairwise_cipher == WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ } else if (pairwise_cipher == WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ } else if (pairwise_cipher == WPA_CIPHER_NONE) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+ pairwise_cipher);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_PSK) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+#ifdef CONFIG_IEEE80211R
+ } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
+ } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
+#endif /* CONFIG_IEEE80211W */
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+ key_mgmt);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+#ifdef CONFIG_IEEE80211W
+ if (sm->mfp)
+ capab |= WPA_CAPABILITY_MFPC;
+ if (sm->mfp == 2)
+ capab |= WPA_CAPABILITY_MFPR;
+#endif /* CONFIG_IEEE80211W */
+ WPA_PUT_LE16(pos, capab);
+ pos += 2;
+
+ if (sm->cur_pmksa) {
+ /* PMKID Count (2 octets, little endian) */
+ *pos++ = 1;
+ *pos++ = 0;
+ /* PMKID */
+ os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN);
+ pos += PMKID_LEN;
+ }
+
+#ifdef CONFIG_IEEE80211W
+ if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+ if (!sm->cur_pmksa) {
+ /* PMKID Count */
+ WPA_PUT_LE16(pos, 0);
+ pos += 2;
+ }
+
+ /* Management Group Cipher Suite */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ pos += RSN_SELECTOR_LEN;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ hdr->len = (pos - rsn_ie) - 2;
+
+ WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len);
+
+ return pos - rsn_ie;
+#else /* CONFIG_NO_WPA2 */
+ return -1;
+#endif /* CONFIG_NO_WPA2 */
+}
+
+
+/**
+ * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE
+ * @wpa_ie_len: Maximum length of the generated WPA/RSN IE
+ * Returns: Length of the generated WPA/RSN IE or -1 on failure
+ */
+int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
+{
+ if (sm->proto == WPA_PROTO_RSN)
+ return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len,
+ sm->pairwise_cipher,
+ sm->group_cipher,
+ sm->key_mgmt, sm->mgmt_group_cipher,
+ sm);
+ else
+ return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
+ sm->pairwise_cipher,
+ sm->group_cipher,
+ sm->key_mgmt);
+}
+
+
+/**
+ * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
+ * @pos: Pointer to the IE header
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_parse_generic(const u8 *pos, const u8 *end,
+ struct wpa_eapol_ie_parse *ie)
+{
+ if (pos[1] == 0)
+ return 1;
+
+ if (pos[1] >= 6 &&
+ RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
+ pos[2 + WPA_SELECTOR_LEN] == 1 &&
+ pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
+ ie->wpa_ie = pos;
+ ie->wpa_ie_len = pos[1] + 2;
+ wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key",
+ ie->wpa_ie, ie->wpa_ie_len);
+ return 0;
+ }
+
+ if (pos + 1 + RSN_SELECTOR_LEN < end &&
+ pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
+ ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
+ ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
+ ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
+ ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
+#ifdef CONFIG_PEERKEY
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
+ ie->smk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
+ ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
+ ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
+ ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
+ ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
+ ie->error = pos + 2 + RSN_SELECTOR_LEN;
+ ie->error_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211W
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
+ ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs
+ * @buf: Pointer to the Key Data buffer
+ * @len: Key Data Length
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
+ struct wpa_eapol_ie_parse *ie)
+{
+ const u8 *pos, *end;
+ int ret = 0;
+
+ os_memset(ie, 0, sizeof(*ie));
+ for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+ if (pos[0] == 0xdd &&
+ ((pos == buf + len - 1) || pos[1] == 0)) {
+ /* Ignore padding */
+ break;
+ }
+ if (pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
+ "underflow (ie=%d len=%d pos=%d)",
+ pos[0], pos[1], (int) (pos - buf));
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
+ buf, len);
+ ret = -1;
+ break;
+ }
+ if (*pos == WLAN_EID_RSN) {
+ ie->rsn_ie = pos;
+ ie->rsn_ie_len = pos[1] + 2;
+ wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
+ ie->rsn_ie, ie->rsn_ie_len);
+ } 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) {
+ 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_VENDOR_SPECIFIC) {
+ ret = wpa_parse_generic(pos, end, ie);
+ if (ret < 0)
+ break;
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ } else {
+ wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
+ "Key Data IE", pos, 2 + pos[1]);
+ }
+ }
+
+ return ret;
+}
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
new file mode 100644
index 0000000..f939b13
--- /dev/null
+++ b/src/rsn_supp/wpa_ie.h
@@ -0,0 +1,60 @@
+/*
+ * wpa_supplicant - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_IE_H
+#define WPA_IE_H
+
+struct wpa_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;
+};
+
+int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
+ struct wpa_eapol_ie_parse *ie);
+int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len);
+
+#endif /* WPA_IE_H */
diff --git a/src/tls/.gitignore b/src/tls/.gitignore
new file mode 100644
index 0000000..d43242d
--- /dev/null
+++ b/src/tls/.gitignore
@@ -0,0 +1 @@
+libtls.a
diff --git a/src/tls/Makefile b/src/tls/Makefile
new file mode 100644
index 0000000..a2da096
--- /dev/null
+++ b/src/tls/Makefile
@@ -0,0 +1,37 @@
+all: libtls.a
+
+clean:
+ rm -f *~ *.o *.d libtls.a
+
+install:
+ @echo Nothing to be made.
+
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+
+LIB_OBJS= \
+ asn1.o \
+ bignum.o \
+ pkcs1.o \
+ pkcs5.o \
+ pkcs8.o \
+ rsa.o \
+ tlsv1_client.o \
+ tlsv1_client_read.o \
+ tlsv1_client_write.o \
+ tlsv1_common.o \
+ tlsv1_cred.o \
+ tlsv1_record.o \
+ tlsv1_server.o \
+ tlsv1_server_read.o \
+ tlsv1_server_write.o \
+ x509v3.o
+
+
+libtls.a: $(LIB_OBJS)
+ $(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/tls/asn1.c b/src/tls/asn1.c
new file mode 100644
index 0000000..3391245
--- /dev/null
+++ b/src/tls/asn1.c
@@ -0,0 +1,212 @@
+/*
+ * ASN.1 DER parsing
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "asn1.h"
+
+int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
+{
+ const u8 *pos, *end;
+ u8 tmp;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos = buf;
+ end = buf + len;
+
+ hdr->identifier = *pos++;
+ hdr->class = hdr->identifier >> 6;
+ hdr->constructed = !!(hdr->identifier & (1 << 5));
+
+ if ((hdr->identifier & 0x1f) == 0x1f) {
+ hdr->tag = 0;
+ do {
+ if (pos >= end) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
+ "underflow");
+ return -1;
+ }
+ tmp = *pos++;
+ wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: "
+ "0x%02x", tmp);
+ hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
+ } while (tmp & 0x80);
+ } else
+ hdr->tag = hdr->identifier & 0x1f;
+
+ tmp = *pos++;
+ if (tmp & 0x80) {
+ if (tmp == 0xff) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
+ "value 0xff used");
+ return -1;
+ }
+ tmp &= 0x7f; /* number of subsequent octets */
+ hdr->length = 0;
+ if (tmp > 4) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
+ return -1;
+ }
+ while (tmp--) {
+ if (pos >= end) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Length "
+ "underflow");
+ return -1;
+ }
+ hdr->length = (hdr->length << 8) | *pos++;
+ }
+ } else {
+ /* Short form - length 0..127 in one octet */
+ hdr->length = tmp;
+ }
+
+ if (end < pos || hdr->length > (unsigned int) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
+ return -1;
+ }
+
+ hdr->payload = pos;
+ return 0;
+}
+
+
+int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid)
+{
+ const u8 *pos, *end;
+ unsigned long val;
+ u8 tmp;
+
+ os_memset(oid, 0, sizeof(*oid));
+
+ pos = buf;
+ end = buf + len;
+
+ while (pos < end) {
+ val = 0;
+
+ do {
+ if (pos >= end)
+ return -1;
+ tmp = *pos++;
+ val = (val << 7) | (tmp & 0x7f);
+ } while (tmp & 0x80);
+
+ if (oid->len >= ASN1_MAX_OID_LEN) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
+ return -1;
+ }
+ if (oid->len == 0) {
+ /*
+ * The first octet encodes the first two object
+ * identifier components in (X*40) + Y formula.
+ * X = 0..2.
+ */
+ oid->oid[0] = val / 40;
+ if (oid->oid[0] > 2)
+ oid->oid[0] = 2;
+ oid->oid[1] = val - oid->oid[0] * 40;
+ oid->len = 2;
+ } else
+ oid->oid[oid->len++] = val;
+ }
+
+ return 0;
+}
+
+
+int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
+ const u8 **next)
+{
+ struct asn1_hdr hdr;
+
+ if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0)
+ return -1;
+
+ if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d "
+ "tag 0x%x", hdr.class, hdr.tag);
+ return -1;
+ }
+
+ *next = hdr.payload + hdr.length;
+
+ return asn1_parse_oid(hdr.payload, hdr.length, oid);
+}
+
+
+void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
+{
+ char *pos = buf;
+ size_t i;
+ int ret;
+
+ if (len == 0)
+ return;
+
+ buf[0] = '\0';
+
+ for (i = 0; i < oid->len; i++) {
+ ret = os_snprintf(pos, buf + len - pos,
+ "%s%lu",
+ i == 0 ? "" : ".", oid->oid[i]);
+ if (ret < 0 || ret >= buf + len - pos)
+ break;
+ pos += ret;
+ }
+ buf[len - 1] = '\0';
+}
+
+
+static u8 rotate_bits(u8 octet)
+{
+ int i;
+ u8 res;
+
+ res = 0;
+ for (i = 0; i < 8; i++) {
+ res <<= 1;
+ if (octet & 1)
+ res |= 1;
+ octet >>= 1;
+ }
+
+ return res;
+}
+
+
+unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
+{
+ unsigned long val = 0;
+ const u8 *pos = buf;
+
+ /* BER requires that unused bits are zero, so we can ignore the number
+ * of unused bits */
+ pos++;
+
+ if (len >= 2)
+ val |= rotate_bits(*pos++);
+ if (len >= 3)
+ val |= ((unsigned long) rotate_bits(*pos++)) << 8;
+ if (len >= 4)
+ val |= ((unsigned long) rotate_bits(*pos++)) << 16;
+ if (len >= 5)
+ val |= ((unsigned long) rotate_bits(*pos++)) << 24;
+ if (len >= 6)
+ wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
+ "(BIT STRING length %lu)",
+ __func__, (unsigned long) len);
+
+ return val;
+}
diff --git a/src/tls/asn1.h b/src/tls/asn1.h
new file mode 100644
index 0000000..2ff571e
--- /dev/null
+++ b/src/tls/asn1.h
@@ -0,0 +1,72 @@
+/*
+ * ASN.1 DER parsing
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef ASN1_H
+#define ASN1_H
+
+#define ASN1_TAG_EOC 0x00 /* not used with DER */
+#define ASN1_TAG_BOOLEAN 0x01
+#define ASN1_TAG_INTEGER 0x02
+#define ASN1_TAG_BITSTRING 0x03
+#define ASN1_TAG_OCTETSTRING 0x04
+#define ASN1_TAG_NULL 0x05
+#define ASN1_TAG_OID 0x06
+#define ASN1_TAG_OBJECT_DESCRIPTOR 0x07 /* not yet parsed */
+#define ASN1_TAG_EXTERNAL 0x08 /* not yet parsed */
+#define ASN1_TAG_REAL 0x09 /* not yet parsed */
+#define ASN1_TAG_ENUMERATED 0x0A /* not yet parsed */
+#define ASN1_TAG_UTF8STRING 0x0C /* not yet parsed */
+#define ANS1_TAG_RELATIVE_OID 0x0D
+#define ASN1_TAG_SEQUENCE 0x10 /* shall be constructed */
+#define ASN1_TAG_SET 0x11
+#define ASN1_TAG_NUMERICSTRING 0x12 /* not yet parsed */
+#define ASN1_TAG_PRINTABLESTRING 0x13
+#define ASN1_TAG_TG1STRING 0x14 /* not yet parsed */
+#define ASN1_TAG_VIDEOTEXSTRING 0x15 /* not yet parsed */
+#define ASN1_TAG_IA5STRING 0x16
+#define ASN1_TAG_UTCTIME 0x17
+#define ASN1_TAG_GENERALIZEDTIME 0x18 /* not yet parsed */
+#define ASN1_TAG_GRAPHICSTRING 0x19 /* not yet parsed */
+#define ASN1_TAG_VISIBLESTRING 0x1A
+#define ASN1_TAG_GENERALSTRING 0x1B /* not yet parsed */
+#define ASN1_TAG_UNIVERSALSTRING 0x1C /* not yet parsed */
+#define ASN1_TAG_BMPSTRING 0x1D /* not yet parsed */
+
+#define ASN1_CLASS_UNIVERSAL 0
+#define ASN1_CLASS_APPLICATION 1
+#define ASN1_CLASS_CONTEXT_SPECIFIC 2
+#define ASN1_CLASS_PRIVATE 3
+
+
+struct asn1_hdr {
+ const u8 *payload;
+ u8 identifier, class, constructed;
+ unsigned int tag, length;
+};
+
+#define ASN1_MAX_OID_LEN 20
+struct asn1_oid {
+ unsigned long oid[ASN1_MAX_OID_LEN];
+ size_t len;
+};
+
+
+int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr);
+int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid);
+int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
+ const u8 **next);
+void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len);
+unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len);
+
+#endif /* ASN1_H */
diff --git a/src/tls/bignum.c b/src/tls/bignum.c
new file mode 100644
index 0000000..5c0fc62
--- /dev/null
+++ b/src/tls/bignum.c
@@ -0,0 +1,230 @@
+/*
+ * Big number math
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "bignum.h"
+
+#ifdef CONFIG_INTERNAL_LIBTOMMATH
+#include "libtommath.c"
+#else /* CONFIG_INTERNAL_LIBTOMMATH */
+#include <tommath.h>
+#endif /* CONFIG_INTERNAL_LIBTOMMATH */
+
+
+/*
+ * The current version is just a wrapper for LibTomMath library, so
+ * struct bignum is just typecast to mp_int.
+ */
+
+/**
+ * bignum_init - Allocate memory for bignum
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct bignum * bignum_init(void)
+{
+ struct bignum *n = os_zalloc(sizeof(mp_int));
+ if (n == NULL)
+ return NULL;
+ if (mp_init((mp_int *) n) != MP_OKAY) {
+ os_free(n);
+ n = NULL;
+ }
+ return n;
+}
+
+
+/**
+ * bignum_deinit - Free bignum
+ * @n: Bignum from bignum_init()
+ */
+void bignum_deinit(struct bignum *n)
+{
+ if (n) {
+ mp_clear((mp_int *) n);
+ os_free(n);
+ }
+}
+
+
+/**
+ * bignum_get_unsigned_bin - Get length of bignum as an unsigned binary buffer
+ * @n: Bignum from bignum_init()
+ * Returns: Length of n if written to a binary buffer
+ */
+size_t bignum_get_unsigned_bin_len(struct bignum *n)
+{
+ return mp_unsigned_bin_size((mp_int *) n);
+}
+
+
+/**
+ * bignum_get_unsigned_bin - Set binary buffer to unsigned bignum
+ * @n: Bignum from bignum_init()
+ * @buf: Buffer for the binary number
+ * @len: Length of the buffer, can be %NULL if buffer is known to be long
+ * enough. Set to used buffer length on success if not %NULL.
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len)
+{
+ size_t need = mp_unsigned_bin_size((mp_int *) n);
+ if (len && need > *len) {
+ *len = need;
+ return -1;
+ }
+ if (mp_to_unsigned_bin((mp_int *) n, buf) != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ if (len)
+ *len = need;
+ return 0;
+}
+
+
+/**
+ * bignum_set_unsigned_bin - Set bignum based on unsigned binary buffer
+ * @n: Bignum from bignum_init(); to be set to the given value
+ * @buf: Buffer with unsigned binary value
+ * @len: Length of buf in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len)
+{
+ if (mp_read_unsigned_bin((mp_int *) n, (u8 *) buf, len) != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * bignum_cmp - Signed comparison
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_cmp(const struct bignum *a, const struct bignum *b)
+{
+ return mp_cmp((mp_int *) a, (mp_int *) b);
+}
+
+
+/**
+ * bignum_cmd_d - Compare bignum to standard integer
+ * @a: Bignum from bignum_init()
+ * @b: Small integer
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_cmp_d(const struct bignum *a, unsigned long b)
+{
+ return mp_cmp_d((mp_int *) a, b);
+}
+
+
+/**
+ * bignum_add - c = a + b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_add(const struct bignum *a, const struct bignum *b,
+ struct bignum *c)
+{
+ if (mp_add((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * bignum_sub - c = a - b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a - b
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_sub(const struct bignum *a, const struct bignum *b,
+ struct bignum *c)
+{
+ if (mp_sub((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * bignum_mul - c = a * b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a * b
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_mul(const struct bignum *a, const struct bignum *b,
+ struct bignum *c)
+{
+ if (mp_mul((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * bignum_mulmod - d = a * b (mod c)
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); modulus
+ * @d: Bignum from bignum_init(); used to store the result of a * b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_mulmod(const struct bignum *a, const struct bignum *b,
+ const struct bignum *c, struct bignum *d)
+{
+ if (mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
+ != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * bignum_exptmod - Modular exponentiation: d = a^b (mod c)
+ * @a: Bignum from bignum_init(); base
+ * @b: Bignum from bignum_init(); exponent
+ * @c: Bignum from bignum_init(); modulus
+ * @d: Bignum from bignum_init(); used to store the result of a^b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_exptmod(const struct bignum *a, const struct bignum *b,
+ const struct bignum *c, struct bignum *d)
+{
+ if (mp_exptmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
+ != MP_OKAY) {
+ wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+ return -1;
+ }
+ return 0;
+}
diff --git a/src/tls/bignum.h b/src/tls/bignum.h
new file mode 100644
index 0000000..f25e267
--- /dev/null
+++ b/src/tls/bignum.h
@@ -0,0 +1,38 @@
+/*
+ * Big number math
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef BIGNUM_H
+#define BIGNUM_H
+
+struct bignum;
+
+struct bignum * bignum_init(void);
+void bignum_deinit(struct bignum *n);
+size_t bignum_get_unsigned_bin_len(struct bignum *n);
+int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len);
+int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len);
+int bignum_cmp(const struct bignum *a, const struct bignum *b);
+int bignum_cmp_d(const struct bignum *a, unsigned long b);
+int bignum_add(const struct bignum *a, const struct bignum *b,
+ struct bignum *c);
+int bignum_sub(const struct bignum *a, const struct bignum *b,
+ struct bignum *c);
+int bignum_mul(const struct bignum *a, const struct bignum *b,
+ struct bignum *c);
+int bignum_mulmod(const struct bignum *a, const struct bignum *b,
+ const struct bignum *c, struct bignum *d);
+int bignum_exptmod(const struct bignum *a, const struct bignum *b,
+ const struct bignum *c, struct bignum *d);
+
+#endif /* BIGNUM_H */
diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c
new file mode 100644
index 0000000..1374264
--- /dev/null
+++ b/src/tls/libtommath.c
@@ -0,0 +1,3381 @@
+/*
+ * Minimal code for RSA support from LibTomMath 0.41
+ * http://libtom.org/
+ * http://libtom.org/files/ltm-0.41.tar.bz2
+ * This library was released in public domain by Tom St Denis.
+ *
+ * The combination in this file may not use all of the optimized algorithms
+ * from LibTomMath and may be considerable slower than the LibTomMath with its
+ * default settings. The main purpose of having this version here is to make it
+ * easier to build bignum.c wrapper without having to install and build an
+ * external library.
+ *
+ * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this
+ * libtommath.c file instead of using the external LibTomMath library.
+ */
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#define BN_MP_INVMOD_C
+#define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would
+ * require BN_MP_EXPTMOD_FAST_C instead */
+#define BN_S_MP_MUL_DIGS_C
+#define BN_MP_INVMOD_SLOW_C
+#define BN_S_MP_SQR_C
+#define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this
+ * would require other than mp_reduce */
+
+#ifdef LTM_FAST
+
+/* Use faster div at the cost of about 1 kB */
+#define BN_MP_MUL_D_C
+
+/* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */
+#define BN_MP_EXPTMOD_FAST_C
+#define BN_MP_MONTGOMERY_SETUP_C
+#define BN_FAST_MP_MONTGOMERY_REDUCE_C
+#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+#define BN_MP_MUL_2_C
+
+/* Include faster sqr at the cost of about 0.5 kB in code */
+#define BN_FAST_S_MP_SQR_C
+
+#else /* LTM_FAST */
+
+#define BN_MP_DIV_SMALL
+#define BN_MP_INIT_MULTI_C
+#define BN_MP_CLEAR_MULTI_C
+#define BN_MP_ABS_C
+#endif /* LTM_FAST */
+
+/* Current uses do not require support for negative exponent in exptmod, so we
+ * can save about 1.5 kB in leaving out invmod. */
+#define LTM_NO_NEG_EXP
+
+/* from tommath.h */
+
+#ifndef MIN
+ #define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
+#ifndef MAX
+ #define MAX(x,y) ((x)>(y)?(x):(y))
+#endif
+
+#define OPT_CAST(x)
+
+typedef unsigned long mp_digit;
+typedef u64 mp_word;
+
+#define DIGIT_BIT 28
+#define MP_28BIT
+
+
+#define XMALLOC os_malloc
+#define XFREE os_free
+#define XREALLOC os_realloc
+
+
+#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1))
+
+#define MP_LT -1 /* less than */
+#define MP_EQ 0 /* equal to */
+#define MP_GT 1 /* greater than */
+
+#define MP_ZPOS 0 /* positive integer */
+#define MP_NEG 1 /* negative */
+
+#define MP_OKAY 0 /* ok result */
+#define MP_MEM -2 /* out of mem */
+#define MP_VAL -3 /* invalid input */
+
+#define MP_YES 1 /* yes response */
+#define MP_NO 0 /* no response */
+
+typedef int mp_err;
+
+/* define this to use lower memory usage routines (exptmods mostly) */
+#define MP_LOW_MEM
+
+/* default precision */
+#ifndef MP_PREC
+ #ifndef MP_LOW_MEM
+ #define MP_PREC 32 /* default digits of precision */
+ #else
+ #define MP_PREC 8 /* default digits of precision */
+ #endif
+#endif
+
+/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */
+#define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1))
+
+/* the infamous mp_int structure */
+typedef struct {
+ int used, alloc, sign;
+ mp_digit *dp;
+} mp_int;
+
+
+/* ---> Basic Manipulations <--- */
+#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO)
+#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO)
+#define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO)
+
+
+/* prototypes for copied functions */
+#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1)
+static int s_mp_exptmod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode);
+static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs);
+static int s_mp_sqr(mp_int * a, mp_int * b);
+static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs);
+
+static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs);
+
+#ifdef BN_MP_INIT_MULTI_C
+static int mp_init_multi(mp_int *mp, ...);
+#endif
+#ifdef BN_MP_CLEAR_MULTI_C
+static void mp_clear_multi(mp_int *mp, ...);
+#endif
+static int mp_lshd(mp_int * a, int b);
+static void mp_set(mp_int * a, mp_digit b);
+static void mp_clamp(mp_int * a);
+static void mp_exch(mp_int * a, mp_int * b);
+static void mp_rshd(mp_int * a, int b);
+static void mp_zero(mp_int * a);
+static int mp_mod_2d(mp_int * a, int b, mp_int * c);
+static int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d);
+static int mp_init_copy(mp_int * a, mp_int * b);
+static int mp_mul_2d(mp_int * a, int b, mp_int * c);
+#ifndef LTM_NO_NEG_EXP
+static int mp_div_2(mp_int * a, mp_int * b);
+static int mp_invmod(mp_int * a, mp_int * b, mp_int * c);
+static int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c);
+#endif /* LTM_NO_NEG_EXP */
+static int mp_copy(mp_int * a, mp_int * b);
+static int mp_count_bits(mp_int * a);
+static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d);
+static int mp_mod(mp_int * a, mp_int * b, mp_int * c);
+static int mp_grow(mp_int * a, int size);
+static int mp_cmp_mag(mp_int * a, mp_int * b);
+#ifdef BN_MP_ABS_C
+static int mp_abs(mp_int * a, mp_int * b);
+#endif
+static int mp_sqr(mp_int * a, mp_int * b);
+static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d);
+static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d);
+static int mp_2expt(mp_int * a, int b);
+static int mp_reduce_setup(mp_int * a, mp_int * b);
+static int mp_reduce(mp_int * x, mp_int * m, mp_int * mu);
+static int mp_init_size(mp_int * a, int size);
+#ifdef BN_MP_EXPTMOD_FAST_C
+static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode);
+#endif /* BN_MP_EXPTMOD_FAST_C */
+#ifdef BN_FAST_S_MP_SQR_C
+static int fast_s_mp_sqr (mp_int * a, mp_int * b);
+#endif /* BN_FAST_S_MP_SQR_C */
+#ifdef BN_MP_MUL_D_C
+static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c);
+#endif /* BN_MP_MUL_D_C */
+
+
+
+/* functions from bn_<func name>.c */
+
+
+/* reverse an array, used for radix code */
+static void bn_reverse (unsigned char *s, int len)
+{
+ int ix, iy;
+ unsigned char t;
+
+ ix = 0;
+ iy = len - 1;
+ while (ix < iy) {
+ t = s[ix];
+ s[ix] = s[iy];
+ s[iy] = t;
+ ++ix;
+ --iy;
+ }
+}
+
+
+/* low level addition, based on HAC pp.594, Algorithm 14.7 */
+static int s_mp_add (mp_int * a, mp_int * b, mp_int * c)
+{
+ mp_int *x;
+ int olduse, res, min, max;
+
+ /* find sizes, we let |a| <= |b| which means we have to sort
+ * them. "x" will point to the input with the most digits
+ */
+ if (a->used > b->used) {
+ min = b->used;
+ max = a->used;
+ x = a;
+ } else {
+ min = a->used;
+ max = b->used;
+ x = b;
+ }
+
+ /* init result */
+ if (c->alloc < max + 1) {
+ if ((res = mp_grow (c, max + 1)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* get old used digit count and set new one */
+ olduse = c->used;
+ c->used = max + 1;
+
+ {
+ register mp_digit u, *tmpa, *tmpb, *tmpc;
+ register int i;
+
+ /* alias for digit pointers */
+
+ /* first input */
+ tmpa = a->dp;
+
+ /* second input */
+ tmpb = b->dp;
+
+ /* destination */
+ tmpc = c->dp;
+
+ /* zero the carry */
+ u = 0;
+ for (i = 0; i < min; i++) {
+ /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */
+ *tmpc = *tmpa++ + *tmpb++ + u;
+
+ /* U = carry bit of T[i] */
+ u = *tmpc >> ((mp_digit)DIGIT_BIT);
+
+ /* take away carry bit from T[i] */
+ *tmpc++ &= MP_MASK;
+ }
+
+ /* now copy higher words if any, that is in A+B
+ * if A or B has more digits add those in
+ */
+ if (min != max) {
+ for (; i < max; i++) {
+ /* T[i] = X[i] + U */
+ *tmpc = x->dp[i] + u;
+
+ /* U = carry bit of T[i] */
+ u = *tmpc >> ((mp_digit)DIGIT_BIT);
+
+ /* take away carry bit from T[i] */
+ *tmpc++ &= MP_MASK;
+ }
+ }
+
+ /* add carry */
+ *tmpc++ = u;
+
+ /* clear digits above oldused */
+ for (i = c->used; i < olduse; i++) {
+ *tmpc++ = 0;
+ }
+ }
+
+ mp_clamp (c);
+ return MP_OKAY;
+}
+
+
+/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */
+static int s_mp_sub (mp_int * a, mp_int * b, mp_int * c)
+{
+ int olduse, res, min, max;
+
+ /* find sizes */
+ min = b->used;
+ max = a->used;
+
+ /* init result */
+ if (c->alloc < max) {
+ if ((res = mp_grow (c, max)) != MP_OKAY) {
+ return res;
+ }
+ }
+ olduse = c->used;
+ c->used = max;
+
+ {
+ register mp_digit u, *tmpa, *tmpb, *tmpc;
+ register int i;
+
+ /* alias for digit pointers */
+ tmpa = a->dp;
+ tmpb = b->dp;
+ tmpc = c->dp;
+
+ /* set carry to zero */
+ u = 0;
+ for (i = 0; i < min; i++) {
+ /* T[i] = A[i] - B[i] - U */
+ *tmpc = *tmpa++ - *tmpb++ - u;
+
+ /* U = carry bit of T[i]
+ * Note this saves performing an AND operation since
+ * if a carry does occur it will propagate all the way to the
+ * MSB. As a result a single shift is enough to get the carry
+ */
+ u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1));
+
+ /* Clear carry from T[i] */
+ *tmpc++ &= MP_MASK;
+ }
+
+ /* now copy higher words if any, e.g. if A has more digits than B */
+ for (; i < max; i++) {
+ /* T[i] = A[i] - U */
+ *tmpc = *tmpa++ - u;
+
+ /* U = carry bit of T[i] */
+ u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1));
+
+ /* Clear carry from T[i] */
+ *tmpc++ &= MP_MASK;
+ }
+
+ /* clear digits above used (since we may not have grown result above) */
+ for (i = c->used; i < olduse; i++) {
+ *tmpc++ = 0;
+ }
+ }
+
+ mp_clamp (c);
+ return MP_OKAY;
+}
+
+
+/* init a new mp_int */
+static int mp_init (mp_int * a)
+{
+ int i;
+
+ /* allocate memory required and clear it */
+ a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC);
+ if (a->dp == NULL) {
+ return MP_MEM;
+ }
+
+ /* set the digits to zero */
+ for (i = 0; i < MP_PREC; i++) {
+ a->dp[i] = 0;
+ }
+
+ /* set the used to zero, allocated digits to the default precision
+ * and sign to positive */
+ a->used = 0;
+ a->alloc = MP_PREC;
+ a->sign = MP_ZPOS;
+
+ return MP_OKAY;
+}
+
+
+/* clear one (frees) */
+static void mp_clear (mp_int * a)
+{
+ int i;
+
+ /* only do anything if a hasn't been freed previously */
+ if (a->dp != NULL) {
+ /* first zero the digits */
+ for (i = 0; i < a->used; i++) {
+ a->dp[i] = 0;
+ }
+
+ /* free ram */
+ XFREE(a->dp);
+
+ /* reset members to make debugging easier */
+ a->dp = NULL;
+ a->alloc = a->used = 0;
+ a->sign = MP_ZPOS;
+ }
+}
+
+
+/* high level addition (handles signs) */
+static int mp_add (mp_int * a, mp_int * b, mp_int * c)
+{
+ int sa, sb, res;
+
+ /* get sign of both inputs */
+ sa = a->sign;
+ sb = b->sign;
+
+ /* handle two cases, not four */
+ if (sa == sb) {
+ /* both positive or both negative */
+ /* add their magnitudes, copy the sign */
+ c->sign = sa;
+ res = s_mp_add (a, b, c);
+ } else {
+ /* one positive, the other negative */
+ /* subtract the one with the greater magnitude from */
+ /* the one of the lesser magnitude. The result gets */
+ /* the sign of the one with the greater magnitude. */
+ if (mp_cmp_mag (a, b) == MP_LT) {
+ c->sign = sb;
+ res = s_mp_sub (b, a, c);
+ } else {
+ c->sign = sa;
+ res = s_mp_sub (a, b, c);
+ }
+ }
+ return res;
+}
+
+
+/* high level subtraction (handles signs) */
+static int mp_sub (mp_int * a, mp_int * b, mp_int * c)
+{
+ int sa, sb, res;
+
+ sa = a->sign;
+ sb = b->sign;
+
+ if (sa != sb) {
+ /* subtract a negative from a positive, OR */
+ /* subtract a positive from a negative. */
+ /* In either case, ADD their magnitudes, */
+ /* and use the sign of the first number. */
+ c->sign = sa;
+ res = s_mp_add (a, b, c);
+ } else {
+ /* subtract a positive from a positive, OR */
+ /* subtract a negative from a negative. */
+ /* First, take the difference between their */
+ /* magnitudes, then... */
+ if (mp_cmp_mag (a, b) != MP_LT) {
+ /* Copy the sign from the first */
+ c->sign = sa;
+ /* The first has a larger or equal magnitude */
+ res = s_mp_sub (a, b, c);
+ } else {
+ /* The result has the *opposite* sign from */
+ /* the first number. */
+ c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS;
+ /* The second has a larger magnitude */
+ res = s_mp_sub (b, a, c);
+ }
+ }
+ return res;
+}
+
+
+/* high level multiplication (handles sign) */
+static int mp_mul (mp_int * a, mp_int * b, mp_int * c)
+{
+ int res, neg;
+ neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
+
+ /* use Toom-Cook? */
+#ifdef BN_MP_TOOM_MUL_C
+ if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) {
+ res = mp_toom_mul(a, b, c);
+ } else
+#endif
+#ifdef BN_MP_KARATSUBA_MUL_C
+ /* use Karatsuba? */
+ if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) {
+ res = mp_karatsuba_mul (a, b, c);
+ } else
+#endif
+ {
+ /* can we use the fast multiplier?
+ *
+ * The fast multiplier can be used if the output will
+ * have less than MP_WARRAY digits and the number of
+ * digits won't affect carry propagation
+ */
+#ifdef BN_FAST_S_MP_MUL_DIGS_C
+ int digs = a->used + b->used + 1;
+
+ if ((digs < MP_WARRAY) &&
+ MIN(a->used, b->used) <=
+ (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+ res = fast_s_mp_mul_digs (a, b, c, digs);
+ } else
+#endif
+#ifdef BN_S_MP_MUL_DIGS_C
+ res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */
+#else
+#error mp_mul could fail
+ res = MP_VAL;
+#endif
+
+ }
+ c->sign = (c->used > 0) ? neg : MP_ZPOS;
+ return res;
+}
+
+
+/* d = a * b (mod c) */
+static int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
+{
+ int res;
+ mp_int t;
+
+ if ((res = mp_init (&t)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_mul (a, b, &t)) != MP_OKAY) {
+ mp_clear (&t);
+ return res;
+ }
+ res = mp_mod (&t, c, d);
+ mp_clear (&t);
+ return res;
+}
+
+
+/* c = a mod b, 0 <= c < b */
+static int mp_mod (mp_int * a, mp_int * b, mp_int * c)
+{
+ mp_int t;
+ int res;
+
+ if ((res = mp_init (&t)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) {
+ mp_clear (&t);
+ return res;
+ }
+
+ if (t.sign != b->sign) {
+ res = mp_add (b, &t, c);
+ } else {
+ res = MP_OKAY;
+ mp_exch (&t, c);
+ }
+
+ mp_clear (&t);
+ return res;
+}
+
+
+/* this is a shell function that calls either the normal or Montgomery
+ * exptmod functions. Originally the call to the montgomery code was
+ * embedded in the normal function but that wasted alot of stack space
+ * for nothing (since 99% of the time the Montgomery code would be called)
+ */
+static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
+{
+ int dr;
+
+ /* modulus P must be positive */
+ if (P->sign == MP_NEG) {
+ return MP_VAL;
+ }
+
+ /* if exponent X is negative we have to recurse */
+ if (X->sign == MP_NEG) {
+#ifdef LTM_NO_NEG_EXP
+ return MP_VAL;
+#else /* LTM_NO_NEG_EXP */
+#ifdef BN_MP_INVMOD_C
+ mp_int tmpG, tmpX;
+ int err;
+
+ /* first compute 1/G mod P */
+ if ((err = mp_init(&tmpG)) != MP_OKAY) {
+ return err;
+ }
+ if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) {
+ mp_clear(&tmpG);
+ return err;
+ }
+
+ /* now get |X| */
+ if ((err = mp_init(&tmpX)) != MP_OKAY) {
+ mp_clear(&tmpG);
+ return err;
+ }
+ if ((err = mp_abs(X, &tmpX)) != MP_OKAY) {
+ mp_clear_multi(&tmpG, &tmpX, NULL);
+ return err;
+ }
+
+ /* and now compute (1/G)**|X| instead of G**X [X < 0] */
+ err = mp_exptmod(&tmpG, &tmpX, P, Y);
+ mp_clear_multi(&tmpG, &tmpX, NULL);
+ return err;
+#else
+#error mp_exptmod would always fail
+ /* no invmod */
+ return MP_VAL;
+#endif
+#endif /* LTM_NO_NEG_EXP */
+ }
+
+/* modified diminished radix reduction */
+#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C)
+ if (mp_reduce_is_2k_l(P) == MP_YES) {
+ return s_mp_exptmod(G, X, P, Y, 1);
+ }
+#endif
+
+#ifdef BN_MP_DR_IS_MODULUS_C
+ /* is it a DR modulus? */
+ dr = mp_dr_is_modulus(P);
+#else
+ /* default to no */
+ dr = 0;
+#endif
+
+#ifdef BN_MP_REDUCE_IS_2K_C
+ /* if not, is it a unrestricted DR modulus? */
+ if (dr == 0) {
+ dr = mp_reduce_is_2k(P) << 1;
+ }
+#endif
+
+ /* if the modulus is odd or dr != 0 use the montgomery method */
+#ifdef BN_MP_EXPTMOD_FAST_C
+ if (mp_isodd (P) == 1 || dr != 0) {
+ return mp_exptmod_fast (G, X, P, Y, dr);
+ } else {
+#endif
+#ifdef BN_S_MP_EXPTMOD_C
+ /* otherwise use the generic Barrett reduction technique */
+ return s_mp_exptmod (G, X, P, Y, 0);
+#else
+#error mp_exptmod could fail
+ /* no exptmod for evens */
+ return MP_VAL;
+#endif
+#ifdef BN_MP_EXPTMOD_FAST_C
+ }
+#endif
+}
+
+
+/* compare two ints (signed)*/
+static int mp_cmp (mp_int * a, mp_int * b)
+{
+ /* compare based on sign */
+ if (a->sign != b->sign) {
+ if (a->sign == MP_NEG) {
+ return MP_LT;
+ } else {
+ return MP_GT;
+ }
+ }
+
+ /* compare digits */
+ if (a->sign == MP_NEG) {
+ /* if negative compare opposite direction */
+ return mp_cmp_mag(b, a);
+ } else {
+ return mp_cmp_mag(a, b);
+ }
+}
+
+
+/* compare a digit */
+static int mp_cmp_d(mp_int * a, mp_digit b)
+{
+ /* compare based on sign */
+ if (a->sign == MP_NEG) {
+ return MP_LT;
+ }
+
+ /* compare based on magnitude */
+ if (a->used > 1) {
+ return MP_GT;
+ }
+
+ /* compare the only digit of a to b */
+ if (a->dp[0] > b) {
+ return MP_GT;
+ } else if (a->dp[0] < b) {
+ return MP_LT;
+ } else {
+ return MP_EQ;
+ }
+}
+
+
+#ifndef LTM_NO_NEG_EXP
+/* hac 14.61, pp608 */
+static int mp_invmod (mp_int * a, mp_int * b, mp_int * c)
+{
+ /* b cannot be negative */
+ if (b->sign == MP_NEG || mp_iszero(b) == 1) {
+ return MP_VAL;
+ }
+
+#ifdef BN_FAST_MP_INVMOD_C
+ /* if the modulus is odd we can use a faster routine instead */
+ if (mp_isodd (b) == 1) {
+ return fast_mp_invmod (a, b, c);
+ }
+#endif
+
+#ifdef BN_MP_INVMOD_SLOW_C
+ return mp_invmod_slow(a, b, c);
+#endif
+
+#ifndef BN_FAST_MP_INVMOD_C
+#ifndef BN_MP_INVMOD_SLOW_C
+#error mp_invmod would always fail
+#endif
+#endif
+ return MP_VAL;
+}
+#endif /* LTM_NO_NEG_EXP */
+
+
+/* get the size for an unsigned equivalent */
+static int mp_unsigned_bin_size (mp_int * a)
+{
+ int size = mp_count_bits (a);
+ return (size / 8 + ((size & 7) != 0 ? 1 : 0));
+}
+
+
+#ifndef LTM_NO_NEG_EXP
+/* hac 14.61, pp608 */
+static int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c)
+{
+ mp_int x, y, u, v, A, B, C, D;
+ int res;
+
+ /* b cannot be negative */
+ if (b->sign == MP_NEG || mp_iszero(b) == 1) {
+ return MP_VAL;
+ }
+
+ /* init temps */
+ if ((res = mp_init_multi(&x, &y, &u, &v,
+ &A, &B, &C, &D, NULL)) != MP_OKAY) {
+ return res;
+ }
+
+ /* x = a, y = b */
+ if ((res = mp_mod(a, b, &x)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_copy (b, &y)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ /* 2. [modified] if x,y are both even then return an error! */
+ if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) {
+ res = MP_VAL;
+ goto LBL_ERR;
+ }
+
+ /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */
+ if ((res = mp_copy (&x, &u)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_copy (&y, &v)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ mp_set (&A, 1);
+ mp_set (&D, 1);
+
+top:
+ /* 4. while u is even do */
+ while (mp_iseven (&u) == 1) {
+ /* 4.1 u = u/2 */
+ if ((res = mp_div_2 (&u, &u)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ /* 4.2 if A or B is odd then */
+ if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) {
+ /* A = (A+y)/2, B = (B-x)/2 */
+ if ((res = mp_add (&A, &y, &A)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+ /* A = A/2, B = B/2 */
+ if ((res = mp_div_2 (&A, &A)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_div_2 (&B, &B)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* 5. while v is even do */
+ while (mp_iseven (&v) == 1) {
+ /* 5.1 v = v/2 */
+ if ((res = mp_div_2 (&v, &v)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ /* 5.2 if C or D is odd then */
+ if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) {
+ /* C = (C+y)/2, D = (D-x)/2 */
+ if ((res = mp_add (&C, &y, &C)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+ /* C = C/2, D = D/2 */
+ if ((res = mp_div_2 (&C, &C)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if ((res = mp_div_2 (&D, &D)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* 6. if u >= v then */
+ if (mp_cmp (&u, &v) != MP_LT) {
+ /* u = u - v, A = A - C, B = B - D */
+ if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ } else {
+ /* v - v - u, C = C - A, D = D - B */
+ if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* if not zero goto step 4 */
+ if (mp_iszero (&u) == 0)
+ goto top;
+
+ /* now a = C, b = D, gcd == g*v */
+
+ /* if v != 1 then there is no inverse */
+ if (mp_cmp_d (&v, 1) != MP_EQ) {
+ res = MP_VAL;
+ goto LBL_ERR;
+ }
+
+ /* if its too low */
+ while (mp_cmp_d(&C, 0) == MP_LT) {
+ if ((res = mp_add(&C, b, &C)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* too big */
+ while (mp_cmp_mag(&C, b) != MP_LT) {
+ if ((res = mp_sub(&C, b, &C)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* C is now the inverse */
+ mp_exch (&C, c);
+ res = MP_OKAY;
+LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL);
+ return res;
+}
+#endif /* LTM_NO_NEG_EXP */
+
+
+/* compare maginitude of two ints (unsigned) */
+static int mp_cmp_mag (mp_int * a, mp_int * b)
+{
+ int n;
+ mp_digit *tmpa, *tmpb;
+
+ /* compare based on # of non-zero digits */
+ if (a->used > b->used) {
+ return MP_GT;
+ }
+
+ if (a->used < b->used) {
+ return MP_LT;
+ }
+
+ /* alias for a */
+ tmpa = a->dp + (a->used - 1);
+
+ /* alias for b */
+ tmpb = b->dp + (a->used - 1);
+
+ /* compare based on digits */
+ for (n = 0; n < a->used; ++n, --tmpa, --tmpb) {
+ if (*tmpa > *tmpb) {
+ return MP_GT;
+ }
+
+ if (*tmpa < *tmpb) {
+ return MP_LT;
+ }
+ }
+ return MP_EQ;
+}
+
+
+/* reads a unsigned char array, assumes the msb is stored first [big endian] */
+static int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c)
+{
+ int res;
+
+ /* make sure there are at least two digits */
+ if (a->alloc < 2) {
+ if ((res = mp_grow(a, 2)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* zero the int */
+ mp_zero (a);
+
+ /* read the bytes in */
+ while (c-- > 0) {
+ if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) {
+ return res;
+ }
+
+#ifndef MP_8BIT
+ a->dp[0] |= *b++;
+ a->used += 1;
+#else
+ a->dp[0] = (*b & MP_MASK);
+ a->dp[1] |= ((*b++ >> 7U) & 1);
+ a->used += 2;
+#endif
+ }
+ mp_clamp (a);
+ return MP_OKAY;
+}
+
+
+/* store in unsigned [big endian] format */
+static int mp_to_unsigned_bin (mp_int * a, unsigned char *b)
+{
+ int x, res;
+ mp_int t;
+
+ if ((res = mp_init_copy (&t, a)) != MP_OKAY) {
+ return res;
+ }
+
+ x = 0;
+ while (mp_iszero (&t) == 0) {
+#ifndef MP_8BIT
+ b[x++] = (unsigned char) (t.dp[0] & 255);
+#else
+ b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7));
+#endif
+ if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) {
+ mp_clear (&t);
+ return res;
+ }
+ }
+ bn_reverse (b, x);
+ mp_clear (&t);
+ return MP_OKAY;
+}
+
+
+/* shift right by a certain bit count (store quotient in c, optional remainder in d) */
+static int mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d)
+{
+ mp_digit D, r, rr;
+ int x, res;
+ mp_int t;
+
+
+ /* if the shift count is <= 0 then we do no work */
+ if (b <= 0) {
+ res = mp_copy (a, c);
+ if (d != NULL) {
+ mp_zero (d);
+ }
+ return res;
+ }
+
+ if ((res = mp_init (&t)) != MP_OKAY) {
+ return res;
+ }
+
+ /* get the remainder */
+ if (d != NULL) {
+ if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) {
+ mp_clear (&t);
+ return res;
+ }
+ }
+
+ /* copy */
+ if ((res = mp_copy (a, c)) != MP_OKAY) {
+ mp_clear (&t);
+ return res;
+ }
+
+ /* shift by as many digits in the bit count */
+ if (b >= (int)DIGIT_BIT) {
+ mp_rshd (c, b / DIGIT_BIT);
+ }
+
+ /* shift any bit count < DIGIT_BIT */
+ D = (mp_digit) (b % DIGIT_BIT);
+ if (D != 0) {
+ register mp_digit *tmpc, mask, shift;
+
+ /* mask */
+ mask = (((mp_digit)1) << D) - 1;
+
+ /* shift for lsb */
+ shift = DIGIT_BIT - D;
+
+ /* alias */
+ tmpc = c->dp + (c->used - 1);
+
+ /* carry */
+ r = 0;
+ for (x = c->used - 1; x >= 0; x--) {
+ /* get the lower bits of this word in a temp */
+ rr = *tmpc & mask;
+
+ /* shift the current word and mix in the carry bits from the previous word */
+ *tmpc = (*tmpc >> D) | (r << shift);
+ --tmpc;
+
+ /* set the carry to the carry bits of the current word found above */
+ r = rr;
+ }
+ }
+ mp_clamp (c);
+ if (d != NULL) {
+ mp_exch (&t, d);
+ }
+ mp_clear (&t);
+ return MP_OKAY;
+}
+
+
+static int mp_init_copy (mp_int * a, mp_int * b)
+{
+ int res;
+
+ if ((res = mp_init (a)) != MP_OKAY) {
+ return res;
+ }
+ return mp_copy (b, a);
+}
+
+
+/* set to zero */
+static void mp_zero (mp_int * a)
+{
+ int n;
+ mp_digit *tmp;
+
+ a->sign = MP_ZPOS;
+ a->used = 0;
+
+ tmp = a->dp;
+ for (n = 0; n < a->alloc; n++) {
+ *tmp++ = 0;
+ }
+}
+
+
+/* copy, b = a */
+static int mp_copy (mp_int * a, mp_int * b)
+{
+ int res, n;
+
+ /* if dst == src do nothing */
+ if (a == b) {
+ return MP_OKAY;
+ }
+
+ /* grow dest */
+ if (b->alloc < a->used) {
+ if ((res = mp_grow (b, a->used)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* zero b and copy the parameters over */
+ {
+ register mp_digit *tmpa, *tmpb;
+
+ /* pointer aliases */
+
+ /* source */
+ tmpa = a->dp;
+
+ /* destination */
+ tmpb = b->dp;
+
+ /* copy all the digits */
+ for (n = 0; n < a->used; n++) {
+ *tmpb++ = *tmpa++;
+ }
+
+ /* clear high digits */
+ for (; n < b->used; n++) {
+ *tmpb++ = 0;
+ }
+ }
+
+ /* copy used count and sign */
+ b->used = a->used;
+ b->sign = a->sign;
+ return MP_OKAY;
+}
+
+
+/* shift right a certain amount of digits */
+static void mp_rshd (mp_int * a, int b)
+{
+ int x;
+
+ /* if b <= 0 then ignore it */
+ if (b <= 0) {
+ return;
+ }
+
+ /* if b > used then simply zero it and return */
+ if (a->used <= b) {
+ mp_zero (a);
+ return;
+ }
+
+ {
+ register mp_digit *bottom, *top;
+
+ /* shift the digits down */
+
+ /* bottom */
+ bottom = a->dp;
+
+ /* top [offset into digits] */
+ top = a->dp + b;
+
+ /* this is implemented as a sliding window where
+ * the window is b-digits long and digits from
+ * the top of the window are copied to the bottom
+ *
+ * e.g.
+
+ b-2 | b-1 | b0 | b1 | b2 | ... | bb | ---->
+ /\ | ---->
+ \-------------------/ ---->
+ */
+ for (x = 0; x < (a->used - b); x++) {
+ *bottom++ = *top++;
+ }
+
+ /* zero the top digits */
+ for (; x < a->used; x++) {
+ *bottom++ = 0;
+ }
+ }
+
+ /* remove excess digits */
+ a->used -= b;
+}
+
+
+/* swap the elements of two integers, for cases where you can't simply swap the
+ * mp_int pointers around
+ */
+static void mp_exch (mp_int * a, mp_int * b)
+{
+ mp_int t;
+
+ t = *a;
+ *a = *b;
+ *b = t;
+}
+
+
+/* trim unused digits
+ *
+ * This is used to ensure that leading zero digits are
+ * trimed and the leading "used" digit will be non-zero
+ * Typically very fast. Also fixes the sign if there
+ * are no more leading digits
+ */
+static void mp_clamp (mp_int * a)
+{
+ /* decrease used while the most significant digit is
+ * zero.
+ */
+ while (a->used > 0 && a->dp[a->used - 1] == 0) {
+ --(a->used);
+ }
+
+ /* reset the sign flag if used == 0 */
+ if (a->used == 0) {
+ a->sign = MP_ZPOS;
+ }
+}
+
+
+/* grow as required */
+static int mp_grow (mp_int * a, int size)
+{
+ int i;
+ mp_digit *tmp;
+
+ /* if the alloc size is smaller alloc more ram */
+ if (a->alloc < size) {
+ /* ensure there are always at least MP_PREC digits extra on top */
+ size += (MP_PREC * 2) - (size % MP_PREC);
+
+ /* reallocate the array a->dp
+ *
+ * We store the return in a temporary variable
+ * in case the operation failed we don't want
+ * to overwrite the dp member of a.
+ */
+ tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size);
+ if (tmp == NULL) {
+ /* reallocation failed but "a" is still valid [can be freed] */
+ return MP_MEM;
+ }
+
+ /* reallocation succeeded so set a->dp */
+ a->dp = tmp;
+
+ /* zero excess digits */
+ i = a->alloc;
+ a->alloc = size;
+ for (; i < a->alloc; i++) {
+ a->dp[i] = 0;
+ }
+ }
+ return MP_OKAY;
+}
+
+
+#ifdef BN_MP_ABS_C
+/* b = |a|
+ *
+ * Simple function copies the input and fixes the sign to positive
+ */
+static int mp_abs (mp_int * a, mp_int * b)
+{
+ int res;
+
+ /* copy a to b */
+ if (a != b) {
+ if ((res = mp_copy (a, b)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* force the sign of b to positive */
+ b->sign = MP_ZPOS;
+
+ return MP_OKAY;
+}
+#endif
+
+
+/* set to a digit */
+static void mp_set (mp_int * a, mp_digit b)
+{
+ mp_zero (a);
+ a->dp[0] = b & MP_MASK;
+ a->used = (a->dp[0] != 0) ? 1 : 0;
+}
+
+
+#ifndef LTM_NO_NEG_EXP
+/* b = a/2 */
+static int mp_div_2(mp_int * a, mp_int * b)
+{
+ int x, res, oldused;
+
+ /* copy */
+ if (b->alloc < a->used) {
+ if ((res = mp_grow (b, a->used)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ oldused = b->used;
+ b->used = a->used;
+ {
+ register mp_digit r, rr, *tmpa, *tmpb;
+
+ /* source alias */
+ tmpa = a->dp + b->used - 1;
+
+ /* dest alias */
+ tmpb = b->dp + b->used - 1;
+
+ /* carry */
+ r = 0;
+ for (x = b->used - 1; x >= 0; x--) {
+ /* get the carry for the next iteration */
+ rr = *tmpa & 1;
+
+ /* shift the current digit, add in carry and store */
+ *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1));
+
+ /* forward carry to next iteration */
+ r = rr;
+ }
+
+ /* zero excess digits */
+ tmpb = b->dp + b->used;
+ for (x = b->used; x < oldused; x++) {
+ *tmpb++ = 0;
+ }
+ }
+ b->sign = a->sign;
+ mp_clamp (b);
+ return MP_OKAY;
+}
+#endif /* LTM_NO_NEG_EXP */
+
+
+/* shift left by a certain bit count */
+static int mp_mul_2d (mp_int * a, int b, mp_int * c)
+{
+ mp_digit d;
+ int res;
+
+ /* copy */
+ if (a != c) {
+ if ((res = mp_copy (a, c)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) {
+ if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* shift by as many digits in the bit count */
+ if (b >= (int)DIGIT_BIT) {
+ if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* shift any bit count < DIGIT_BIT */
+ d = (mp_digit) (b % DIGIT_BIT);
+ if (d != 0) {
+ register mp_digit *tmpc, shift, mask, r, rr;
+ register int x;
+
+ /* bitmask for carries */
+ mask = (((mp_digit)1) << d) - 1;
+
+ /* shift for msbs */
+ shift = DIGIT_BIT - d;
+
+ /* alias */
+ tmpc = c->dp;
+
+ /* carry */
+ r = 0;
+ for (x = 0; x < c->used; x++) {
+ /* get the higher bits of the current word */
+ rr = (*tmpc >> shift) & mask;
+
+ /* shift the current word and OR in the carry */
+ *tmpc = ((*tmpc << d) | r) & MP_MASK;
+ ++tmpc;
+
+ /* set the carry to the carry bits of the current word */
+ r = rr;
+ }
+
+ /* set final carry */
+ if (r != 0) {
+ c->dp[(c->used)++] = r;
+ }
+ }
+ mp_clamp (c);
+ return MP_OKAY;
+}
+
+
+#ifdef BN_MP_INIT_MULTI_C
+static int mp_init_multi(mp_int *mp, ...)
+{
+ mp_err res = MP_OKAY; /* Assume ok until proven otherwise */
+ int n = 0; /* Number of ok inits */
+ mp_int* cur_arg = mp;
+ va_list args;
+
+ va_start(args, mp); /* init args to next argument from caller */
+ while (cur_arg != NULL) {
+ if (mp_init(cur_arg) != MP_OKAY) {
+ /* Oops - error! Back-track and mp_clear what we already
+ succeeded in init-ing, then return error.
+ */
+ va_list clean_args;
+
+ /* end the current list */
+ va_end(args);
+
+ /* now start cleaning up */
+ cur_arg = mp;
+ va_start(clean_args, mp);
+ while (n--) {
+ mp_clear(cur_arg);
+ cur_arg = va_arg(clean_args, mp_int*);
+ }
+ va_end(clean_args);
+ res = MP_MEM;
+ break;
+ }
+ n++;
+ cur_arg = va_arg(args, mp_int*);
+ }
+ va_end(args);
+ return res; /* Assumed ok, if error flagged above. */
+}
+#endif
+
+
+#ifdef BN_MP_CLEAR_MULTI_C
+static void mp_clear_multi(mp_int *mp, ...)
+{
+ mp_int* next_mp = mp;
+ va_list args;
+ va_start(args, mp);
+ while (next_mp != NULL) {
+ mp_clear(next_mp);
+ next_mp = va_arg(args, mp_int*);
+ }
+ va_end(args);
+}
+#endif
+
+
+/* shift left a certain amount of digits */
+static int mp_lshd (mp_int * a, int b)
+{
+ int x, res;
+
+ /* if its less than zero return */
+ if (b <= 0) {
+ return MP_OKAY;
+ }
+
+ /* grow to fit the new digits */
+ if (a->alloc < a->used + b) {
+ if ((res = mp_grow (a, a->used + b)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ {
+ register mp_digit *top, *bottom;
+
+ /* increment the used by the shift amount then copy upwards */
+ a->used += b;
+
+ /* top */
+ top = a->dp + a->used - 1;
+
+ /* base */
+ bottom = a->dp + a->used - 1 - b;
+
+ /* much like mp_rshd this is implemented using a sliding window
+ * except the window goes the otherway around. Copying from
+ * the bottom to the top. see bn_mp_rshd.c for more info.
+ */
+ for (x = a->used - 1; x >= b; x--) {
+ *top-- = *bottom--;
+ }
+
+ /* zero the lower digits */
+ top = a->dp;
+ for (x = 0; x < b; x++) {
+ *top++ = 0;
+ }
+ }
+ return MP_OKAY;
+}
+
+
+/* returns the number of bits in an int */
+static int mp_count_bits (mp_int * a)
+{
+ int r;
+ mp_digit q;
+
+ /* shortcut */
+ if (a->used == 0) {
+ return 0;
+ }
+
+ /* get number of digits and add that */
+ r = (a->used - 1) * DIGIT_BIT;
+
+ /* take the last digit and count the bits in it */
+ q = a->dp[a->used - 1];
+ while (q > ((mp_digit) 0)) {
+ ++r;
+ q >>= ((mp_digit) 1);
+ }
+ return r;
+}
+
+
+/* calc a value mod 2**b */
+static int mp_mod_2d (mp_int * a, int b, mp_int * c)
+{
+ int x, res;
+
+ /* if b is <= 0 then zero the int */
+ if (b <= 0) {
+ mp_zero (c);
+ return MP_OKAY;
+ }
+
+ /* if the modulus is larger than the value than return */
+ if (b >= (int) (a->used * DIGIT_BIT)) {
+ res = mp_copy (a, c);
+ return res;
+ }
+
+ /* copy */
+ if ((res = mp_copy (a, c)) != MP_OKAY) {
+ return res;
+ }
+
+ /* zero digits above the last digit of the modulus */
+ for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) {
+ c->dp[x] = 0;
+ }
+ /* clear the digit that is not completely outside/inside the modulus */
+ c->dp[b / DIGIT_BIT] &=
+ (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1));
+ mp_clamp (c);
+ return MP_OKAY;
+}
+
+
+#ifdef BN_MP_DIV_SMALL
+
+/* slower bit-bang division... also smaller */
+static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d)
+{
+ mp_int ta, tb, tq, q;
+ int res, n, n2;
+
+ /* is divisor zero ? */
+ if (mp_iszero (b) == 1) {
+ return MP_VAL;
+ }
+
+ /* if a < b then q=0, r = a */
+ if (mp_cmp_mag (a, b) == MP_LT) {
+ if (d != NULL) {
+ res = mp_copy (a, d);
+ } else {
+ res = MP_OKAY;
+ }
+ if (c != NULL) {
+ mp_zero (c);
+ }
+ return res;
+ }
+
+ /* init our temps */
+ if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) {
+ return res;
+ }
+
+
+ mp_set(&tq, 1);
+ n = mp_count_bits(a) - mp_count_bits(b);
+ if (((res = mp_abs(a, &ta)) != MP_OKAY) ||
+ ((res = mp_abs(b, &tb)) != MP_OKAY) ||
+ ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) ||
+ ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) {
+ goto LBL_ERR;
+ }
+
+ while (n-- >= 0) {
+ if (mp_cmp(&tb, &ta) != MP_GT) {
+ if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) ||
+ ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) {
+ goto LBL_ERR;
+ }
+ }
+ if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) ||
+ ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) {
+ goto LBL_ERR;
+ }
+ }
+
+ /* now q == quotient and ta == remainder */
+ n = a->sign;
+ n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG);
+ if (c != NULL) {
+ mp_exch(c, &q);
+ c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2;
+ }
+ if (d != NULL) {
+ mp_exch(d, &ta);
+ d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n;
+ }
+LBL_ERR:
+ mp_clear_multi(&ta, &tb, &tq, &q, NULL);
+ return res;
+}
+
+#else
+
+/* integer signed division.
+ * c*b + d == a [e.g. a/b, c=quotient, d=remainder]
+ * HAC pp.598 Algorithm 14.20
+ *
+ * Note that the description in HAC is horribly
+ * incomplete. For example, it doesn't consider
+ * the case where digits are removed from 'x' in
+ * the inner loop. It also doesn't consider the
+ * case that y has fewer than three digits, etc..
+ *
+ * The overall algorithm is as described as
+ * 14.20 from HAC but fixed to treat these cases.
+*/
+static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
+{
+ mp_int q, x, y, t1, t2;
+ int res, n, t, i, norm, neg;
+
+ /* is divisor zero ? */
+ if (mp_iszero (b) == 1) {
+ return MP_VAL;
+ }
+
+ /* if a < b then q=0, r = a */
+ if (mp_cmp_mag (a, b) == MP_LT) {
+ if (d != NULL) {
+ res = mp_copy (a, d);
+ } else {
+ res = MP_OKAY;
+ }
+ if (c != NULL) {
+ mp_zero (c);
+ }
+ return res;
+ }
+
+ if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) {
+ return res;
+ }
+ q.used = a->used + 2;
+
+ if ((res = mp_init (&t1)) != MP_OKAY) {
+ goto LBL_Q;
+ }
+
+ if ((res = mp_init (&t2)) != MP_OKAY) {
+ goto LBL_T1;
+ }
+
+ if ((res = mp_init_copy (&x, a)) != MP_OKAY) {
+ goto LBL_T2;
+ }
+
+ if ((res = mp_init_copy (&y, b)) != MP_OKAY) {
+ goto LBL_X;
+ }
+
+ /* fix the sign */
+ neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
+ x.sign = y.sign = MP_ZPOS;
+
+ /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */
+ norm = mp_count_bits(&y) % DIGIT_BIT;
+ if (norm < (int)(DIGIT_BIT-1)) {
+ norm = (DIGIT_BIT-1) - norm;
+ if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+ if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+ } else {
+ norm = 0;
+ }
+
+ /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */
+ n = x.used - 1;
+ t = y.used - 1;
+
+ /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */
+ if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */
+ goto LBL_Y;
+ }
+
+ while (mp_cmp (&x, &y) != MP_LT) {
+ ++(q.dp[n - t]);
+ if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+ }
+
+ /* reset y by shifting it back down */
+ mp_rshd (&y, n - t);
+
+ /* step 3. for i from n down to (t + 1) */
+ for (i = n; i >= (t + 1); i--) {
+ if (i > x.used) {
+ continue;
+ }
+
+ /* step 3.1 if xi == yt then set q{i-t-1} to b-1,
+ * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */
+ if (x.dp[i] == y.dp[t]) {
+ q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1);
+ } else {
+ mp_word tmp;
+ tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT);
+ tmp |= ((mp_word) x.dp[i - 1]);
+ tmp /= ((mp_word) y.dp[t]);
+ if (tmp > (mp_word) MP_MASK)
+ tmp = MP_MASK;
+ q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK));
+ }
+
+ /* while (q{i-t-1} * (yt * b + y{t-1})) >
+ xi * b**2 + xi-1 * b + xi-2
+
+ do q{i-t-1} -= 1;
+ */
+ q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK;
+ do {
+ q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK;
+
+ /* find left hand */
+ mp_zero (&t1);
+ t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1];
+ t1.dp[1] = y.dp[t];
+ t1.used = 2;
+ if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+
+ /* find right hand */
+ t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2];
+ t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1];
+ t2.dp[2] = x.dp[i];
+ t2.used = 3;
+ } while (mp_cmp_mag(&t1, &t2) == MP_GT);
+
+ /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */
+ if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+
+ if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+
+ if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+
+ /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */
+ if (x.sign == MP_NEG) {
+ if ((res = mp_copy (&y, &t1)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+ if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+ if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) {
+ goto LBL_Y;
+ }
+
+ q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK;
+ }
+ }
+
+ /* now q is the quotient and x is the remainder
+ * [which we have to normalize]
+ */
+
+ /* get sign before writing to c */
+ x.sign = x.used == 0 ? MP_ZPOS : a->sign;
+
+ if (c != NULL) {
+ mp_clamp (&q);
+ mp_exch (&q, c);
+ c->sign = neg;
+ }
+
+ if (d != NULL) {
+ mp_div_2d (&x, norm, &x, NULL);
+ mp_exch (&x, d);
+ }
+
+ res = MP_OKAY;
+
+LBL_Y:mp_clear (&y);
+LBL_X:mp_clear (&x);
+LBL_T2:mp_clear (&t2);
+LBL_T1:mp_clear (&t1);
+LBL_Q:mp_clear (&q);
+ return res;
+}
+
+#endif
+
+
+#ifdef MP_LOW_MEM
+ #define TAB_SIZE 32
+#else
+ #define TAB_SIZE 256
+#endif
+
+static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
+{
+ mp_int M[TAB_SIZE], res, mu;
+ mp_digit buf;
+ int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
+ int (*redux)(mp_int*,mp_int*,mp_int*);
+
+ /* find window size */
+ x = mp_count_bits (X);
+ if (x <= 7) {
+ winsize = 2;
+ } else if (x <= 36) {
+ winsize = 3;
+ } else if (x <= 140) {
+ winsize = 4;
+ } else if (x <= 450) {
+ winsize = 5;
+ } else if (x <= 1303) {
+ winsize = 6;
+ } else if (x <= 3529) {
+ winsize = 7;
+ } else {
+ winsize = 8;
+ }
+
+#ifdef MP_LOW_MEM
+ if (winsize > 5) {
+ winsize = 5;
+ }
+#endif
+
+ /* init M array */
+ /* init first cell */
+ if ((err = mp_init(&M[1])) != MP_OKAY) {
+ return err;
+ }
+
+ /* now init the second half of the array */
+ for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+ if ((err = mp_init(&M[x])) != MP_OKAY) {
+ for (y = 1<<(winsize-1); y < x; y++) {
+ mp_clear (&M[y]);
+ }
+ mp_clear(&M[1]);
+ return err;
+ }
+ }
+
+ /* create mu, used for Barrett reduction */
+ if ((err = mp_init (&mu)) != MP_OKAY) {
+ goto LBL_M;
+ }
+
+ if (redmode == 0) {
+ if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) {
+ goto LBL_MU;
+ }
+ redux = mp_reduce;
+ } else {
+ if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) {
+ goto LBL_MU;
+ }
+ redux = mp_reduce_2k_l;
+ }
+
+ /* create M table
+ *
+ * The M table contains powers of the base,
+ * e.g. M[x] = G**x mod P
+ *
+ * The first half of the table is not
+ * computed though accept for M[0] and M[1]
+ */
+ if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) {
+ goto LBL_MU;
+ }
+
+ /* compute the value at M[1<<(winsize-1)] by squaring
+ * M[1] (winsize-1) times
+ */
+ if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
+ goto LBL_MU;
+ }
+
+ for (x = 0; x < (winsize - 1); x++) {
+ /* square it */
+ if ((err = mp_sqr (&M[1 << (winsize - 1)],
+ &M[1 << (winsize - 1)])) != MP_OKAY) {
+ goto LBL_MU;
+ }
+
+ /* reduce modulo P */
+ if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) {
+ goto LBL_MU;
+ }
+ }
+
+ /* create upper table, that is M[x] = M[x-1] * M[1] (mod P)
+ * for x = (2**(winsize - 1) + 1) to (2**winsize - 1)
+ */
+ for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
+ if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
+ goto LBL_MU;
+ }
+ if ((err = redux (&M[x], P, &mu)) != MP_OKAY) {
+ goto LBL_MU;
+ }
+ }
+
+ /* setup result */
+ if ((err = mp_init (&res)) != MP_OKAY) {
+ goto LBL_MU;
+ }
+ mp_set (&res, 1);
+
+ /* set initial mode and bit cnt */
+ mode = 0;
+ bitcnt = 1;
+ buf = 0;
+ digidx = X->used - 1;
+ bitcpy = 0;
+ bitbuf = 0;
+
+ for (;;) {
+ /* grab next digit as required */
+ if (--bitcnt == 0) {
+ /* if digidx == -1 we are out of digits */
+ if (digidx == -1) {
+ break;
+ }
+ /* read next digit and reset the bitcnt */
+ buf = X->dp[digidx--];
+ bitcnt = (int) DIGIT_BIT;
+ }
+
+ /* grab the next msb from the exponent */
+ y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1;
+ buf <<= (mp_digit)1;
+
+ /* if the bit is zero and mode == 0 then we ignore it
+ * These represent the leading zero bits before the first 1 bit
+ * in the exponent. Technically this opt is not required but it
+ * does lower the # of trivial squaring/reductions used
+ */
+ if (mode == 0 && y == 0) {
+ continue;
+ }
+
+ /* if the bit is zero and mode == 1 then we square */
+ if (mode == 1 && y == 0) {
+ if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ continue;
+ }
+
+ /* else we add it to the window */
+ bitbuf |= (y << (winsize - ++bitcpy));
+ mode = 2;
+
+ if (bitcpy == winsize) {
+ /* ok window is filled so square as required and multiply */
+ /* square first */
+ for (x = 0; x < winsize; x++) {
+ if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+
+ /* then multiply */
+ if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+
+ /* empty window and reset */
+ bitcpy = 0;
+ bitbuf = 0;
+ mode = 1;
+ }
+ }
+
+ /* if bits remain then square/multiply */
+ if (mode == 2 && bitcpy > 0) {
+ /* square then multiply if the bit is set */
+ for (x = 0; x < bitcpy; x++) {
+ if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+
+ bitbuf <<= 1;
+ if ((bitbuf & (1 << winsize)) != 0) {
+ /* then multiply */
+ if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+ }
+ }
+
+ mp_exch (&res, Y);
+ err = MP_OKAY;
+LBL_RES:mp_clear (&res);
+LBL_MU:mp_clear (&mu);
+LBL_M:
+ mp_clear(&M[1]);
+ for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+ mp_clear (&M[x]);
+ }
+ return err;
+}
+
+
+/* computes b = a*a */
+static int mp_sqr (mp_int * a, mp_int * b)
+{
+ int res;
+
+#ifdef BN_MP_TOOM_SQR_C
+ /* use Toom-Cook? */
+ if (a->used >= TOOM_SQR_CUTOFF) {
+ res = mp_toom_sqr(a, b);
+ /* Karatsuba? */
+ } else
+#endif
+#ifdef BN_MP_KARATSUBA_SQR_C
+if (a->used >= KARATSUBA_SQR_CUTOFF) {
+ res = mp_karatsuba_sqr (a, b);
+ } else
+#endif
+ {
+#ifdef BN_FAST_S_MP_SQR_C
+ /* can we use the fast comba multiplier? */
+ if ((a->used * 2 + 1) < MP_WARRAY &&
+ a->used <
+ (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) {
+ res = fast_s_mp_sqr (a, b);
+ } else
+#endif
+#ifdef BN_S_MP_SQR_C
+ res = s_mp_sqr (a, b);
+#else
+#error mp_sqr could fail
+ res = MP_VAL;
+#endif
+ }
+ b->sign = MP_ZPOS;
+ return res;
+}
+
+
+/* reduces a modulo n where n is of the form 2**p - d
+ This differs from reduce_2k since "d" can be larger
+ than a single digit.
+*/
+static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d)
+{
+ mp_int q;
+ int p, res;
+
+ if ((res = mp_init(&q)) != MP_OKAY) {
+ return res;
+ }
+
+ p = mp_count_bits(n);
+top:
+ /* q = a/2**p, a = a mod 2**p */
+ if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* q = q * d */
+ if ((res = mp_mul(&q, d, &q)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ /* a = a + q */
+ if ((res = s_mp_add(a, &q, a)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if (mp_cmp_mag(a, n) != MP_LT) {
+ s_mp_sub(a, n, a);
+ goto top;
+ }
+
+ERR:
+ mp_clear(&q);
+ return res;
+}
+
+
+/* determines the setup value */
+static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d)
+{
+ int res;
+ mp_int tmp;
+
+ if ((res = mp_init(&tmp)) != MP_OKAY) {
+ return res;
+ }
+
+ if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) {
+ goto ERR;
+ }
+
+ if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) {
+ goto ERR;
+ }
+
+ERR:
+ mp_clear(&tmp);
+ return res;
+}
+
+
+/* computes a = 2**b
+ *
+ * Simple algorithm which zeroes the int, grows it then just sets one bit
+ * as required.
+ */
+static int mp_2expt (mp_int * a, int b)
+{
+ int res;
+
+ /* zero a as per default */
+ mp_zero (a);
+
+ /* grow a to accomodate the single bit */
+ if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) {
+ return res;
+ }
+
+ /* set the used count of where the bit will go */
+ a->used = b / DIGIT_BIT + 1;
+
+ /* put the single bit in its place */
+ a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT);
+
+ return MP_OKAY;
+}
+
+
+/* pre-calculate the value required for Barrett reduction
+ * For a given modulus "b" it calulates the value required in "a"
+ */
+static int mp_reduce_setup (mp_int * a, mp_int * b)
+{
+ int res;
+
+ if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) {
+ return res;
+ }
+ return mp_div (a, b, a, NULL);
+}
+
+
+/* reduces x mod m, assumes 0 < x < m**2, mu is
+ * precomputed via mp_reduce_setup.
+ * From HAC pp.604 Algorithm 14.42
+ */
+static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu)
+{
+ mp_int q;
+ int res, um = m->used;
+
+ /* q = x */
+ if ((res = mp_init_copy (&q, x)) != MP_OKAY) {
+ return res;
+ }
+
+ /* q1 = x / b**(k-1) */
+ mp_rshd (&q, um - 1);
+
+ /* according to HAC this optimization is ok */
+ if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) {
+ if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+ } else {
+#ifdef BN_S_MP_MUL_HIGH_DIGS_C
+ if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C)
+ if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+#else
+ {
+#error mp_reduce would always fail
+ res = MP_VAL;
+ goto CLEANUP;
+ }
+#endif
+ }
+
+ /* q3 = q2 / b**(k+1) */
+ mp_rshd (&q, um + 1);
+
+ /* x = x mod b**(k+1), quick (no division) */
+ if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+
+ /* q = q * m mod b**(k+1), quick (no division) */
+ if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+
+ /* x = x - q */
+ if ((res = mp_sub (x, &q, x)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+
+ /* If x < 0, add b**(k+1) to it */
+ if (mp_cmp_d (x, 0) == MP_LT) {
+ mp_set (&q, 1);
+ if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+ if ((res = mp_add (x, &q, x)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+ }
+
+ /* Back off if it's too big */
+ while (mp_cmp (x, m) != MP_LT) {
+ if ((res = s_mp_sub (x, m, x)) != MP_OKAY) {
+ goto CLEANUP;
+ }
+ }
+
+CLEANUP:
+ mp_clear (&q);
+
+ return res;
+}
+
+
+/* multiplies |a| * |b| and only computes upto digs digits of result
+ * HAC pp. 595, Algorithm 14.12 Modified so you can control how
+ * many digits of output are created.
+ */
+static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
+{
+ mp_int t;
+ int res, pa, pb, ix, iy;
+ mp_digit u;
+ mp_word r;
+ mp_digit tmpx, *tmpt, *tmpy;
+
+ /* can we use the fast multiplier? */
+ if (((digs) < MP_WARRAY) &&
+ MIN (a->used, b->used) <
+ (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+ return fast_s_mp_mul_digs (a, b, c, digs);
+ }
+
+ if ((res = mp_init_size (&t, digs)) != MP_OKAY) {
+ return res;
+ }
+ t.used = digs;
+
+ /* compute the digits of the product directly */
+ pa = a->used;
+ for (ix = 0; ix < pa; ix++) {
+ /* set the carry to zero */
+ u = 0;
+
+ /* limit ourselves to making digs digits of output */
+ pb = MIN (b->used, digs - ix);
+
+ /* setup some aliases */
+ /* copy of the digit from a used within the nested loop */
+ tmpx = a->dp[ix];
+
+ /* an alias for the destination shifted ix places */
+ tmpt = t.dp + ix;
+
+ /* an alias for the digits of b */
+ tmpy = b->dp;
+
+ /* compute the columns of the output and propagate the carry */
+ for (iy = 0; iy < pb; iy++) {
+ /* compute the column as a mp_word */
+ r = ((mp_word)*tmpt) +
+ ((mp_word)tmpx) * ((mp_word)*tmpy++) +
+ ((mp_word) u);
+
+ /* the new column is the lower part of the result */
+ *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+ /* get the carry word from the result */
+ u = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
+ }
+ /* set carry if it is placed below digs */
+ if (ix + iy < digs) {
+ *tmpt = u;
+ }
+ }
+
+ mp_clamp (&t);
+ mp_exch (&t, c);
+
+ mp_clear (&t);
+ return MP_OKAY;
+}
+
+
+/* Fast (comba) multiplier
+ *
+ * This is the fast column-array [comba] multiplier. It is
+ * designed to compute the columns of the product first
+ * then handle the carries afterwards. This has the effect
+ * of making the nested loops that compute the columns very
+ * simple and schedulable on super-scalar processors.
+ *
+ * This has been modified to produce a variable number of
+ * digits of output so if say only a half-product is required
+ * you don't have to compute the upper half (a feature
+ * required for fast Barrett reduction).
+ *
+ * Based on Algorithm 14.12 on pp.595 of HAC.
+ *
+ */
+static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
+{
+ int olduse, res, pa, ix, iz;
+ mp_digit W[MP_WARRAY];
+ register mp_word _W;
+
+ /* grow the destination as required */
+ if (c->alloc < digs) {
+ if ((res = mp_grow (c, digs)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* number of output digits to produce */
+ pa = MIN(digs, a->used + b->used);
+
+ /* clear the carry */
+ _W = 0;
+ for (ix = 0; ix < pa; ix++) {
+ int tx, ty;
+ int iy;
+ mp_digit *tmpx, *tmpy;
+
+ /* get offsets into the two bignums */
+ ty = MIN(b->used-1, ix);
+ tx = ix - ty;
+
+ /* setup temp aliases */
+ tmpx = a->dp + tx;
+ tmpy = b->dp + ty;
+
+ /* this is the number of times the loop will iterrate, essentially
+ while (tx++ < a->used && ty-- >= 0) { ... }
+ */
+ iy = MIN(a->used-tx, ty+1);
+
+ /* execute loop */
+ for (iz = 0; iz < iy; ++iz) {
+ _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
+
+ }
+
+ /* store term */
+ W[ix] = ((mp_digit)_W) & MP_MASK;
+
+ /* make next carry */
+ _W = _W >> ((mp_word)DIGIT_BIT);
+ }
+
+ /* setup dest */
+ olduse = c->used;
+ c->used = pa;
+
+ {
+ register mp_digit *tmpc;
+ tmpc = c->dp;
+ for (ix = 0; ix < pa+1; ix++) {
+ /* now extract the previous digit [below the carry] */
+ *tmpc++ = W[ix];
+ }
+
+ /* clear unused digits [that existed in the old copy of c] */
+ for (; ix < olduse; ix++) {
+ *tmpc++ = 0;
+ }
+ }
+ mp_clamp (c);
+ return MP_OKAY;
+}
+
+
+/* init an mp_init for a given size */
+static int mp_init_size (mp_int * a, int size)
+{
+ int x;
+
+ /* pad size so there are always extra digits */
+ size += (MP_PREC * 2) - (size % MP_PREC);
+
+ /* alloc mem */
+ a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size);
+ if (a->dp == NULL) {
+ return MP_MEM;
+ }
+
+ /* set the members */
+ a->used = 0;
+ a->alloc = size;
+ a->sign = MP_ZPOS;
+
+ /* zero the digits */
+ for (x = 0; x < size; x++) {
+ a->dp[x] = 0;
+ }
+
+ return MP_OKAY;
+}
+
+
+/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */
+static int s_mp_sqr (mp_int * a, mp_int * b)
+{
+ mp_int t;
+ int res, ix, iy, pa;
+ mp_word r;
+ mp_digit u, tmpx, *tmpt;
+
+ pa = a->used;
+ if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) {
+ return res;
+ }
+
+ /* default used is maximum possible size */
+ t.used = 2*pa + 1;
+
+ for (ix = 0; ix < pa; ix++) {
+ /* first calculate the digit at 2*ix */
+ /* calculate double precision result */
+ r = ((mp_word) t.dp[2*ix]) +
+ ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]);
+
+ /* store lower part in result */
+ t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK));
+
+ /* get the carry */
+ u = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
+
+ /* left hand side of A[ix] * A[iy] */
+ tmpx = a->dp[ix];
+
+ /* alias for where to store the results */
+ tmpt = t.dp + (2*ix + 1);
+
+ for (iy = ix + 1; iy < pa; iy++) {
+ /* first calculate the product */
+ r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]);
+
+ /* now calculate the double precision result, note we use
+ * addition instead of *2 since it's easier to optimize
+ */
+ r = ((mp_word) *tmpt) + r + r + ((mp_word) u);
+
+ /* store lower part */
+ *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+ /* get carry */
+ u = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
+ }
+ /* propagate upwards */
+ while (u != ((mp_digit) 0)) {
+ r = ((mp_word) *tmpt) + ((mp_word) u);
+ *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+ u = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
+ }
+ }
+
+ mp_clamp (&t);
+ mp_exch (&t, b);
+ mp_clear (&t);
+ return MP_OKAY;
+}
+
+
+/* multiplies |a| * |b| and does not compute the lower digs digits
+ * [meant to get the higher part of the product]
+ */
+static int s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
+{
+ mp_int t;
+ int res, pa, pb, ix, iy;
+ mp_digit u;
+ mp_word r;
+ mp_digit tmpx, *tmpt, *tmpy;
+
+ /* can we use the fast multiplier? */
+#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C
+ if (((a->used + b->used + 1) < MP_WARRAY)
+ && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+ return fast_s_mp_mul_high_digs (a, b, c, digs);
+ }
+#endif
+
+ if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) {
+ return res;
+ }
+ t.used = a->used + b->used + 1;
+
+ pa = a->used;
+ pb = b->used;
+ for (ix = 0; ix < pa; ix++) {
+ /* clear the carry */
+ u = 0;
+
+ /* left hand side of A[ix] * B[iy] */
+ tmpx = a->dp[ix];
+
+ /* alias to the address of where the digits will be stored */
+ tmpt = &(t.dp[digs]);
+
+ /* alias for where to read the right hand side from */
+ tmpy = b->dp + (digs - ix);
+
+ for (iy = digs - ix; iy < pb; iy++) {
+ /* calculate the double precision result */
+ r = ((mp_word)*tmpt) +
+ ((mp_word)tmpx) * ((mp_word)*tmpy++) +
+ ((mp_word) u);
+
+ /* get the lower part */
+ *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+ /* carry the carry */
+ u = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
+ }
+ *tmpt = u;
+ }
+ mp_clamp (&t);
+ mp_exch (&t, c);
+ mp_clear (&t);
+ return MP_OKAY;
+}
+
+
+#ifdef BN_MP_MONTGOMERY_SETUP_C
+/* setups the montgomery reduction stuff */
+static int
+mp_montgomery_setup (mp_int * n, mp_digit * rho)
+{
+ mp_digit x, b;
+
+/* fast inversion mod 2**k
+ *
+ * Based on the fact that
+ *
+ * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n)
+ * => 2*X*A - X*X*A*A = 1
+ * => 2*(1) - (1) = 1
+ */
+ b = n->dp[0];
+
+ if ((b & 1) == 0) {
+ return MP_VAL;
+ }
+
+ x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */
+ x *= 2 - b * x; /* here x*a==1 mod 2**8 */
+#if !defined(MP_8BIT)
+ x *= 2 - b * x; /* here x*a==1 mod 2**16 */
+#endif
+#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT))
+ x *= 2 - b * x; /* here x*a==1 mod 2**32 */
+#endif
+#ifdef MP_64BIT
+ x *= 2 - b * x; /* here x*a==1 mod 2**64 */
+#endif
+
+ /* rho = -1/m mod b */
+ *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK;
+
+ return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
+/* computes xR**-1 == x (mod N) via Montgomery Reduction
+ *
+ * This is an optimized implementation of montgomery_reduce
+ * which uses the comba method to quickly calculate the columns of the
+ * reduction.
+ *
+ * Based on Algorithm 14.32 on pp.601 of HAC.
+*/
+int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
+{
+ int ix, res, olduse;
+ mp_word W[MP_WARRAY];
+
+ /* get old used count */
+ olduse = x->used;
+
+ /* grow a as required */
+ if (x->alloc < n->used + 1) {
+ if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* first we have to get the digits of the input into
+ * an array of double precision words W[...]
+ */
+ {
+ register mp_word *_W;
+ register mp_digit *tmpx;
+
+ /* alias for the W[] array */
+ _W = W;
+
+ /* alias for the digits of x*/
+ tmpx = x->dp;
+
+ /* copy the digits of a into W[0..a->used-1] */
+ for (ix = 0; ix < x->used; ix++) {
+ *_W++ = *tmpx++;
+ }
+
+ /* zero the high words of W[a->used..m->used*2] */
+ for (; ix < n->used * 2 + 1; ix++) {
+ *_W++ = 0;
+ }
+ }
+
+ /* now we proceed to zero successive digits
+ * from the least significant upwards
+ */
+ for (ix = 0; ix < n->used; ix++) {
+ /* mu = ai * m' mod b
+ *
+ * We avoid a double precision multiplication (which isn't required)
+ * by casting the value down to a mp_digit. Note this requires
+ * that W[ix-1] have the carry cleared (see after the inner loop)
+ */
+ register mp_digit mu;
+ mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK);
+
+ /* a = a + mu * m * b**i
+ *
+ * This is computed in place and on the fly. The multiplication
+ * by b**i is handled by offseting which columns the results
+ * are added to.
+ *
+ * Note the comba method normally doesn't handle carries in the
+ * inner loop In this case we fix the carry from the previous
+ * column since the Montgomery reduction requires digits of the
+ * result (so far) [see above] to work. This is
+ * handled by fixing up one carry after the inner loop. The
+ * carry fixups are done in order so after these loops the
+ * first m->used words of W[] have the carries fixed
+ */
+ {
+ register int iy;
+ register mp_digit *tmpn;
+ register mp_word *_W;
+
+ /* alias for the digits of the modulus */
+ tmpn = n->dp;
+
+ /* Alias for the columns set by an offset of ix */
+ _W = W + ix;
+
+ /* inner loop */
+ for (iy = 0; iy < n->used; iy++) {
+ *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++);
+ }
+ }
+
+ /* now fix carry for next digit, W[ix+1] */
+ W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT);
+ }
+
+ /* now we have to propagate the carries and
+ * shift the words downward [all those least
+ * significant digits we zeroed].
+ */
+ {
+ register mp_digit *tmpx;
+ register mp_word *_W, *_W1;
+
+ /* nox fix rest of carries */
+
+ /* alias for current word */
+ _W1 = W + ix;
+
+ /* alias for next word, where the carry goes */
+ _W = W + ++ix;
+
+ for (; ix <= n->used * 2 + 1; ix++) {
+ *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT);
+ }
+
+ /* copy out, A = A/b**n
+ *
+ * The result is A/b**n but instead of converting from an
+ * array of mp_word to mp_digit than calling mp_rshd
+ * we just copy them in the right order
+ */
+
+ /* alias for destination word */
+ tmpx = x->dp;
+
+ /* alias for shifted double precision result */
+ _W = W + n->used;
+
+ for (ix = 0; ix < n->used + 1; ix++) {
+ *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK));
+ }
+
+ /* zero oldused digits, if the input a was larger than
+ * m->used+1 we'll have to clear the digits
+ */
+ for (; ix < olduse; ix++) {
+ *tmpx++ = 0;
+ }
+ }
+
+ /* set the max used and clamp */
+ x->used = n->used + 1;
+ mp_clamp (x);
+
+ /* if A >= m then A = A - m */
+ if (mp_cmp_mag (x, n) != MP_LT) {
+ return s_mp_sub (x, n, x);
+ }
+ return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_MUL_2_C
+/* b = a*2 */
+static int mp_mul_2(mp_int * a, mp_int * b)
+{
+ int x, res, oldused;
+
+ /* grow to accomodate result */
+ if (b->alloc < a->used + 1) {
+ if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ oldused = b->used;
+ b->used = a->used;
+
+ {
+ register mp_digit r, rr, *tmpa, *tmpb;
+
+ /* alias for source */
+ tmpa = a->dp;
+
+ /* alias for dest */
+ tmpb = b->dp;
+
+ /* carry */
+ r = 0;
+ for (x = 0; x < a->used; x++) {
+
+ /* get what will be the *next* carry bit from the
+ * MSB of the current digit
+ */
+ rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1));
+
+ /* now shift up this digit, add in the carry [from the previous] */
+ *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK;
+
+ /* copy the carry that would be from the source
+ * digit into the next iteration
+ */
+ r = rr;
+ }
+
+ /* new leading digit? */
+ if (r != 0) {
+ /* add a MSB which is always 1 at this point */
+ *tmpb = 1;
+ ++(b->used);
+ }
+
+ /* now zero any excess digits on the destination
+ * that we didn't write to
+ */
+ tmpb = b->dp + b->used;
+ for (x = b->used; x < oldused; x++) {
+ *tmpb++ = 0;
+ }
+ }
+ b->sign = a->sign;
+ return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+/*
+ * shifts with subtractions when the result is greater than b.
+ *
+ * The method is slightly modified to shift B unconditionally upto just under
+ * the leading bit of b. This saves alot of multiple precision shifting.
+ */
+static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
+{
+ int x, bits, res;
+
+ /* how many bits of last digit does b use */
+ bits = mp_count_bits (b) % DIGIT_BIT;
+
+ if (b->used > 1) {
+ if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) {
+ return res;
+ }
+ } else {
+ mp_set(a, 1);
+ bits = 1;
+ }
+
+
+ /* now compute C = A * B mod b */
+ for (x = bits - 1; x < (int)DIGIT_BIT; x++) {
+ if ((res = mp_mul_2 (a, a)) != MP_OKAY) {
+ return res;
+ }
+ if (mp_cmp_mag (a, b) != MP_LT) {
+ if ((res = s_mp_sub (a, b, a)) != MP_OKAY) {
+ return res;
+ }
+ }
+ }
+
+ return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_EXPTMOD_FAST_C
+/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85
+ *
+ * Uses a left-to-right k-ary sliding window to compute the modular exponentiation.
+ * The value of k changes based on the size of the exponent.
+ *
+ * Uses Montgomery or Diminished Radix reduction [whichever appropriate]
+ */
+
+static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
+{
+ mp_int M[TAB_SIZE], res;
+ mp_digit buf, mp;
+ int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
+
+ /* use a pointer to the reduction algorithm. This allows us to use
+ * one of many reduction algorithms without modding the guts of
+ * the code with if statements everywhere.
+ */
+ int (*redux)(mp_int*,mp_int*,mp_digit);
+
+ /* find window size */
+ x = mp_count_bits (X);
+ if (x <= 7) {
+ winsize = 2;
+ } else if (x <= 36) {
+ winsize = 3;
+ } else if (x <= 140) {
+ winsize = 4;
+ } else if (x <= 450) {
+ winsize = 5;
+ } else if (x <= 1303) {
+ winsize = 6;
+ } else if (x <= 3529) {
+ winsize = 7;
+ } else {
+ winsize = 8;
+ }
+
+#ifdef MP_LOW_MEM
+ if (winsize > 5) {
+ winsize = 5;
+ }
+#endif
+
+ /* init M array */
+ /* init first cell */
+ if ((err = mp_init(&M[1])) != MP_OKAY) {
+ return err;
+ }
+
+ /* now init the second half of the array */
+ for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+ if ((err = mp_init(&M[x])) != MP_OKAY) {
+ for (y = 1<<(winsize-1); y < x; y++) {
+ mp_clear (&M[y]);
+ }
+ mp_clear(&M[1]);
+ return err;
+ }
+ }
+
+ /* determine and setup reduction code */
+ if (redmode == 0) {
+#ifdef BN_MP_MONTGOMERY_SETUP_C
+ /* now setup montgomery */
+ if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) {
+ goto LBL_M;
+ }
+#else
+ err = MP_VAL;
+ goto LBL_M;
+#endif
+
+ /* automatically pick the comba one if available (saves quite a few calls/ifs) */
+#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
+ if (((P->used * 2 + 1) < MP_WARRAY) &&
+ P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+ redux = fast_mp_montgomery_reduce;
+ } else
+#endif
+ {
+#ifdef BN_MP_MONTGOMERY_REDUCE_C
+ /* use slower baseline Montgomery method */
+ redux = mp_montgomery_reduce;
+#else
+ err = MP_VAL;
+ goto LBL_M;
+#endif
+ }
+ } else if (redmode == 1) {
+#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C)
+ /* setup DR reduction for moduli of the form B**k - b */
+ mp_dr_setup(P, &mp);
+ redux = mp_dr_reduce;
+#else
+ err = MP_VAL;
+ goto LBL_M;
+#endif
+ } else {
+#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C)
+ /* setup DR reduction for moduli of the form 2**k - b */
+ if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) {
+ goto LBL_M;
+ }
+ redux = mp_reduce_2k;
+#else
+ err = MP_VAL;
+ goto LBL_M;
+#endif
+ }
+
+ /* setup result */
+ if ((err = mp_init (&res)) != MP_OKAY) {
+ goto LBL_M;
+ }
+
+ /* create M table
+ *
+
+ *
+ * The first half of the table is not computed though accept for M[0] and M[1]
+ */
+
+ if (redmode == 0) {
+#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+ /* now we need R mod m */
+ if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+#else
+ err = MP_VAL;
+ goto LBL_RES;
+#endif
+
+ /* now set M[1] to G * R mod m */
+ if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ } else {
+ mp_set(&res, 1);
+ if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+
+ /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */
+ if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
+ goto LBL_RES;
+ }
+
+ for (x = 0; x < (winsize - 1); x++) {
+ if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+
+ /* create upper table */
+ for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
+ if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux (&M[x], P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+
+ /* set initial mode and bit cnt */
+ mode = 0;
+ bitcnt = 1;
+ buf = 0;
+ digidx = X->used - 1;
+ bitcpy = 0;
+ bitbuf = 0;
+
+ for (;;) {
+ /* grab next digit as required */
+ if (--bitcnt == 0) {
+ /* if digidx == -1 we are out of digits so break */
+ if (digidx == -1) {
+ break;
+ }
+ /* read next digit and reset bitcnt */
+ buf = X->dp[digidx--];
+ bitcnt = (int)DIGIT_BIT;
+ }
+
+ /* grab the next msb from the exponent */
+ y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1;
+ buf <<= (mp_digit)1;
+
+ /* if the bit is zero and mode == 0 then we ignore it
+ * These represent the leading zero bits before the first 1 bit
+ * in the exponent. Technically this opt is not required but it
+ * does lower the # of trivial squaring/reductions used
+ */
+ if (mode == 0 && y == 0) {
+ continue;
+ }
+
+ /* if the bit is zero and mode == 1 then we square */
+ if (mode == 1 && y == 0) {
+ if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux (&res, P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ continue;
+ }
+
+ /* else we add it to the window */
+ bitbuf |= (y << (winsize - ++bitcpy));
+ mode = 2;
+
+ if (bitcpy == winsize) {
+ /* ok window is filled so square as required and multiply */
+ /* square first */
+ for (x = 0; x < winsize; x++) {
+ if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux (&res, P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+
+ /* then multiply */
+ if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux (&res, P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+
+ /* empty window and reset */
+ bitcpy = 0;
+ bitbuf = 0;
+ mode = 1;
+ }
+ }
+
+ /* if bits remain then square/multiply */
+ if (mode == 2 && bitcpy > 0) {
+ /* square then multiply if the bit is set */
+ for (x = 0; x < bitcpy; x++) {
+ if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux (&res, P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+
+ /* get next bit of the window */
+ bitbuf <<= 1;
+ if ((bitbuf & (1 << winsize)) != 0) {
+ /* then multiply */
+ if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ if ((err = redux (&res, P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+ }
+ }
+
+ if (redmode == 0) {
+ /* fixup result if Montgomery reduction is used
+ * recall that any value in a Montgomery system is
+ * actually multiplied by R mod n. So we have
+ * to reduce one more time to cancel out the factor
+ * of R.
+ */
+ if ((err = redux(&res, P, mp)) != MP_OKAY) {
+ goto LBL_RES;
+ }
+ }
+
+ /* swap res with Y */
+ mp_exch (&res, Y);
+ err = MP_OKAY;
+LBL_RES:mp_clear (&res);
+LBL_M:
+ mp_clear(&M[1]);
+ for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+ mp_clear (&M[x]);
+ }
+ return err;
+}
+#endif
+
+
+#ifdef BN_FAST_S_MP_SQR_C
+/* the jist of squaring...
+ * you do like mult except the offset of the tmpx [one that
+ * starts closer to zero] can't equal the offset of tmpy.
+ * So basically you set up iy like before then you min it with
+ * (ty-tx) so that it never happens. You double all those
+ * you add in the inner loop
+
+After that loop you do the squares and add them in.
+*/
+
+static int fast_s_mp_sqr (mp_int * a, mp_int * b)
+{
+ int olduse, res, pa, ix, iz;
+ mp_digit W[MP_WARRAY], *tmpx;
+ mp_word W1;
+
+ /* grow the destination as required */
+ pa = a->used + a->used;
+ if (b->alloc < pa) {
+ if ((res = mp_grow (b, pa)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* number of output digits to produce */
+ W1 = 0;
+ for (ix = 0; ix < pa; ix++) {
+ int tx, ty, iy;
+ mp_word _W;
+ mp_digit *tmpy;
+
+ /* clear counter */
+ _W = 0;
+
+ /* get offsets into the two bignums */
+ ty = MIN(a->used-1, ix);
+ tx = ix - ty;
+
+ /* setup temp aliases */
+ tmpx = a->dp + tx;
+ tmpy = a->dp + ty;
+
+ /* this is the number of times the loop will iterrate, essentially
+ while (tx++ < a->used && ty-- >= 0) { ... }
+ */
+ iy = MIN(a->used-tx, ty+1);
+
+ /* now for squaring tx can never equal ty
+ * we halve the distance since they approach at a rate of 2x
+ * and we have to round because odd cases need to be executed
+ */
+ iy = MIN(iy, (ty-tx+1)>>1);
+
+ /* execute loop */
+ for (iz = 0; iz < iy; iz++) {
+ _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
+ }
+
+ /* double the inner product and add carry */
+ _W = _W + _W + W1;
+
+ /* even columns have the square term in them */
+ if ((ix&1) == 0) {
+ _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]);
+ }
+
+ /* store it */
+ W[ix] = (mp_digit)(_W & MP_MASK);
+
+ /* make next carry */
+ W1 = _W >> ((mp_word)DIGIT_BIT);
+ }
+
+ /* setup dest */
+ olduse = b->used;
+ b->used = a->used+a->used;
+
+ {
+ mp_digit *tmpb;
+ tmpb = b->dp;
+ for (ix = 0; ix < pa; ix++) {
+ *tmpb++ = W[ix] & MP_MASK;
+ }
+
+ /* clear unused digits [that existed in the old copy of c] */
+ for (; ix < olduse; ix++) {
+ *tmpb++ = 0;
+ }
+ }
+ mp_clamp (b);
+ return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_MUL_D_C
+/* multiply by a digit */
+static int
+mp_mul_d (mp_int * a, mp_digit b, mp_int * c)
+{
+ mp_digit u, *tmpa, *tmpc;
+ mp_word r;
+ int ix, res, olduse;
+
+ /* make sure c is big enough to hold a*b */
+ if (c->alloc < a->used + 1) {
+ if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) {
+ return res;
+ }
+ }
+
+ /* get the original destinations used count */
+ olduse = c->used;
+
+ /* set the sign */
+ c->sign = a->sign;
+
+ /* alias for a->dp [source] */
+ tmpa = a->dp;
+
+ /* alias for c->dp [dest] */
+ tmpc = c->dp;
+
+ /* zero carry */
+ u = 0;
+
+ /* compute columns */
+ for (ix = 0; ix < a->used; ix++) {
+ /* compute product and carry sum for this term */
+ r = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b);
+
+ /* mask off higher bits to get a single digit */
+ *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+ /* send carry into next iteration */
+ u = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
+ }
+
+ /* store final carry [if any] and increment ix offset */
+ *tmpc++ = u;
+ ++ix;
+
+ /* now zero digits above the top */
+ while (ix++ < olduse) {
+ *tmpc++ = 0;
+ }
+
+ /* set used count */
+ c->used = a->used + 1;
+ mp_clamp(c);
+
+ return MP_OKAY;
+}
+#endif
diff --git a/src/tls/pkcs1.c b/src/tls/pkcs1.c
new file mode 100644
index 0000000..72ebd87
--- /dev/null
+++ b/src/tls/pkcs1.c
@@ -0,0 +1,201 @@
+/*
+ * PKCS #1 (RSA Encryption)
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "rsa.h"
+#include "pkcs1.h"
+
+
+static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ size_t ps_len;
+ u8 *pos;
+
+ /*
+ * PKCS #1 v1.5, 8.1:
+ *
+ * EB = 00 || BT || PS || 00 || D
+ * BT = 00 or 01 for private-key operation; 02 for public-key operation
+ * PS = k-3-||D||; at least eight octets
+ * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero)
+ * k = length of modulus in octets (modlen)
+ */
+
+ if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) {
+ wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer "
+ "lengths (modlen=%lu outlen=%lu inlen=%lu)",
+ __func__, (unsigned long) modlen,
+ (unsigned long) *outlen,
+ (unsigned long) inlen);
+ return -1;
+ }
+
+ pos = out;
+ *pos++ = 0x00;
+ *pos++ = block_type; /* BT */
+ ps_len = modlen - inlen - 3;
+ switch (block_type) {
+ case 0:
+ os_memset(pos, 0x00, ps_len);
+ pos += ps_len;
+ break;
+ case 1:
+ os_memset(pos, 0xff, ps_len);
+ pos += ps_len;
+ break;
+ case 2:
+ if (os_get_random(pos, ps_len) < 0) {
+ wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get "
+ "random data for PS", __func__);
+ return -1;
+ }
+ while (ps_len--) {
+ if (*pos == 0x00)
+ *pos = 0x01;
+ pos++;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type "
+ "%d", __func__, block_type);
+ return -1;
+ }
+ *pos++ = 0x00;
+ os_memcpy(pos, in, inlen); /* D */
+
+ return 0;
+}
+
+
+int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key,
+ int use_private, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ size_t modlen;
+
+ modlen = crypto_rsa_get_modulus_len(key);
+
+ if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen,
+ out, outlen) < 0)
+ return -1;
+
+ return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private);
+}
+
+
+int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen)
+{
+ int res;
+ u8 *pos, *end;
+
+ res = crypto_rsa_exptmod(in, inlen, out, outlen, key, 1);
+ if (res)
+ return res;
+
+ if (*outlen < 2 || out[0] != 0 || out[1] != 2)
+ return -1;
+
+ /* Skip PS (pseudorandom non-zero octets) */
+ pos = out + 2;
+ end = out + *outlen;
+ while (*pos && pos < end)
+ pos++;
+ if (pos == end)
+ return -1;
+ pos++;
+
+ *outlen -= pos - out;
+
+ /* Strip PKCS #1 header */
+ os_memmove(out, pos, *outlen);
+
+ return 0;
+}
+
+
+int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
+ const u8 *crypt, size_t crypt_len,
+ u8 *plain, size_t *plain_len)
+{
+ size_t len;
+ u8 *pos;
+
+ len = *plain_len;
+ if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, key, 0) < 0)
+ return -1;
+
+ /*
+ * PKCS #1 v1.5, 8.1:
+ *
+ * EB = 00 || BT || PS || 00 || D
+ * BT = 00 or 01
+ * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01)
+ * k = length of modulus in octets
+ */
+
+ if (len < 3 + 8 + 16 /* min hash len */ ||
+ plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) {
+ wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+ "structure");
+ return -1;
+ }
+
+ pos = plain + 3;
+ if (plain[1] == 0x00) {
+ /* BT = 00 */
+ if (plain[2] != 0x00) {
+ wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
+ "PS (BT=00)");
+ return -1;
+ }
+ while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00)
+ pos++;
+ } else {
+ /* BT = 01 */
+ if (plain[2] != 0xff) {
+ wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
+ "PS (BT=01)");
+ return -1;
+ }
+ while (pos < plain + len && *pos == 0xff)
+ pos++;
+ }
+
+ if (pos - plain - 2 < 8) {
+ /* PKCS #1 v1.5, 8.1: At least eight octets long PS */
+ wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature "
+ "padding");
+ return -1;
+ }
+
+ if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) {
+ wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+ "structure (2)");
+ return -1;
+ }
+ pos++;
+ len -= pos - plain;
+
+ /* Strip PKCS #1 header */
+ os_memmove(plain, pos, len);
+ *plain_len = len;
+
+ return 0;
+}
diff --git a/src/tls/pkcs1.h b/src/tls/pkcs1.h
new file mode 100644
index 0000000..68872b1
--- /dev/null
+++ b/src/tls/pkcs1.h
@@ -0,0 +1,28 @@
+/*
+ * PKCS #1 (RSA Encryption)
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PKCS1_H
+#define PKCS1_H
+
+int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key,
+ int use_private, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
+ const u8 *crypt, size_t crypt_len,
+ u8 *plain, size_t *plain_len);
+
+#endif /* PKCS1_H */
diff --git a/src/tls/pkcs5.c b/src/tls/pkcs5.c
new file mode 100644
index 0000000..4291b84
--- /dev/null
+++ b/src/tls/pkcs5.c
@@ -0,0 +1,238 @@
+/*
+ * PKCS #5 (Password-based Encryption)
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "crypto/md5.h"
+#include "asn1.h"
+#include "pkcs5.h"
+
+
+struct pkcs5_params {
+ enum pkcs5_alg {
+ PKCS5_ALG_UNKNOWN,
+ PKCS5_ALG_MD5_DES_CBC
+ } alg;
+ u8 salt[8];
+ size_t salt_len;
+ unsigned int iter_count;
+};
+
+
+enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
+{
+ if (oid->len == 7 &&
+ oid->oid[0] == 1 /* iso */ &&
+ oid->oid[1] == 2 /* member-body */ &&
+ oid->oid[2] == 840 /* us */ &&
+ oid->oid[3] == 113549 /* rsadsi */ &&
+ oid->oid[4] == 1 /* pkcs */ &&
+ oid->oid[5] == 5 /* pkcs-5 */ &&
+ oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */)
+ return PKCS5_ALG_MD5_DES_CBC;
+
+ return PKCS5_ALG_UNKNOWN;
+}
+
+
+static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len,
+ struct pkcs5_params *params)
+{
+ struct asn1_hdr hdr;
+ const u8 *enc_alg_end, *pos, *end;
+ struct asn1_oid oid;
+ char obuf[80];
+
+ /* AlgorithmIdentifier */
+
+ enc_alg_end = enc_alg + enc_alg_len;
+
+ os_memset(params, 0, sizeof(*params));
+
+ if (asn1_get_oid(enc_alg, enc_alg_end - enc_alg, &oid, &pos)) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Failed to parse OID "
+ "(algorithm)");
+ return -1;
+ }
+
+ asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+ wpa_printf(MSG_DEBUG, "PKCS #5: encryption algorithm %s", obuf);
+ params->alg = pkcs5_get_alg(&oid);
+ if (params->alg == PKCS5_ALG_UNKNOWN) {
+ wpa_printf(MSG_INFO, "PKCS #5: unsupported encryption "
+ "algorithm %s", obuf);
+ return -1;
+ }
+
+ /*
+ * PKCS#5, Section 8
+ * PBEParameter ::= SEQUENCE {
+ * salt OCTET STRING SIZE(8),
+ * iterationCount INTEGER }
+ */
+
+ if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Expected SEQUENCE "
+ "(PBEParameter) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+ end = hdr.payload + hdr.length;
+
+ /* salt OCTET STRING SIZE(8) */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING ||
+ hdr.length != 8) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) "
+ "(salt) - found class %d tag 0x%x size %d",
+ hdr.class, hdr.tag, hdr.length);
+ return -1;
+ }
+ pos = hdr.payload + hdr.length;
+ os_memcpy(params->salt, hdr.payload, hdr.length);
+ params->salt_len = hdr.length;
+ wpa_hexdump(MSG_DEBUG, "PKCS #5: salt",
+ params->salt, params->salt_len);
+
+ /* iterationCount INTEGER */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Expected INTEGER - found "
+ "class %d tag 0x%x", hdr.class, hdr.tag);
+ return -1;
+ }
+ if (hdr.length == 1)
+ params->iter_count = *hdr.payload;
+ else if (hdr.length == 2)
+ params->iter_count = WPA_GET_BE16(hdr.payload);
+ else if (hdr.length == 4)
+ params->iter_count = WPA_GET_BE32(hdr.payload);
+ else {
+ wpa_hexdump(MSG_DEBUG, "PKCS #5: Unsupported INTEGER value "
+ " (iterationCount)",
+ hdr.payload, hdr.length);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x",
+ params->iter_count);
+ if (params->iter_count == 0 || params->iter_count > 0xffff) {
+ wpa_printf(MSG_INFO, "PKCS #5: Unsupported "
+ "iterationCount=0x%x", params->iter_count);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params,
+ const char *passwd)
+{
+ unsigned int i;
+ u8 hash[MD5_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+
+ if (params->alg != PKCS5_ALG_MD5_DES_CBC)
+ return NULL;
+
+ addr[0] = (const u8 *) passwd;
+ len[0] = os_strlen(passwd);
+ addr[1] = params->salt;
+ len[1] = params->salt_len;
+ if (md5_vector(2, addr, len, hash) < 0)
+ return NULL;
+ addr[0] = hash;
+ len[0] = MD5_MAC_LEN;
+ for (i = 1; i < params->iter_count; i++) {
+ if (md5_vector(1, addr, len, hash) < 0)
+ return NULL;
+ }
+ /* TODO: DES key parity bits(?) */
+ wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES key", hash, 8);
+ wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES IV", hash + 8, 8);
+
+ return crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8);
+}
+
+
+u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len,
+ const u8 *enc_data, size_t enc_data_len,
+ const char *passwd, size_t *data_len)
+{
+ struct crypto_cipher *ctx;
+ u8 *eb, pad;
+ struct pkcs5_params params;
+ unsigned int i;
+
+ if (pkcs5_get_params(enc_alg, enc_alg_len, &params) < 0) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters");
+ return NULL;
+ }
+
+ ctx = pkcs5_crypto_init(&params, passwd);
+ if (ctx == NULL) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Failed to initialize crypto");
+ return NULL;
+ }
+
+ /* PKCS #5, Section 7 - Decryption process */
+ if (enc_data_len < 16 || enc_data_len % 8) {
+ wpa_printf(MSG_INFO, "PKCS #5: invalid length of ciphertext "
+ "%d", (int) enc_data_len);
+ crypto_cipher_deinit(ctx);
+ return NULL;
+ }
+
+ eb = os_malloc(enc_data_len);
+ if (eb == NULL) {
+ crypto_cipher_deinit(ctx);
+ return NULL;
+ }
+
+ if (crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) {
+ wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB");
+ crypto_cipher_deinit(ctx);
+ os_free(eb);
+ return NULL;
+ }
+ crypto_cipher_deinit(ctx);
+
+ pad = eb[enc_data_len - 1];
+ if (pad > 8) {
+ wpa_printf(MSG_INFO, "PKCS #5: Invalid PS octet 0x%x", pad);
+ os_free(eb);
+ return NULL;
+ }
+ for (i = enc_data_len - pad; i < enc_data_len; i++) {
+ if (eb[i] != pad) {
+ wpa_hexdump(MSG_INFO, "PKCS #5: Invalid PS",
+ eb + enc_data_len - pad, pad);
+ os_free(eb);
+ return NULL;
+ }
+ }
+
+ wpa_hexdump_key(MSG_MSGDUMP, "PKCS #5: message M (encrypted key)",
+ eb, enc_data_len - pad);
+
+ *data_len = enc_data_len - pad;
+ return eb;
+}
diff --git a/src/tls/pkcs5.h b/src/tls/pkcs5.h
new file mode 100644
index 0000000..6ed3923
--- /dev/null
+++ b/src/tls/pkcs5.h
@@ -0,0 +1,22 @@
+/*
+ * PKCS #5 (Password-based Encryption)
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PKCS5_H
+#define PKCS5_H
+
+u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len,
+ const u8 *enc_data, size_t enc_data_len,
+ const char *passwd, size_t *data_len);
+
+#endif /* PKCS5_H */
diff --git a/src/tls/pkcs8.c b/src/tls/pkcs8.c
new file mode 100644
index 0000000..69ab262
--- /dev/null
+++ b/src/tls/pkcs8.c
@@ -0,0 +1,193 @@
+/*
+ * PKCS #8 (Private-key information syntax)
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "asn1.h"
+#include "bignum.h"
+#include "rsa.h"
+#include "pkcs5.h"
+#include "pkcs8.h"
+
+
+struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+ struct bignum *zero;
+ struct asn1_oid oid;
+ char obuf[80];
+
+ /* PKCS #8, Chapter 6 */
+
+ /* PrivateKeyInfo ::= SEQUENCE */
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
+ "header (SEQUENCE); assume PKCS #8 not used");
+ return NULL;
+ }
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ /* version Version (Version ::= INTEGER) */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found "
+ "class %d tag 0x%x; assume PKCS #8 not used",
+ hdr.class, hdr.tag);
+ return NULL;
+ }
+
+ zero = bignum_init();
+ if (zero == NULL)
+ return NULL;
+
+ if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER");
+ bignum_deinit(zero);
+ return NULL;
+ }
+ pos = hdr.payload + hdr.length;
+
+ if (bignum_cmp_d(zero, 0) != 0) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the "
+ "beginning of private key; not found; assume "
+ "PKCS #8 not used");
+ bignum_deinit(zero);
+ return NULL;
+ }
+ bignum_deinit(zero);
+
+ /* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier
+ * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
+ "(AlgorithmIdentifier) - found class %d tag 0x%x; "
+ "assume PKCS #8 not used",
+ hdr.class, hdr.tag);
+ return NULL;
+ }
+
+ if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID "
+ "(algorithm); assume PKCS #8 not used");
+ return NULL;
+ }
+
+ asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+ wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf);
+
+ if (oid.len != 7 ||
+ oid.oid[0] != 1 /* iso */ ||
+ oid.oid[1] != 2 /* member-body */ ||
+ oid.oid[2] != 840 /* us */ ||
+ oid.oid[3] != 113549 /* rsadsi */ ||
+ oid.oid[4] != 1 /* pkcs */ ||
+ oid.oid[5] != 1 /* pkcs-1 */ ||
+ oid.oid[6] != 1 /* rsaEncryption */) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key "
+ "algorithm %s", obuf);
+ return NULL;
+ }
+
+ pos = hdr.payload + hdr.length;
+
+ /* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
+ "(privateKey) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey");
+
+ return (struct crypto_private_key *)
+ crypto_rsa_import_private_key(hdr.payload, hdr.length);
+}
+
+
+struct crypto_private_key *
+pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end, *enc_alg;
+ size_t enc_alg_len;
+ u8 *data;
+ size_t data_len;
+
+ if (passwd == NULL)
+ return NULL;
+
+ /*
+ * PKCS #8, Chapter 7
+ * EncryptedPrivateKeyInfo ::= SEQUENCE {
+ * encryptionAlgorithm EncryptionAlgorithmIdentifier,
+ * encryptedData EncryptedData }
+ * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ * EncryptedData ::= OCTET STRING
+ */
+
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
+ "header (SEQUENCE); assume encrypted PKCS #8 not "
+ "used");
+ return NULL;
+ }
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ /* encryptionAlgorithm EncryptionAlgorithmIdentifier */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
+ "(AlgorithmIdentifier) - found class %d tag 0x%x; "
+ "assume encrypted PKCS #8 not used",
+ hdr.class, hdr.tag);
+ return NULL;
+ }
+ enc_alg = hdr.payload;
+ enc_alg_len = hdr.length;
+ pos = hdr.payload + hdr.length;
+
+ /* encryptedData EncryptedData */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
+ "(encryptedData) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return NULL;
+ }
+
+ data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length,
+ passwd, &data_len);
+ if (data) {
+ struct crypto_private_key *key;
+ key = pkcs8_key_import(data, data_len);
+ os_free(data);
+ return key;
+ }
+
+ return NULL;
+}
diff --git a/src/tls/pkcs8.h b/src/tls/pkcs8.h
new file mode 100644
index 0000000..dac517c
--- /dev/null
+++ b/src/tls/pkcs8.h
@@ -0,0 +1,22 @@
+/*
+ * PKCS #8 (Private-key information syntax)
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PKCS8_H
+#define PKCS8_H
+
+struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len);
+struct crypto_private_key *
+pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd);
+
+#endif /* PKCS8_H */
diff --git a/src/tls/rsa.c b/src/tls/rsa.c
new file mode 100644
index 0000000..3084adc
--- /dev/null
+++ b/src/tls/rsa.c
@@ -0,0 +1,358 @@
+/*
+ * RSA
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "asn1.h"
+#include "bignum.h"
+#include "rsa.h"
+
+
+struct crypto_rsa_key {
+ int private_key; /* whether private key is set */
+ struct bignum *n; /* modulus (p * q) */
+ struct bignum *e; /* public exponent */
+ /* The following parameters are available only if private_key is set */
+ struct bignum *d; /* private exponent */
+ struct bignum *p; /* prime p (factor of n) */
+ struct bignum *q; /* prime q (factor of n) */
+ struct bignum *dmp1; /* d mod (p - 1); CRT exponent */
+ struct bignum *dmq1; /* d mod (q - 1); CRT exponent */
+ struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */
+};
+
+
+static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end,
+ struct bignum *num)
+{
+ struct asn1_hdr hdr;
+
+ if (pos == NULL)
+ return NULL;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "RSA: Expected INTEGER - found class %d "
+ "tag 0x%x", hdr.class, hdr.tag);
+ return NULL;
+ }
+
+ if (bignum_set_unsigned_bin(num, hdr.payload, hdr.length) < 0) {
+ wpa_printf(MSG_DEBUG, "RSA: Failed to parse INTEGER");
+ return NULL;
+ }
+
+ return hdr.payload + hdr.length;
+}
+
+
+/**
+ * crypto_rsa_import_public_key - Import an RSA public key
+ * @buf: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ */
+struct crypto_rsa_key *
+crypto_rsa_import_public_key(const u8 *buf, size_t len)
+{
+ struct crypto_rsa_key *key;
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+
+ key = os_zalloc(sizeof(*key));
+ if (key == NULL)
+ return NULL;
+
+ key->n = bignum_init();
+ key->e = bignum_init();
+ if (key->n == NULL || key->e == NULL) {
+ crypto_rsa_free(key);
+ return NULL;
+ }
+
+ /*
+ * PKCS #1, 7.1:
+ * RSAPublicKey ::= SEQUENCE {
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER -- e
+ * }
+ */
+
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
+ "(public key) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto error;
+ }
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ pos = crypto_rsa_parse_integer(pos, end, key->n);
+ pos = crypto_rsa_parse_integer(pos, end, key->e);
+
+ if (pos == NULL)
+ goto error;
+
+ if (pos != end) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSA: Extra data in public key SEQUENCE",
+ pos, end - pos);
+ goto error;
+ }
+
+ return key;
+
+error:
+ crypto_rsa_free(key);
+ return NULL;
+}
+
+
+/**
+ * crypto_rsa_import_private_key - Import an RSA private key
+ * @buf: Key buffer (DER encoded RSA private key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the private key or %NULL on failure
+ */
+struct crypto_rsa_key *
+crypto_rsa_import_private_key(const u8 *buf, size_t len)
+{
+ struct crypto_rsa_key *key;
+ struct bignum *zero;
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+
+ key = os_zalloc(sizeof(*key));
+ if (key == NULL)
+ return NULL;
+
+ key->private_key = 1;
+
+ key->n = bignum_init();
+ key->e = bignum_init();
+ key->d = bignum_init();
+ key->p = bignum_init();
+ key->q = bignum_init();
+ key->dmp1 = bignum_init();
+ key->dmq1 = bignum_init();
+ key->iqmp = bignum_init();
+
+ if (key->n == NULL || key->e == NULL || key->d == NULL ||
+ key->p == NULL || key->q == NULL || key->dmp1 == NULL ||
+ key->dmq1 == NULL || key->iqmp == NULL) {
+ crypto_rsa_free(key);
+ return NULL;
+ }
+
+ /*
+ * PKCS #1, 7.2:
+ * RSAPrivateKey ::= SEQUENCE {
+ * version Version,
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER, -- e
+ * privateExponent INTEGER, -- d
+ * prime1 INTEGER, -- p
+ * prime2 INTEGER, -- q
+ * exponent1 INTEGER, -- d mod (p-1)
+ * exponent2 INTEGER, -- d mod (q-1)
+ * coefficient INTEGER -- (inverse of q) mod p
+ * }
+ *
+ * Version ::= INTEGER -- shall be 0 for this version of the standard
+ */
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
+ "(public key) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto error;
+ }
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ zero = bignum_init();
+ if (zero == NULL)
+ goto error;
+ pos = crypto_rsa_parse_integer(pos, end, zero);
+ if (pos == NULL || bignum_cmp_d(zero, 0) != 0) {
+ wpa_printf(MSG_DEBUG, "RSA: Expected zero INTEGER in the "
+ "beginning of private key; not found");
+ bignum_deinit(zero);
+ goto error;
+ }
+ bignum_deinit(zero);
+
+ pos = crypto_rsa_parse_integer(pos, end, key->n);
+ pos = crypto_rsa_parse_integer(pos, end, key->e);
+ pos = crypto_rsa_parse_integer(pos, end, key->d);
+ pos = crypto_rsa_parse_integer(pos, end, key->p);
+ pos = crypto_rsa_parse_integer(pos, end, key->q);
+ pos = crypto_rsa_parse_integer(pos, end, key->dmp1);
+ pos = crypto_rsa_parse_integer(pos, end, key->dmq1);
+ pos = crypto_rsa_parse_integer(pos, end, key->iqmp);
+
+ if (pos == NULL)
+ goto error;
+
+ if (pos != end) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSA: Extra data in public key SEQUENCE",
+ pos, end - pos);
+ goto error;
+ }
+
+ return key;
+
+error:
+ crypto_rsa_free(key);
+ return NULL;
+}
+
+
+/**
+ * crypto_rsa_get_modulus_len - Get the modulus length of the RSA key
+ * @key: RSA key
+ * Returns: Modulus length of the key
+ */
+size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key)
+{
+ return bignum_get_unsigned_bin_len(key->n);
+}
+
+
+/**
+ * crypto_rsa_exptmod - RSA modular exponentiation
+ * @in: Input data
+ * @inlen: Input data length
+ * @out: Buffer for output data
+ * @outlen: Maximum size of the output buffer and used size on success
+ * @key: RSA key
+ * @use_private: 1 = Use RSA private key, 0 = Use RSA public key
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
+ struct crypto_rsa_key *key, int use_private)
+{
+ struct bignum *tmp, *a = NULL, *b = NULL;
+ int ret = -1;
+ size_t modlen;
+
+ if (use_private && !key->private_key)
+ return -1;
+
+ tmp = bignum_init();
+ if (tmp == NULL)
+ return -1;
+
+ if (bignum_set_unsigned_bin(tmp, in, inlen) < 0)
+ goto error;
+ if (bignum_cmp(key->n, tmp) < 0) {
+ /* Too large input value for the RSA key modulus */
+ goto error;
+ }
+
+ if (use_private) {
+ /*
+ * Decrypt (or sign) using Chinese remainer theorem to speed
+ * up calculation. This is equivalent to tmp = tmp^d mod n
+ * (which would require more CPU to calculate directly).
+ *
+ * dmp1 = (1/e) mod (p-1)
+ * dmq1 = (1/e) mod (q-1)
+ * iqmp = (1/q) mod p, where p > q
+ * m1 = c^dmp1 mod p
+ * m2 = c^dmq1 mod q
+ * h = q^-1 (m1 - m2) mod p
+ * m = m2 + hq
+ */
+ a = bignum_init();
+ b = bignum_init();
+ if (a == NULL || b == NULL)
+ goto error;
+
+ /* a = tmp^dmp1 mod p */
+ if (bignum_exptmod(tmp, key->dmp1, key->p, a) < 0)
+ goto error;
+
+ /* b = tmp^dmq1 mod q */
+ if (bignum_exptmod(tmp, key->dmq1, key->q, b) < 0)
+ goto error;
+
+ /* tmp = (a - b) * (1/q mod p) (mod p) */
+ if (bignum_sub(a, b, tmp) < 0 ||
+ bignum_mulmod(tmp, key->iqmp, key->p, tmp) < 0)
+ goto error;
+
+ /* tmp = b + q * tmp */
+ if (bignum_mul(tmp, key->q, tmp) < 0 ||
+ bignum_add(tmp, b, tmp) < 0)
+ goto error;
+ } else {
+ /* Encrypt (or verify signature) */
+ /* tmp = tmp^e mod N */
+ if (bignum_exptmod(tmp, key->e, key->n, tmp) < 0)
+ goto error;
+ }
+
+ modlen = crypto_rsa_get_modulus_len(key);
+ if (modlen > *outlen) {
+ *outlen = modlen;
+ goto error;
+ }
+
+ if (bignum_get_unsigned_bin_len(tmp) > modlen)
+ goto error; /* should never happen */
+
+ *outlen = modlen;
+ os_memset(out, 0, modlen);
+ if (bignum_get_unsigned_bin(
+ tmp, out +
+ (modlen - bignum_get_unsigned_bin_len(tmp)), NULL) < 0)
+ goto error;
+
+ ret = 0;
+
+error:
+ bignum_deinit(tmp);
+ bignum_deinit(a);
+ bignum_deinit(b);
+ return ret;
+}
+
+
+/**
+ * crypto_rsa_free - Free RSA key
+ * @key: RSA key to be freed
+ *
+ * This function frees an RSA key imported with either
+ * crypto_rsa_import_public_key() or crypto_rsa_import_private_key().
+ */
+void crypto_rsa_free(struct crypto_rsa_key *key)
+{
+ if (key) {
+ bignum_deinit(key->n);
+ bignum_deinit(key->e);
+ bignum_deinit(key->d);
+ bignum_deinit(key->p);
+ bignum_deinit(key->q);
+ bignum_deinit(key->dmp1);
+ bignum_deinit(key->dmq1);
+ bignum_deinit(key->iqmp);
+ os_free(key);
+ }
+}
diff --git a/src/tls/rsa.h b/src/tls/rsa.h
new file mode 100644
index 0000000..ac50dfd
--- /dev/null
+++ b/src/tls/rsa.h
@@ -0,0 +1,29 @@
+/*
+ * RSA
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef RSA_H
+#define RSA_H
+
+struct crypto_rsa_key;
+
+struct crypto_rsa_key *
+crypto_rsa_import_public_key(const u8 *buf, size_t len);
+struct crypto_rsa_key *
+crypto_rsa_import_private_key(const u8 *buf, size_t len);
+size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key);
+int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
+ struct crypto_rsa_key *key, int use_private);
+void crypto_rsa_free(struct crypto_rsa_key *key);
+
+#endif /* RSA_H */
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
new file mode 100644
index 0000000..afb6031
--- /dev/null
+++ b/src/tls/tlsv1_client.c
@@ -0,0 +1,667 @@
+/*
+ * TLSv1 client (RFC 2246)
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+/* TODO:
+ * Support for a message fragmented across several records (RFC 2246, 6.2.1)
+ */
+
+
+void tls_alert(struct tlsv1_client *conn, u8 level, u8 description)
+{
+ conn->alert_level = level;
+ conn->alert_description = description;
+}
+
+
+void tlsv1_client_free_dh(struct tlsv1_client *conn)
+{
+ os_free(conn->dh_p);
+ os_free(conn->dh_g);
+ os_free(conn->dh_ys);
+ conn->dh_p = conn->dh_g = conn->dh_ys = NULL;
+}
+
+
+int tls_derive_pre_master_secret(u8 *pre_master_secret)
+{
+ WPA_PUT_BE16(pre_master_secret, TLS_VERSION);
+ if (os_get_random(pre_master_secret + 2,
+ TLS_PRE_MASTER_SECRET_LEN - 2))
+ return -1;
+ return 0;
+}
+
+
+int tls_derive_keys(struct tlsv1_client *conn,
+ const u8 *pre_master_secret, size_t pre_master_secret_len)
+{
+ u8 seed[2 * TLS_RANDOM_LEN];
+ u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
+ u8 *pos;
+ size_t key_block_len;
+
+ if (pre_master_secret) {
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
+ pre_master_secret, pre_master_secret_len);
+ os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+ TLS_RANDOM_LEN);
+ if (tls_prf(pre_master_secret, pre_master_secret_len,
+ "master secret", seed, 2 * TLS_RANDOM_LEN,
+ conn->master_secret, TLS_MASTER_SECRET_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
+ "master_secret");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
+ conn->master_secret, TLS_MASTER_SECRET_LEN);
+ }
+
+ os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
+ key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+ conn->rl.iv_size);
+ if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "key expansion", seed, 2 * TLS_RANDOM_LEN,
+ key_block, key_block_len)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
+ key_block, key_block_len);
+
+ pos = key_block;
+
+ /* client_write_MAC_secret */
+ os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
+ pos += conn->rl.hash_size;
+ /* server_write_MAC_secret */
+ os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
+ pos += conn->rl.hash_size;
+
+ /* client_write_key */
+ os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
+ pos += conn->rl.key_material_len;
+ /* server_write_key */
+ os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
+ pos += conn->rl.key_material_len;
+
+ /* client_write_IV */
+ os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+ pos += conn->rl.iv_size;
+ /* server_write_IV */
+ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+ pos += conn->rl.iv_size;
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_client_handshake - Process TLS handshake
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * @appl_data_len: Pointer to variable that is set to appl_data length
+ * Returns: Pointer to output data, %NULL on failure
+ */
+u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ size_t *out_len, u8 **appl_data,
+ size_t *appl_data_len)
+{
+ const u8 *pos, *end;
+ u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
+ size_t in_msg_len;
+ int no_appl_data;
+
+ if (conn->state == CLIENT_HELLO) {
+ if (in_len)
+ return NULL;
+ return tls_send_client_hello(conn, out_len);
+ }
+
+ if (in_data == NULL || in_len == 0)
+ return NULL;
+
+ pos = in_data;
+ end = in_data + in_len;
+ in_msg = os_malloc(in_len);
+ if (in_msg == NULL)
+ return NULL;
+
+ /* Each received packet may include multiple records */
+ while (pos < end) {
+ in_msg_len = in_len;
+ if (tlsv1_record_receive(&conn->rl, pos, end - pos,
+ in_msg, &in_msg_len, &alert)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
+ "record failed");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ goto failed;
+ }
+ ct = pos[0];
+
+ in_pos = in_msg;
+ in_end = in_msg + in_msg_len;
+
+ /* Each received record may include multiple messages of the
+ * same ContentType. */
+ while (in_pos < in_end) {
+ in_msg_len = in_end - in_pos;
+ if (tlsv1_client_process_handshake(conn, ct, in_pos,
+ &in_msg_len,
+ appl_data,
+ appl_data_len) < 0)
+ goto failed;
+ in_pos += in_msg_len;
+ }
+
+ pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+ }
+
+ os_free(in_msg);
+ in_msg = NULL;
+
+ no_appl_data = appl_data == NULL || *appl_data == NULL;
+ msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data);
+
+failed:
+ os_free(in_msg);
+ if (conn->alert_level) {
+ conn->state = FAILED;
+ os_free(msg);
+ msg = tlsv1_client_send_alert(conn, conn->alert_level,
+ conn->alert_description,
+ out_len);
+ } else if (msg == NULL) {
+ msg = os_zalloc(1);
+ *out_len = 0;
+ }
+
+ return msg;
+}
+
+
+/**
+ * tlsv1_client_encrypt - Encrypt data into TLS tunnel
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel.
+ */
+int tlsv1_client_encrypt(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len)
+{
+ size_t rlen;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
+ in_data, in_len);
+
+ os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
+ out_data, out_len, in_len, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ return rlen;
+}
+
+
+/**
+ * tlsv1_client_decrypt - Decrypt data from TLS tunnel
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Pointer to input buffer (encrypted TLS data)
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel.
+ */
+int tlsv1_client_decrypt(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len)
+{
+ const u8 *in_end, *pos;
+ int res;
+ u8 alert, *out_end, *out_pos;
+ size_t olen;
+
+ pos = in_data;
+ in_end = in_data + in_len;
+ out_pos = out_data;
+ out_end = out_data + out_len;
+
+ while (pos < in_end) {
+ if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
+ "0x%x", pos[0]);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ olen = out_end - out_pos;
+ res = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+ out_pos, &olen, &alert);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
+ "failed");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ return -1;
+ }
+ out_pos += olen;
+ if (out_pos > out_end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
+ "for processing the received record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+ }
+
+ return out_pos - out_data;
+}
+
+
+/**
+ * tlsv1_client_global_init - Initialize TLSv1 client
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before using any other TLSv1 client functions.
+ */
+int tlsv1_client_global_init(void)
+{
+ return crypto_global_init();
+}
+
+
+/**
+ * tlsv1_client_global_deinit - Deinitialize TLSv1 client
+ *
+ * This function can be used to deinitialize the TLSv1 client that was
+ * initialized by calling tlsv1_client_global_init(). No TLSv1 client functions
+ * can be called after this before calling tlsv1_client_global_init() again.
+ */
+void tlsv1_client_global_deinit(void)
+{
+ crypto_global_deinit();
+}
+
+
+/**
+ * tlsv1_client_init - Initialize TLSv1 client connection
+ * Returns: Pointer to TLSv1 client connection data or %NULL on failure
+ */
+struct tlsv1_client * tlsv1_client_init(void)
+{
+ struct tlsv1_client *conn;
+ size_t count;
+ u16 *suites;
+
+ conn = os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
+
+ conn->state = CLIENT_HELLO;
+
+ if (tls_verify_hash_init(&conn->verify) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
+ "hash");
+ os_free(conn);
+ return NULL;
+ }
+
+ count = 0;
+ suites = conn->cipher_suites;
+#ifndef CONFIG_CRYPTO_INTERNAL
+ suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+#endif /* CONFIG_CRYPTO_INTERNAL */
+ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+ suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+ conn->num_cipher_suites = count;
+
+ return conn;
+}
+
+
+/**
+ * tlsv1_client_deinit - Deinitialize TLSv1 client connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ */
+void tlsv1_client_deinit(struct tlsv1_client *conn)
+{
+ crypto_public_key_free(conn->server_rsa_key);
+ tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+ tlsv1_record_change_write_cipher(&conn->rl);
+ tlsv1_record_change_read_cipher(&conn->rl);
+ tls_verify_hash_free(&conn->verify);
+ os_free(conn->client_hello_ext);
+ tlsv1_client_free_dh(conn);
+ tlsv1_cred_free(conn->cred);
+ os_free(conn);
+}
+
+
+/**
+ * tlsv1_client_established - Check whether connection has been established
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 1 if connection is established, 0 if not
+ */
+int tlsv1_client_established(struct tlsv1_client *conn)
+{
+ return conn->state == ESTABLISHED;
+}
+
+
+/**
+ * tlsv1_client_prf - Use TLS-PRF to derive keying material
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+ int server_random_first, u8 *out, size_t out_len)
+{
+ u8 seed[2 * TLS_RANDOM_LEN];
+
+ if (conn->state != ESTABLISHED)
+ return -1;
+
+ if (server_random_first) {
+ os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
+ TLS_RANDOM_LEN);
+ } else {
+ os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+ TLS_RANDOM_LEN);
+ }
+
+ return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+}
+
+
+/**
+ * tlsv1_client_get_cipher - Get current cipher name
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
+ size_t buflen)
+{
+ char *cipher;
+
+ switch (conn->rl.cipher_suite) {
+ case TLS_RSA_WITH_RC4_128_MD5:
+ cipher = "RC4-MD5";
+ break;
+ case TLS_RSA_WITH_RC4_128_SHA:
+ cipher = "RC4-SHA";
+ break;
+ case TLS_RSA_WITH_DES_CBC_SHA:
+ cipher = "DES-CBC-SHA";
+ break;
+ case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ cipher = "DES-CBC3-SHA";
+ break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+ cipher = "ADH-AES-128-SHA";
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ cipher = "AES-256-SHA";
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ cipher = "AES-128-SHA";
+ break;
+ default:
+ return -1;
+ }
+
+ if (os_strlcpy(buf, cipher, buflen) >= buflen)
+ return -1;
+ return 0;
+}
+
+
+/**
+ * tlsv1_client_shutdown - Shutdown TLS connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_shutdown(struct tlsv1_client *conn)
+{
+ conn->state = CLIENT_HELLO;
+
+ if (tls_verify_hash_init(&conn->verify) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
+ "hash");
+ return -1;
+ }
+
+ tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+ tlsv1_record_change_write_cipher(&conn->rl);
+ tlsv1_record_change_read_cipher(&conn->rl);
+
+ conn->certificate_requested = 0;
+ crypto_public_key_free(conn->server_rsa_key);
+ conn->server_rsa_key = NULL;
+ conn->session_resumed = 0;
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_client_resumed - Was session resumption used
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tlsv1_client_resumed(struct tlsv1_client *conn)
+{
+ return !!conn->session_resumed;
+}
+
+
+/**
+ * tlsv1_client_hello_ext - Set TLS extension for ClientHello
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @ext_type: Extension type
+ * @data: Extension payload (%NULL to remove extension)
+ * @data_len: Extension payload length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
+ const u8 *data, size_t data_len)
+{
+ u8 *pos;
+
+ conn->session_ticket_included = 0;
+ os_free(conn->client_hello_ext);
+ conn->client_hello_ext = NULL;
+ conn->client_hello_ext_len = 0;
+
+ if (data == NULL || data_len == 0)
+ return 0;
+
+ pos = conn->client_hello_ext = os_malloc(6 + data_len);
+ if (pos == NULL)
+ return -1;
+
+ WPA_PUT_BE16(pos, 4 + data_len);
+ pos += 2;
+ WPA_PUT_BE16(pos, ext_type);
+ pos += 2;
+ WPA_PUT_BE16(pos, data_len);
+ pos += 2;
+ os_memcpy(pos, data, data_len);
+ conn->client_hello_ext_len = 6 + data_len;
+
+ if (ext_type == TLS_EXT_PAC_OPAQUE) {
+ conn->session_ticket_included = 1;
+ wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket");
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_client_get_keys - Get master key and random data from TLS connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @keys: Structure of key/random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys)
+{
+ os_memset(keys, 0, sizeof(*keys));
+ if (conn->state == CLIENT_HELLO)
+ return -1;
+
+ keys->client_random = conn->client_random;
+ keys->client_random_len = TLS_RANDOM_LEN;
+
+ if (conn->state != SERVER_HELLO) {
+ keys->server_random = conn->server_random;
+ keys->server_random_len = TLS_RANDOM_LEN;
+ keys->master_key = conn->master_secret;
+ keys->master_key_len = TLS_MASTER_SECRET_LEN;
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_client_get_keyblock_size - Get TLS key_block size
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn)
+{
+ if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
+ return -1;
+
+ return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+ conn->rl.iv_size);
+}
+
+
+/**
+ * tlsv1_client_set_cipher_list - Configure acceptable cipher suites
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers)
+{
+ size_t count;
+ u16 *suites;
+
+ /* TODO: implement proper configuration of cipher suites */
+ if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
+ count = 0;
+ suites = conn->cipher_suites;
+#ifndef CONFIG_CRYPTO_INTERNAL
+ suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
+#endif /* CONFIG_CRYPTO_INTERNAL */
+ suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+ suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+ suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
+ suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
+
+ /*
+ * Cisco AP (at least 350 and 1200 series) local authentication
+ * server does not know how to search cipher suites from the
+ * list and seem to require that the last entry in the list is
+ * the one that it wants to use. However, TLS specification
+ * requires the list to be in the client preference order. As a
+ * workaround, add anon-DH AES-128-SHA1 again at the end of the
+ * list to allow the Cisco code to find it.
+ */
+ suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+ conn->num_cipher_suites = count;
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_client_set_cred - Set client credentials
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @cred: Credentials from tlsv1_cred_alloc()
+ * Returns: 0 on success, -1 on failure
+ *
+ * On success, the client takes ownership of the credentials block and caller
+ * must not free it. On failure, caller is responsible for freeing the
+ * credential block.
+ */
+int tlsv1_client_set_cred(struct tlsv1_client *conn,
+ struct tlsv1_credentials *cred)
+{
+ tlsv1_cred_free(conn->cred);
+ conn->cred = cred;
+ return 0;
+}
+
+
+void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
+ tlsv1_client_session_ticket_cb cb,
+ void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
+ cb, ctx);
+ conn->session_ticket_cb = cb;
+ conn->session_ticket_cb_ctx = ctx;
+}
diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h
new file mode 100644
index 0000000..16ad57d
--- /dev/null
+++ b/src/tls/tlsv1_client.h
@@ -0,0 +1,59 @@
+/*
+ * TLSv1 client (RFC 2246)
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef TLSV1_CLIENT_H
+#define TLSV1_CLIENT_H
+
+#include "tlsv1_cred.h"
+
+struct tlsv1_client;
+
+int tlsv1_client_global_init(void);
+void tlsv1_client_global_deinit(void);
+struct tlsv1_client * tlsv1_client_init(void);
+void tlsv1_client_deinit(struct tlsv1_client *conn);
+int tlsv1_client_established(struct tlsv1_client *conn);
+int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+ int server_random_first, u8 *out, size_t out_len);
+u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ size_t *out_len, u8 **appl_data,
+ size_t *appl_data_len);
+int tlsv1_client_encrypt(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len);
+int tlsv1_client_decrypt(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len);
+int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
+ size_t buflen);
+int tlsv1_client_shutdown(struct tlsv1_client *conn);
+int tlsv1_client_resumed(struct tlsv1_client *conn);
+int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
+ const u8 *data, size_t data_len);
+int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys);
+int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn);
+int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers);
+int tlsv1_client_set_cred(struct tlsv1_client *conn,
+ struct tlsv1_credentials *cred);
+
+typedef int (*tlsv1_client_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
+ tlsv1_client_session_ticket_cb cb,
+ void *ctx);
+
+#endif /* TLSV1_CLIENT_H */
diff --git a/src/tls/tlsv1_client_i.h b/src/tls/tlsv1_client_i.h
new file mode 100644
index 0000000..7fe179f
--- /dev/null
+++ b/src/tls/tlsv1_client_i.h
@@ -0,0 +1,87 @@
+/*
+ * TLSv1 client - internal structures
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef TLSV1_CLIENT_I_H
+#define TLSV1_CLIENT_I_H
+
+struct tlsv1_client {
+ enum {
+ CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE,
+ SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST,
+ SERVER_HELLO_DONE, CLIENT_KEY_EXCHANGE, CHANGE_CIPHER_SPEC,
+ SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, ACK_FINISHED,
+ ESTABLISHED, FAILED
+ } state;
+
+ struct tlsv1_record_layer rl;
+
+ u8 session_id[TLS_SESSION_ID_MAX_LEN];
+ size_t session_id_len;
+ u8 client_random[TLS_RANDOM_LEN];
+ u8 server_random[TLS_RANDOM_LEN];
+ u8 master_secret[TLS_MASTER_SECRET_LEN];
+
+ u8 alert_level;
+ u8 alert_description;
+
+ unsigned int certificate_requested:1;
+ unsigned int session_resumed:1;
+ unsigned int session_ticket_included:1;
+ unsigned int use_session_ticket:1;
+
+ struct crypto_public_key *server_rsa_key;
+
+ struct tls_verify_hash verify;
+
+#define MAX_CIPHER_COUNT 30
+ u16 cipher_suites[MAX_CIPHER_COUNT];
+ size_t num_cipher_suites;
+
+ u16 prev_cipher_suite;
+
+ u8 *client_hello_ext;
+ size_t client_hello_ext_len;
+
+ /* The prime modulus used for Diffie-Hellman */
+ u8 *dh_p;
+ size_t dh_p_len;
+ /* The generator used for Diffie-Hellman */
+ u8 *dh_g;
+ size_t dh_g_len;
+ /* The server's Diffie-Hellman public value */
+ u8 *dh_ys;
+ size_t dh_ys_len;
+
+ struct tlsv1_credentials *cred;
+
+ tlsv1_client_session_ticket_cb session_ticket_cb;
+ void *session_ticket_cb_ctx;
+};
+
+
+void tls_alert(struct tlsv1_client *conn, u8 level, u8 description);
+void tlsv1_client_free_dh(struct tlsv1_client *conn);
+int tls_derive_pre_master_secret(u8 *pre_master_secret);
+int tls_derive_keys(struct tlsv1_client *conn,
+ const u8 *pre_master_secret, size_t pre_master_secret_len);
+u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len);
+u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
+ u8 description, size_t *out_len);
+u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len,
+ int no_appl_data);
+int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
+ const u8 *buf, size_t *len,
+ u8 **out_data, size_t *out_len);
+
+#endif /* TLSV1_CLIENT_I_H */
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
new file mode 100644
index 0000000..ed3f260
--- /dev/null
+++ b/src/tls/tlsv1_client_read.c
@@ -0,0 +1,976 @@
+/*
+ * TLSv1 client - read handshake message
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len);
+static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len);
+static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len);
+
+
+static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len, i;
+ u16 cipher_suite;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4)
+ goto decode_error;
+
+ /* HandshakeType msg_type */
+ if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected ServerHello)", *pos);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello");
+ pos++;
+ /* uint24 length */
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left)
+ goto decode_error;
+
+ /* body - ServerHello */
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len);
+ end = pos + len;
+
+ /* ProtocolVersion server_version */
+ if (end - pos < 2)
+ goto decode_error;
+ if (WPA_GET_BE16(pos) != TLS_VERSION) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
+ "ServerHello");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_PROTOCOL_VERSION);
+ return -1;
+ }
+ pos += 2;
+
+ /* Random random */
+ if (end - pos < TLS_RANDOM_LEN)
+ goto decode_error;
+
+ os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN);
+ pos += TLS_RANDOM_LEN;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
+ conn->server_random, TLS_RANDOM_LEN);
+
+ /* SessionID session_id */
+ if (end - pos < 1)
+ goto decode_error;
+ if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
+ goto decode_error;
+ if (conn->session_id_len && conn->session_id_len == *pos &&
+ os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) {
+ pos += 1 + conn->session_id_len;
+ wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session");
+ conn->session_resumed = 1;
+ } else {
+ conn->session_id_len = *pos;
+ pos++;
+ os_memcpy(conn->session_id, pos, conn->session_id_len);
+ pos += conn->session_id_len;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
+ conn->session_id, conn->session_id_len);
+
+ /* CipherSuite cipher_suite */
+ if (end - pos < 2)
+ goto decode_error;
+ cipher_suite = WPA_GET_BE16(pos);
+ pos += 2;
+ for (i = 0; i < conn->num_cipher_suites; i++) {
+ if (cipher_suite == conn->cipher_suites[i])
+ break;
+ }
+ if (i == conn->num_cipher_suites) {
+ wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
+ "cipher suite 0x%04x", cipher_suite);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_ILLEGAL_PARAMETER);
+ return -1;
+ }
+
+ if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different "
+ "cipher suite for a resumed connection (0x%04x != "
+ "0x%04x)", cipher_suite, conn->prev_cipher_suite);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_ILLEGAL_PARAMETER);
+ return -1;
+ }
+
+ if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
+ "record layer");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ conn->prev_cipher_suite = cipher_suite;
+
+ /* CompressionMethod compression_method */
+ if (end - pos < 1)
+ goto decode_error;
+ if (*pos != TLS_COMPRESSION_NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
+ "compression 0x%02x", *pos);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_ILLEGAL_PARAMETER);
+ return -1;
+ }
+ pos++;
+
+ if (end != pos) {
+ /* TODO: ServerHello extensions */
+ wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the "
+ "end of ServerHello", pos, end - pos);
+ goto decode_error;
+ }
+
+ if (conn->session_ticket_included && conn->session_ticket_cb) {
+ /* TODO: include SessionTicket extension if one was included in
+ * ServerHello */
+ int res = conn->session_ticket_cb(
+ conn->session_ticket_cb_ctx, NULL, 0,
+ conn->client_random, conn->server_random,
+ conn->master_secret);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
+ "indicated failure");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_HANDSHAKE_FAILURE);
+ return -1;
+ }
+ conn->use_session_ticket = !!res;
+ }
+
+ if ((conn->session_resumed || conn->use_session_ticket) &&
+ tls_derive_keys(conn, NULL, 0)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ *in_len = end - in_data;
+
+ conn->state = (conn->session_resumed || conn->use_session_ticket) ?
+ SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE;
+
+ return 0;
+
+decode_error:
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+}
+
+
+static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len, list_len, cert_len, idx;
+ u8 type;
+ struct x509_certificate *chain = NULL, *last = NULL, *cert;
+ int reason;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
+ "(len=%lu)", (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
+ "length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE)
+ return tls_process_server_key_exchange(conn, ct, in_data,
+ in_len);
+ if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
+ return tls_process_certificate_request(conn, ct, in_data,
+ in_len);
+ if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+ return tls_process_server_hello_done(conn, ct, in_data,
+ in_len);
+ if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected Certificate/"
+ "ServerKeyExchange/CertificateRequest/"
+ "ServerHelloDone)", type);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "TLSv1: Received Certificate (certificate_list len %lu)",
+ (unsigned long) len);
+
+ /*
+ * opaque ASN.1Cert<2^24-1>;
+ *
+ * struct {
+ * ASN.1Cert certificate_list<1..2^24-1>;
+ * } Certificate;
+ */
+
+ end = pos + len;
+
+ if (end - pos < 3) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
+ "(left=%lu)", (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ list_len = WPA_GET_BE24(pos);
+ pos += 3;
+
+ if ((size_t) (end - pos) != list_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
+ "length (len=%lu left=%lu)",
+ (unsigned long) list_len,
+ (unsigned long) (end - pos));
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ idx = 0;
+ while (pos < end) {
+ if (end - pos < 3) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+ "certificate_list");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ cert_len = WPA_GET_BE24(pos);
+ pos += 3;
+
+ if ((size_t) (end - pos) < cert_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
+ "length (len=%lu left=%lu)",
+ (unsigned long) cert_len,
+ (unsigned long) (end - pos));
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
+ (unsigned long) idx, (unsigned long) cert_len);
+
+ if (idx == 0) {
+ crypto_public_key_free(conn->server_rsa_key);
+ if (tls_parse_cert(pos, cert_len,
+ &conn->server_rsa_key)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+ "the certificate");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_BAD_CERTIFICATE);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+ }
+
+ cert = x509_certificate_parse(pos, cert_len);
+ if (cert == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+ "the certificate");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_BAD_CERTIFICATE);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ if (last == NULL)
+ chain = cert;
+ else
+ last->next = cert;
+ last = cert;
+
+ idx++;
+ pos += cert_len;
+ }
+
+ if (conn->cred &&
+ x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
+ &reason) < 0) {
+ int tls_reason;
+ wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
+ "validation failed (reason=%d)", reason);
+ switch (reason) {
+ case X509_VALIDATE_BAD_CERTIFICATE:
+ tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+ break;
+ case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
+ tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
+ break;
+ case X509_VALIDATE_CERTIFICATE_REVOKED:
+ tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+ break;
+ case X509_VALIDATE_CERTIFICATE_EXPIRED:
+ tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+ break;
+ case X509_VALIDATE_CERTIFICATE_UNKNOWN:
+ tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
+ break;
+ case X509_VALIDATE_UNKNOWN_CA:
+ tls_reason = TLS_ALERT_UNKNOWN_CA;
+ break;
+ default:
+ tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+ break;
+ }
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ x509_certificate_chain_free(chain);
+
+ *in_len = end - in_data;
+
+ conn->state = SERVER_KEY_EXCHANGE;
+
+ return 0;
+}
+
+
+static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
+ const u8 *buf, size_t len)
+{
+ const u8 *pos, *end;
+
+ tlsv1_client_free_dh(conn);
+
+ pos = buf;
+ end = buf + len;
+
+ if (end - pos < 3)
+ goto fail;
+ conn->dh_p_len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu",
+ (unsigned long) conn->dh_p_len);
+ goto fail;
+ }
+ conn->dh_p = os_malloc(conn->dh_p_len);
+ if (conn->dh_p == NULL)
+ goto fail;
+ os_memcpy(conn->dh_p, pos, conn->dh_p_len);
+ pos += conn->dh_p_len;
+ wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)",
+ conn->dh_p, conn->dh_p_len);
+
+ if (end - pos < 3)
+ goto fail;
+ conn->dh_g_len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len)
+ goto fail;
+ conn->dh_g = os_malloc(conn->dh_g_len);
+ if (conn->dh_g == NULL)
+ goto fail;
+ os_memcpy(conn->dh_g, pos, conn->dh_g_len);
+ pos += conn->dh_g_len;
+ wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)",
+ conn->dh_g, conn->dh_g_len);
+ if (conn->dh_g_len == 1 && conn->dh_g[0] < 2)
+ goto fail;
+
+ if (end - pos < 3)
+ goto fail;
+ conn->dh_ys_len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len)
+ goto fail;
+ conn->dh_ys = os_malloc(conn->dh_ys_len);
+ if (conn->dh_ys == NULL)
+ goto fail;
+ os_memcpy(conn->dh_ys, pos, conn->dh_ys_len);
+ pos += conn->dh_ys_len;
+ wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
+ conn->dh_ys, conn->dh_ys_len);
+
+ return 0;
+
+fail:
+ wpa_printf(MSG_DEBUG, "TLSv1: Processing DH params failed");
+ tlsv1_client_free_dh(conn);
+ return -1;
+}
+
+
+static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len;
+ u8 type;
+ const struct tls_cipher_suite *suite;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange "
+ "(Left=%lu)", (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange "
+ "length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ end = pos + len;
+
+ if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
+ return tls_process_certificate_request(conn, ct, in_data,
+ in_len);
+ if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+ return tls_process_server_hello_done(conn, ct, in_data,
+ in_len);
+ if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected ServerKeyExchange/"
+ "CertificateRequest/ServerHelloDone)", type);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange");
+
+ if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed "
+ "with the selected cipher suite");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len);
+ suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+ if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
+ if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ *in_len = end - in_data;
+
+ conn->state = SERVER_CERTIFICATE_REQUEST;
+
+ return 0;
+}
+
+
+static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len;
+ u8 type;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateRequest "
+ "(left=%lu)", (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in CertificateRequest "
+ "length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ end = pos + len;
+
+ if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+ return tls_process_server_hello_done(conn, ct, in_data,
+ in_len);
+ if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected CertificateRequest/"
+ "ServerHelloDone)", type);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateRequest");
+
+ conn->certificate_requested = 1;
+
+ *in_len = end - in_data;
+
+ conn->state = SERVER_HELLO_DONE;
+
+ return 0;
+}
+
+
+static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len;
+ u8 type;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone "
+ "(left=%lu)", (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone "
+ "length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ end = pos + len;
+
+ if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected ServerHelloDone)", type);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone");
+
+ *in_len = end - in_data;
+
+ conn->state = CLIENT_KEY_EXCHANGE;
+
+ return 0;
+}
+
+
+static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn,
+ u8 ct, const u8 *in_data,
+ size_t *in_len)
+{
+ const u8 *pos;
+ size_t left;
+
+ if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+ "received content type 0x%x", ct);
+ if (conn->use_session_ticket) {
+ int res;
+ wpa_printf(MSG_DEBUG, "TLSv1: Server may have "
+ "rejected SessionTicket");
+ conn->use_session_ticket = 0;
+
+ /* Notify upper layers that SessionTicket failed */
+ res = conn->session_ticket_cb(
+ conn->session_ticket_cb_ctx, NULL, 0, NULL,
+ NULL, NULL);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket "
+ "callback indicated failure");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_HANDSHAKE_FAILURE);
+ return -1;
+ }
+
+ conn->state = SERVER_CERTIFICATE;
+ return tls_process_certificate(conn, ct, in_data,
+ in_len);
+ }
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 1) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ if (*pos != TLS_CHANGE_CIPHER_SPEC) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+ "received data 0x%x", *pos);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
+ if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
+ "for record layer");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ *in_len = pos + 1 - in_data;
+
+ conn->state = SERVER_FINISHED;
+
+ return 0;
+}
+
+
+static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len, hlen;
+ u8 verify_data[TLS_VERIFY_DATA_LEN];
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
+ "Finished",
+ (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
+ "type 0x%x", pos[0]);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ len = WPA_GET_BE24(pos + 1);
+
+ pos += 4;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
+ "(len=%lu > left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ end = pos + len;
+ if (len != TLS_VERIFY_DATA_LEN) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
+ "in Finished: %lu (expected %d)",
+ (unsigned long) len, TLS_VERIFY_DATA_LEN);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
+ pos, TLS_VERIFY_DATA_LEN);
+
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_server == NULL ||
+ crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_server = NULL;
+ crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
+ conn->verify.sha1_server = NULL;
+ return -1;
+ }
+ conn->verify.md5_server = NULL;
+ hlen = SHA1_MAC_LEN;
+ if (conn->verify.sha1_server == NULL ||
+ crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
+ &hlen) < 0) {
+ conn->verify.sha1_server = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha1_server = NULL;
+
+ if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+ verify_data, TLS_VERIFY_DATA_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
+ verify_data, TLS_VERIFY_DATA_LEN);
+
+ if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+ wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+
+ *in_len = end - in_data;
+
+ conn->state = (conn->session_resumed || conn->use_session_ticket) ?
+ CHANGE_CIPHER_SPEC : ACK_FINISHED;
+
+ return 0;
+}
+
+
+static int tls_process_application_data(struct tlsv1_client *conn, u8 ct,
+ const u8 *in_data, size_t *in_len,
+ u8 **out_data, size_t *out_len)
+{
+ const u8 *pos;
+ size_t left;
+
+ if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; "
+ "received content type 0x%x", ct);
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake",
+ pos, left);
+
+ *out_data = os_malloc(left);
+ if (*out_data) {
+ os_memcpy(*out_data, pos, left);
+ *out_len = left;
+ }
+
+ return 0;
+}
+
+
+int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
+ const u8 *buf, size_t *len,
+ u8 **out_data, size_t *out_len)
+{
+ if (ct == TLS_CONTENT_TYPE_ALERT) {
+ if (*len < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+ buf[0], buf[1]);
+ *len = 2;
+ conn->state = FAILED;
+ return -1;
+ }
+
+ if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 &&
+ buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) {
+ size_t hr_len = WPA_GET_BE24(buf + 1);
+ if (hr_len > *len - 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest");
+ *len = 4 + hr_len;
+ return 0;
+ }
+
+ switch (conn->state) {
+ case SERVER_HELLO:
+ if (tls_process_server_hello(conn, ct, buf, len))
+ return -1;
+ break;
+ case SERVER_CERTIFICATE:
+ if (tls_process_certificate(conn, ct, buf, len))
+ return -1;
+ break;
+ case SERVER_KEY_EXCHANGE:
+ if (tls_process_server_key_exchange(conn, ct, buf, len))
+ return -1;
+ break;
+ case SERVER_CERTIFICATE_REQUEST:
+ if (tls_process_certificate_request(conn, ct, buf, len))
+ return -1;
+ break;
+ case SERVER_HELLO_DONE:
+ if (tls_process_server_hello_done(conn, ct, buf, len))
+ return -1;
+ break;
+ case SERVER_CHANGE_CIPHER_SPEC:
+ if (tls_process_server_change_cipher_spec(conn, ct, buf, len))
+ return -1;
+ break;
+ case SERVER_FINISHED:
+ if (tls_process_server_finished(conn, ct, buf, len))
+ return -1;
+ break;
+ case ACK_FINISHED:
+ if (out_data &&
+ tls_process_application_data(conn, ct, buf, len, out_data,
+ out_len))
+ return -1;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
+ "while processing received message",
+ conn->state);
+ return -1;
+ }
+
+ if (ct == TLS_CONTENT_TYPE_HANDSHAKE)
+ tls_verify_hash_add(&conn->verify, buf, *len);
+
+ return 0;
+}
diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c
new file mode 100644
index 0000000..0898df9
--- /dev/null
+++ b/src/tls/tlsv1_client_write.c
@@ -0,0 +1,798 @@
+/*
+ * TLSv1 client - write handshake message
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "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, pos - hs_start, out_len) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(hello);
+ return NULL;
+ }
+
+ conn->state = SERVER_HELLO;
+
+ return hello;
+}
+
+
+static int tls_write_client_certificate(struct tlsv1_client *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
+ size_t rlen;
+ struct x509_certificate *cert;
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+ /* body - Certificate */
+ /* uint24 length (to be filled) */
+ cert_start = pos;
+ pos += 3;
+ cert = conn->cred ? conn->cred->cert : NULL;
+ while (cert) {
+ if (pos + 3 + cert->cert_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
+ "for Certificate (cert_len=%lu left=%lu)",
+ (unsigned long) cert->cert_len,
+ (unsigned long) (end - pos));
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ WPA_PUT_BE24(pos, cert->cert_len);
+ pos += 3;
+ os_memcpy(pos, cert->cert_start, cert->cert_len);
+ pos += cert->cert_len;
+
+ if (x509_certificate_self_signed(cert))
+ break;
+ cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+ &cert->issuer);
+ }
+ if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) {
+ /*
+ * Client was not configured with all the needed certificates
+ * to form a full certificate chain. The server may fail to
+ * validate the chain unless it is configured with all the
+ * missing CA certificates.
+ */
+ wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain "
+ "not configured - validation may fail");
+ }
+ WPA_PUT_BE24(cert_start, pos - cert_start - 3);
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
+{
+ /* ClientDiffieHellmanPublic */
+ u8 *csecret, *csecret_start, *dh_yc, *shared;
+ size_t csecret_len, dh_yc_len, shared_len;
+
+ csecret_len = conn->dh_p_len;
+ csecret = os_malloc(csecret_len);
+ if (csecret == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+ "memory for Yc (Diffie-Hellman)");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ if (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, pos - hs_start, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
+ size_t rlen, hlen, clen;
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos;
+ enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+
+ /*
+ * RFC 2246: 7.4.3 and 7.4.8:
+ * Signature signature
+ *
+ * RSA:
+ * digitally-signed struct {
+ * opaque md5_hash[16];
+ * opaque sha_hash[20];
+ * };
+ *
+ * DSA:
+ * digitally-signed struct {
+ * opaque sha_hash[20];
+ * };
+ *
+ * The hash values are calculated over all handshake messages sent or
+ * received starting at ClientHello up to, but not including, this
+ * CertificateVerify message, including the type and length fields of
+ * the handshake messages.
+ */
+
+ hpos = hash;
+
+ if (alg == SIGN_ALG_RSA) {
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_cert == NULL ||
+ crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
+ {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_cert = NULL;
+ crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+ conn->verify.sha1_cert = NULL;
+ return -1;
+ }
+ hpos += MD5_MAC_LEN;
+ } else
+ crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+
+ conn->verify.md5_cert = NULL;
+ hlen = SHA1_MAC_LEN;
+ if (conn->verify.sha1_cert == NULL ||
+ crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
+ conn->verify.sha1_cert = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha1_cert = NULL;
+
+ if (alg == SIGN_ALG_RSA)
+ hlen += MD5_MAC_LEN;
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+
+ /*
+ * RFC 2246, 4.7:
+ * In digital signing, one-way hash functions are used as input for a
+ * signing algorithm. A digitally-signed element is encoded as an
+ * opaque vector <0..2^16-1>, where the length is specified by the
+ * signing algorithm and key.
+ *
+ * In RSA signing, a 36-byte structure of two hashes (one SHA and one
+ * MD5) is signed (encrypted with the private key). It is encoded with
+ * PKCS #1 block type 0 or type 1 as described in [PKCS1].
+ */
+ signed_start = pos; /* length to be filled */
+ pos += 2;
+ clen = end - pos;
+ if (conn->cred == NULL ||
+ crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen,
+ pos, &clen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ WPA_PUT_BE16(signed_start, clen);
+
+ pos += clen;
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr;
+ size_t rlen;
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+ *pos = TLS_CHANGE_CIPHER_SPEC;
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
+ rhdr, end - rhdr, 1, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
+ "record layer");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ *msgpos = rhdr + rlen;
+
+ return 0;
+}
+
+
+static int tls_write_client_finished(struct tlsv1_client *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length;
+ size_t rlen, hlen;
+ u8 verify_data[TLS_VERIFY_DATA_LEN];
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+
+ /* Encrypted Handshake Message: Finished */
+
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_client == NULL ||
+ crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_client = NULL;
+ crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
+ conn->verify.sha1_client = NULL;
+ return -1;
+ }
+ conn->verify.md5_client = NULL;
+ hlen = SHA1_MAC_LEN;
+ if (conn->verify.sha1_client == NULL ||
+ crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
+ &hlen) < 0) {
+ conn->verify.sha1_client = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha1_client = NULL;
+
+ if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+ verify_data, TLS_VERIFY_DATA_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
+ verify_data, TLS_VERIFY_DATA_LEN);
+
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+ os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN);
+ pos += TLS_VERIFY_DATA_LEN;
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ pos = rhdr + rlen;
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn,
+ size_t *out_len)
+{
+ u8 *msg, *end, *pos;
+ size_t msglen;
+
+ *out_len = 0;
+
+ msglen = 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, TLS_VERSION);
+ pos += 2;
+ /* uint16 length (to be filled) */
+ length = pos;
+ pos += 2;
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Alert */
+ /* AlertLevel level */
+ *pos++ = level;
+ /* AlertDescription description */
+ *pos++ = description;
+
+ WPA_PUT_BE16(length, pos - length - 2);
+ *out_len = pos - alert;
+
+ return alert;
+}
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
new file mode 100644
index 0000000..2f9dd0f
--- /dev/null
+++ b/src/tls/tlsv1_common.c
@@ -0,0 +1,241 @@
+/*
+ * TLSv1 common routines
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+
+
+/*
+ * TODO:
+ * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
+ * Add support for commonly used cipher suites; don't bother with exportable
+ * suites.
+ */
+
+static const struct tls_cipher_suite tls_cipher_suites[] = {
+ { TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL,
+ TLS_HASH_NULL },
+ { TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
+ TLS_HASH_MD5 },
+ { TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
+ TLS_HASH_SHA },
+ { TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC,
+ TLS_HASH_SHA },
+ { TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA,
+ TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+ { TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_RC4_128, TLS_HASH_MD5 },
+ { TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_DES_CBC, TLS_HASH_SHA },
+ { TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+ { TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC,
+ TLS_HASH_SHA },
+ { TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
+ { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC,
+ TLS_HASH_SHA },
+ { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }
+};
+
+#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
+#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites)
+
+
+static const struct tls_cipher_data tls_ciphers[] = {
+ { TLS_CIPHER_NULL, TLS_CIPHER_STREAM, 0, 0, 0,
+ CRYPTO_CIPHER_NULL },
+ { TLS_CIPHER_IDEA_CBC, TLS_CIPHER_BLOCK, 16, 16, 8,
+ CRYPTO_CIPHER_NULL },
+ { TLS_CIPHER_RC2_CBC_40, TLS_CIPHER_BLOCK, 5, 16, 0,
+ CRYPTO_CIPHER_ALG_RC2 },
+ { TLS_CIPHER_RC4_40, TLS_CIPHER_STREAM, 5, 16, 0,
+ CRYPTO_CIPHER_ALG_RC4 },
+ { TLS_CIPHER_RC4_128, TLS_CIPHER_STREAM, 16, 16, 0,
+ CRYPTO_CIPHER_ALG_RC4 },
+ { TLS_CIPHER_DES40_CBC, TLS_CIPHER_BLOCK, 5, 8, 8,
+ CRYPTO_CIPHER_ALG_DES },
+ { TLS_CIPHER_DES_CBC, TLS_CIPHER_BLOCK, 8, 8, 8,
+ CRYPTO_CIPHER_ALG_DES },
+ { TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK, 24, 24, 8,
+ CRYPTO_CIPHER_ALG_3DES },
+ { TLS_CIPHER_AES_128_CBC, TLS_CIPHER_BLOCK, 16, 16, 16,
+ CRYPTO_CIPHER_ALG_AES },
+ { TLS_CIPHER_AES_256_CBC, TLS_CIPHER_BLOCK, 32, 32, 16,
+ CRYPTO_CIPHER_ALG_AES }
+};
+
+#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers)
+
+
+/**
+ * tls_get_cipher_suite - Get TLS cipher suite
+ * @suite: Cipher suite identifier
+ * Returns: Pointer to the cipher data or %NULL if not found
+ */
+const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite)
+{
+ size_t i;
+ for (i = 0; i < NUM_TLS_CIPHER_SUITES; i++)
+ if (tls_cipher_suites[i].suite == suite)
+ return &tls_cipher_suites[i];
+ return NULL;
+}
+
+
+const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher)
+{
+ size_t i;
+ for (i = 0; i < NUM_TLS_CIPHER_DATA; i++)
+ if (tls_ciphers[i].cipher == cipher)
+ return &tls_ciphers[i];
+ return NULL;
+}
+
+
+int tls_server_key_exchange_allowed(tls_cipher cipher)
+{
+ const struct tls_cipher_suite *suite;
+
+ /* RFC 2246, Section 7.4.3 */
+ suite = tls_get_cipher_suite(cipher);
+ if (suite == NULL)
+ return 0;
+
+ switch (suite->key_exchange) {
+ case TLS_KEY_X_DHE_DSS:
+ case TLS_KEY_X_DHE_DSS_EXPORT:
+ case TLS_KEY_X_DHE_RSA:
+ case TLS_KEY_X_DHE_RSA_EXPORT:
+ case TLS_KEY_X_DH_anon_EXPORT:
+ case TLS_KEY_X_DH_anon:
+ return 1;
+ case TLS_KEY_X_RSA_EXPORT:
+ return 1 /* FIX: public key len > 512 bits */;
+ default:
+ return 0;
+ }
+}
+
+
+/**
+ * tls_parse_cert - Parse DER encoded X.509 certificate and get public key
+ * @buf: ASN.1 DER encoded certificate
+ * @len: Length of the buffer
+ * @pk: Buffer for returning the allocated public key
+ * Returns: 0 on success, -1 on failure
+ *
+ * This functions parses an ASN.1 DER encoded X.509 certificate and retrieves
+ * the public key from it. The caller is responsible for freeing the public key
+ * by calling crypto_public_key_free().
+ */
+int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk)
+{
+ struct x509_certificate *cert;
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Parse ASN.1 DER certificate",
+ buf, len);
+
+ *pk = crypto_public_key_from_cert(buf, len);
+ if (*pk)
+ return 0;
+
+ cert = x509_certificate_parse(buf, len);
+ if (cert == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse X.509 "
+ "certificate");
+ return -1;
+ }
+
+ /* TODO
+ * verify key usage (must allow encryption)
+ *
+ * All certificate profiles, key and cryptographic formats are
+ * defined by the IETF PKIX working group [PKIX]. When a key
+ * usage extension is present, the digitalSignature bit must be
+ * set for the key to be eligible for signing, as described
+ * above, and the keyEncipherment bit must be present to allow
+ * encryption, as described above. The keyAgreement bit must be
+ * set on Diffie-Hellman certificates. (PKIX: RFC 3280)
+ */
+
+ *pk = crypto_public_key_import(cert->public_key, cert->public_key_len);
+ x509_certificate_free(cert);
+
+ if (*pk == NULL) {
+ wpa_printf(MSG_ERROR, "TLSv1: Failed to import "
+ "server public key");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int tls_verify_hash_init(struct tls_verify_hash *verify)
+{
+ tls_verify_hash_free(verify);
+ verify->md5_client = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+ verify->md5_server = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+ verify->md5_cert = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+ verify->sha1_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+ verify->sha1_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+ verify->sha1_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+ if (verify->md5_client == NULL || verify->md5_server == NULL ||
+ verify->md5_cert == NULL || verify->sha1_client == NULL ||
+ verify->sha1_server == NULL || verify->sha1_cert == NULL) {
+ tls_verify_hash_free(verify);
+ return -1;
+ }
+ return 0;
+}
+
+
+void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
+ size_t len)
+{
+ if (verify->md5_client && verify->sha1_client) {
+ crypto_hash_update(verify->md5_client, buf, len);
+ crypto_hash_update(verify->sha1_client, buf, len);
+ }
+ if (verify->md5_server && verify->sha1_server) {
+ crypto_hash_update(verify->md5_server, buf, len);
+ crypto_hash_update(verify->sha1_server, buf, len);
+ }
+ if (verify->md5_cert && verify->sha1_cert) {
+ crypto_hash_update(verify->md5_cert, buf, len);
+ crypto_hash_update(verify->sha1_cert, buf, len);
+ }
+}
+
+
+void tls_verify_hash_free(struct tls_verify_hash *verify)
+{
+ crypto_hash_finish(verify->md5_client, NULL, NULL);
+ crypto_hash_finish(verify->md5_server, NULL, NULL);
+ crypto_hash_finish(verify->md5_cert, NULL, NULL);
+ crypto_hash_finish(verify->sha1_client, NULL, NULL);
+ crypto_hash_finish(verify->sha1_server, NULL, NULL);
+ crypto_hash_finish(verify->sha1_cert, NULL, NULL);
+ verify->md5_client = NULL;
+ verify->md5_server = NULL;
+ verify->md5_cert = NULL;
+ verify->sha1_client = NULL;
+ verify->sha1_server = NULL;
+ verify->sha1_cert = NULL;
+}
diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h
new file mode 100644
index 0000000..763a4af
--- /dev/null
+++ b/src/tls/tlsv1_common.h
@@ -0,0 +1,216 @@
+/*
+ * TLSv1 common definitions
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef TLSV1_COMMON_H
+#define TLSV1_COMMON_H
+
+#include "crypto/crypto.h"
+
+#define TLS_VERSION 0x0301 /* TLSv1 */
+#define TLS_RANDOM_LEN 32
+#define TLS_PRE_MASTER_SECRET_LEN 48
+#define TLS_MASTER_SECRET_LEN 48
+#define TLS_SESSION_ID_MAX_LEN 32
+#define TLS_VERIFY_DATA_LEN 12
+
+/* HandshakeType */
+enum {
+ TLS_HANDSHAKE_TYPE_HELLO_REQUEST = 0,
+ TLS_HANDSHAKE_TYPE_CLIENT_HELLO = 1,
+ TLS_HANDSHAKE_TYPE_SERVER_HELLO = 2,
+ TLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET = 4 /* RFC 4507 */,
+ TLS_HANDSHAKE_TYPE_CERTIFICATE = 11,
+ TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE = 12,
+ TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST = 13,
+ TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE = 14,
+ TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY = 15,
+ TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE = 16,
+ TLS_HANDSHAKE_TYPE_FINISHED = 20,
+ TLS_HANDSHAKE_TYPE_CERTIFICATE_URL = 21 /* RFC 4366 */,
+ TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS = 22 /* RFC 4366 */
+};
+
+/* CipherSuite */
+#define TLS_NULL_WITH_NULL_NULL 0x0000 /* RFC 2246 */
+#define TLS_RSA_WITH_NULL_MD5 0x0001 /* RFC 2246 */
+#define TLS_RSA_WITH_NULL_SHA 0x0002 /* RFC 2246 */
+#define TLS_RSA_EXPORT_WITH_RC4_40_MD5 0x0003 /* RFC 2246 */
+#define TLS_RSA_WITH_RC4_128_MD5 0x0004 /* RFC 2246 */
+#define TLS_RSA_WITH_RC4_128_SHA 0x0005 /* RFC 2246 */
+#define TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 0x0006 /* RFC 2246 */
+#define TLS_RSA_WITH_IDEA_CBC_SHA 0x0007 /* RFC 2246 */
+#define TLS_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0008 /* RFC 2246 */
+#define TLS_RSA_WITH_DES_CBC_SHA 0x0009 /* RFC 2246 */
+#define TLS_RSA_WITH_3DES_EDE_CBC_SHA 0x000A /* RFC 2246 */
+#define TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA 0x000B /* RFC 2246 */
+#define TLS_DH_DSS_WITH_DES_CBC_SHA 0x000C /* RFC 2246 */
+#define TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA 0x000D /* RFC 2246 */
+#define TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA 0x000E /* RFC 2246 */
+#define TLS_DH_RSA_WITH_DES_CBC_SHA 0x000F /* RFC 2246 */
+#define TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA 0x0010 /* RFC 2246 */
+#define TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA 0x0011 /* RFC 2246 */
+#define TLS_DHE_DSS_WITH_DES_CBC_SHA 0x0012 /* RFC 2246 */
+#define TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA 0x0013 /* RFC 2246 */
+#define TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0014 /* RFC 2246 */
+#define TLS_DHE_RSA_WITH_DES_CBC_SHA 0x0015 /* RFC 2246 */
+#define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0x0016 /* RFC 2246 */
+#define TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 0x0017 /* RFC 2246 */
+#define TLS_DH_anon_WITH_RC4_128_MD5 0x0018 /* RFC 2246 */
+#define TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA 0x0019 /* RFC 2246 */
+#define TLS_DH_anon_WITH_DES_CBC_SHA 0x001A /* RFC 2246 */
+#define TLS_DH_anon_WITH_3DES_EDE_CBC_SHA 0x001B /* RFC 2246 */
+#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002F /* RFC 3268 */
+#define TLS_DH_DSS_WITH_AES_128_CBC_SHA 0x0030 /* RFC 3268 */
+#define TLS_DH_RSA_WITH_AES_128_CBC_SHA 0x0031 /* RFC 3268 */
+#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA 0x0032 /* RFC 3268 */
+#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033 /* RFC 3268 */
+#define TLS_DH_anon_WITH_AES_128_CBC_SHA 0x0034 /* RFC 3268 */
+#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 /* RFC 3268 */
+#define TLS_DH_DSS_WITH_AES_256_CBC_SHA 0x0036 /* RFC 3268 */
+#define TLS_DH_RSA_WITH_AES_256_CBC_SHA 0x0037 /* RFC 3268 */
+#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 /* RFC 3268 */
+#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 /* RFC 3268 */
+#define TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A /* RFC 3268 */
+
+/* CompressionMethod */
+#define TLS_COMPRESSION_NULL 0
+
+/* AlertLevel */
+#define TLS_ALERT_LEVEL_WARNING 1
+#define TLS_ALERT_LEVEL_FATAL 2
+
+/* AlertDescription */
+#define TLS_ALERT_CLOSE_NOTIFY 0
+#define TLS_ALERT_UNEXPECTED_MESSAGE 10
+#define TLS_ALERT_BAD_RECORD_MAC 20
+#define TLS_ALERT_DECRYPTION_FAILED 21
+#define TLS_ALERT_RECORD_OVERFLOW 22
+#define TLS_ALERT_DECOMPRESSION_FAILURE 30
+#define TLS_ALERT_HANDSHAKE_FAILURE 40
+#define TLS_ALERT_BAD_CERTIFICATE 42
+#define TLS_ALERT_UNSUPPORTED_CERTIFICATE 43
+#define TLS_ALERT_CERTIFICATE_REVOKED 44
+#define TLS_ALERT_CERTIFICATE_EXPIRED 45
+#define TLS_ALERT_CERTIFICATE_UNKNOWN 46
+#define TLS_ALERT_ILLEGAL_PARAMETER 47
+#define TLS_ALERT_UNKNOWN_CA 48
+#define TLS_ALERT_ACCESS_DENIED 49
+#define TLS_ALERT_DECODE_ERROR 50
+#define TLS_ALERT_DECRYPT_ERROR 51
+#define TLS_ALERT_EXPORT_RESTRICTION 60
+#define TLS_ALERT_PROTOCOL_VERSION 70
+#define TLS_ALERT_INSUFFICIENT_SECURITY 71
+#define TLS_ALERT_INTERNAL_ERROR 80
+#define TLS_ALERT_USER_CANCELED 90
+#define TLS_ALERT_NO_RENEGOTIATION 100
+#define TLS_ALERT_UNSUPPORTED_EXTENSION 110 /* RFC 4366 */
+#define TLS_ALERT_CERTIFICATE_UNOBTAINABLE 111 /* RFC 4366 */
+#define TLS_ALERT_UNRECOGNIZED_NAME 112 /* RFC 4366 */
+#define TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE 113 /* RFC 4366 */
+#define TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE 114 /* RFC 4366 */
+
+/* ChangeCipherSpec */
+enum {
+ TLS_CHANGE_CIPHER_SPEC = 1
+};
+
+/* TLS Extensions */
+#define TLS_EXT_SERVER_NAME 0 /* RFC 4366 */
+#define TLS_EXT_MAX_FRAGMENT_LENGTH 1 /* RFC 4366 */
+#define TLS_EXT_CLIENT_CERTIFICATE_URL 2 /* RFC 4366 */
+#define TLS_EXT_TRUSTED_CA_KEYS 3 /* RFC 4366 */
+#define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */
+#define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */
+#define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */
+
+#define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */
+
+
+typedef enum {
+ TLS_KEY_X_NULL,
+ TLS_KEY_X_RSA,
+ TLS_KEY_X_RSA_EXPORT,
+ TLS_KEY_X_DH_DSS_EXPORT,
+ TLS_KEY_X_DH_DSS,
+ TLS_KEY_X_DH_RSA_EXPORT,
+ TLS_KEY_X_DH_RSA,
+ TLS_KEY_X_DHE_DSS_EXPORT,
+ TLS_KEY_X_DHE_DSS,
+ TLS_KEY_X_DHE_RSA_EXPORT,
+ TLS_KEY_X_DHE_RSA,
+ TLS_KEY_X_DH_anon_EXPORT,
+ TLS_KEY_X_DH_anon
+} tls_key_exchange;
+
+typedef enum {
+ TLS_CIPHER_NULL,
+ TLS_CIPHER_RC4_40,
+ TLS_CIPHER_RC4_128,
+ TLS_CIPHER_RC2_CBC_40,
+ TLS_CIPHER_IDEA_CBC,
+ TLS_CIPHER_DES40_CBC,
+ TLS_CIPHER_DES_CBC,
+ TLS_CIPHER_3DES_EDE_CBC,
+ TLS_CIPHER_AES_128_CBC,
+ TLS_CIPHER_AES_256_CBC
+} tls_cipher;
+
+typedef enum {
+ TLS_HASH_NULL,
+ TLS_HASH_MD5,
+ TLS_HASH_SHA
+} tls_hash;
+
+struct tls_cipher_suite {
+ u16 suite;
+ tls_key_exchange key_exchange;
+ tls_cipher cipher;
+ tls_hash hash;
+};
+
+typedef enum {
+ TLS_CIPHER_STREAM,
+ TLS_CIPHER_BLOCK
+} tls_cipher_type;
+
+struct tls_cipher_data {
+ tls_cipher cipher;
+ tls_cipher_type type;
+ size_t key_material;
+ size_t expanded_key_material;
+ size_t block_size; /* also iv_size */
+ enum crypto_cipher_alg alg;
+};
+
+
+struct tls_verify_hash {
+ struct crypto_hash *md5_client;
+ struct crypto_hash *sha1_client;
+ struct crypto_hash *md5_server;
+ struct crypto_hash *sha1_server;
+ struct crypto_hash *md5_cert;
+ struct crypto_hash *sha1_cert;
+};
+
+
+const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite);
+const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher);
+int tls_server_key_exchange_allowed(tls_cipher cipher);
+int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk);
+int tls_verify_hash_init(struct tls_verify_hash *verify);
+void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
+ size_t len);
+void tls_verify_hash_free(struct tls_verify_hash *verify);
+
+#endif /* TLSV1_COMMON_H */
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
new file mode 100644
index 0000000..aa467ef
--- /dev/null
+++ b/src/tls/tlsv1_cred.c
@@ -0,0 +1,493 @@
+/*
+ * TLSv1 credentials
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "crypto/crypto.h"
+#include "x509v3.h"
+#include "tlsv1_cred.h"
+
+
+struct tlsv1_credentials * tlsv1_cred_alloc(void)
+{
+ struct tlsv1_credentials *cred;
+ cred = os_zalloc(sizeof(*cred));
+ return cred;
+}
+
+
+void tlsv1_cred_free(struct tlsv1_credentials *cred)
+{
+ if (cred == NULL)
+ return;
+
+ x509_certificate_chain_free(cred->trusted_certs);
+ x509_certificate_chain_free(cred->cert);
+ crypto_private_key_free(cred->key);
+ os_free(cred->dh_p);
+ os_free(cred->dh_g);
+ os_free(cred);
+}
+
+
+static int tlsv1_add_cert_der(struct x509_certificate **chain,
+ const u8 *buf, size_t len)
+{
+ struct x509_certificate *cert;
+ char name[128];
+
+ cert = x509_certificate_parse(buf, len);
+ if (cert == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate",
+ __func__);
+ return -1;
+ }
+
+ cert->next = *chain;
+ *chain = cert;
+
+ x509_name_string(&cert->subject, name, sizeof(name));
+ wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
+
+ return 0;
+}
+
+
+static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
+static const char *pem_cert_end = "-----END CERTIFICATE-----";
+static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_key_end = "-----END RSA PRIVATE KEY-----";
+static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----";
+static const char *pem_key2_end = "-----END PRIVATE KEY-----";
+static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
+static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----";
+
+
+static const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
+{
+ size_t i, plen;
+
+ plen = os_strlen(tag);
+ if (len < plen)
+ return NULL;
+
+ for (i = 0; i < len - plen; i++) {
+ if (os_memcmp(buf + i, tag, plen) == 0)
+ return buf + i;
+ }
+
+ return NULL;
+}
+
+
+static int tlsv1_add_cert(struct x509_certificate **chain,
+ const u8 *buf, size_t len)
+{
+ const u8 *pos, *end;
+ unsigned char *der;
+ size_t der_len;
+
+ pos = search_tag(pem_cert_begin, buf, len);
+ if (!pos) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
+ "assume DER format");
+ return tlsv1_add_cert_der(chain, buf, len);
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
+ "DER format");
+
+ while (pos) {
+ pos += os_strlen(pem_cert_begin);
+ end = search_tag(pem_cert_end, pos, buf + len - pos);
+ if (end == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
+ "certificate end tag (%s)", pem_cert_end);
+ return -1;
+ }
+
+ der = base64_decode(pos, end - pos, &der_len);
+ if (der == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
+ "certificate");
+ return -1;
+ }
+
+ if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
+ wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
+ "certificate after DER conversion");
+ os_free(der);
+ return -1;
+ }
+
+ os_free(der);
+
+ end += os_strlen(pem_cert_end);
+ pos = search_tag(pem_cert_begin, end, buf + len - end);
+ }
+
+ return 0;
+}
+
+
+static int tlsv1_set_cert_chain(struct x509_certificate **chain,
+ const char *cert, const u8 *cert_blob,
+ size_t cert_blob_len)
+{
+ if (cert_blob)
+ return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
+
+ if (cert) {
+ u8 *buf;
+ size_t len;
+ int ret;
+
+ buf = (u8 *) os_readfile(cert, &len);
+ if (buf == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+ cert);
+ return -1;
+ }
+
+ ret = tlsv1_add_cert(chain, buf, len);
+ os_free(buf);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_set_ca_cert - Set trusted CA certificate(s)
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @cert: File or reference name for X.509 certificate in PEM or DER format
+ * @cert_blob: cert as inlined data or %NULL if not used
+ * @cert_blob_len: ca_cert_blob length
+ * @path: Path to CA certificates (not yet supported)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
+ const u8 *cert_blob, size_t cert_blob_len,
+ const char *path)
+{
+ if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
+ cert_blob, cert_blob_len) < 0)
+ return -1;
+
+ if (path) {
+ /* TODO: add support for reading number of certificate files */
+ wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
+ "not yet supported");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_set_cert - Set certificate
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @cert: File or reference name for X.509 certificate in PEM or DER format
+ * @cert_blob: cert as inlined data or %NULL if not used
+ * @cert_blob_len: cert_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
+ const u8 *cert_blob, size_t cert_blob_len)
+{
+ return tlsv1_set_cert_chain(&cred->cert, cert,
+ cert_blob, cert_blob_len);
+}
+
+
+static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
+{
+ const u8 *pos, *end;
+ unsigned char *der;
+ size_t der_len;
+ struct crypto_private_key *pkey;
+
+ pos = search_tag(pem_key_begin, key, len);
+ if (!pos) {
+ pos = search_tag(pem_key2_begin, key, len);
+ if (!pos)
+ return NULL;
+ pos += os_strlen(pem_key2_begin);
+ end = search_tag(pem_key2_end, pos, key + len - pos);
+ if (!end)
+ return NULL;
+ } else {
+ pos += os_strlen(pem_key_begin);
+ end = search_tag(pem_key_end, pos, key + len - pos);
+ if (!end)
+ return NULL;
+ }
+
+ der = base64_decode(pos, end - pos, &der_len);
+ if (!der)
+ return NULL;
+ pkey = crypto_private_key_import(der, der_len, NULL);
+ os_free(der);
+ return pkey;
+}
+
+
+static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key,
+ size_t len,
+ const char *passwd)
+{
+ const u8 *pos, *end;
+ unsigned char *der;
+ size_t der_len;
+ struct crypto_private_key *pkey;
+
+ if (passwd == NULL)
+ return NULL;
+ pos = search_tag(pem_key_enc_begin, key, len);
+ if (!pos)
+ return NULL;
+ pos += os_strlen(pem_key_enc_begin);
+ end = search_tag(pem_key_enc_end, pos, key + len - pos);
+ if (!end)
+ return NULL;
+
+ der = base64_decode(pos, end - pos, &der_len);
+ if (!der)
+ return NULL;
+ pkey = crypto_private_key_import(der, der_len, passwd);
+ os_free(der);
+ return pkey;
+}
+
+
+static int tlsv1_set_key(struct tlsv1_credentials *cred,
+ const u8 *key, size_t len, const char *passwd)
+{
+ cred->key = crypto_private_key_import(key, len, passwd);
+ if (cred->key == NULL)
+ cred->key = tlsv1_set_key_pem(key, len);
+ if (cred->key == NULL)
+ cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
+ if (cred->key == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * tlsv1_set_private_key - Set private key
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @private_key: File or reference name for the key in PEM or DER format
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ * @private_key_blob: private_key as inlined data or %NULL if not used
+ * @private_key_blob_len: private_key_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_private_key(struct tlsv1_credentials *cred,
+ const char *private_key,
+ const char *private_key_passwd,
+ const u8 *private_key_blob,
+ size_t private_key_blob_len)
+{
+ crypto_private_key_free(cred->key);
+ cred->key = NULL;
+
+ if (private_key_blob)
+ return tlsv1_set_key(cred, private_key_blob,
+ private_key_blob_len,
+ private_key_passwd);
+
+ if (private_key) {
+ u8 *buf;
+ size_t len;
+ int ret;
+
+ buf = (u8 *) os_readfile(private_key, &len);
+ if (buf == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+ private_key);
+ return -1;
+ }
+
+ ret = tlsv1_set_key(cred, buf, len, private_key_passwd);
+ os_free(buf);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
+ const u8 *dh, size_t len)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+
+ pos = dh;
+ end = dh + len;
+
+ /*
+ * DHParameter ::= SEQUENCE {
+ * prime INTEGER, -- p
+ * base INTEGER, -- g
+ * privateValueLength INTEGER OPTIONAL }
+ */
+
+ /* DHParamer ::= SEQUENCE */
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
+ "valid SEQUENCE - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+
+ /* prime INTEGER */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0)
+ return -1;
+
+ if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
+ "class=%d tag=0x%x", hdr.class, hdr.tag);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
+ if (hdr.length == 0)
+ return -1;
+ os_free(cred->dh_p);
+ cred->dh_p = os_malloc(hdr.length);
+ if (cred->dh_p == NULL)
+ return -1;
+ os_memcpy(cred->dh_p, hdr.payload, hdr.length);
+ cred->dh_p_len = hdr.length;
+ pos = hdr.payload + hdr.length;
+
+ /* base INTEGER */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0)
+ return -1;
+
+ if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
+ "class=%d tag=0x%x", hdr.class, hdr.tag);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
+ if (hdr.length == 0)
+ return -1;
+ os_free(cred->dh_g);
+ cred->dh_g = os_malloc(hdr.length);
+ if (cred->dh_g == NULL)
+ return -1;
+ os_memcpy(cred->dh_g, hdr.payload, hdr.length);
+ cred->dh_g_len = hdr.length;
+
+ return 0;
+}
+
+
+static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
+static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
+
+
+static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
+ const u8 *buf, size_t len)
+{
+ const u8 *pos, *end;
+ unsigned char *der;
+ size_t der_len;
+
+ pos = search_tag(pem_dhparams_begin, buf, len);
+ if (!pos) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
+ "assume DER format");
+ return tlsv1_set_dhparams_der(cred, buf, len);
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
+ "format");
+
+ pos += os_strlen(pem_dhparams_begin);
+ end = search_tag(pem_dhparams_end, pos, buf + len - pos);
+ if (end == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
+ "tag (%s)", pem_dhparams_end);
+ return -1;
+ }
+
+ der = base64_decode(pos, end - pos, &der_len);
+ if (der == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
+ return -1;
+ }
+
+ if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
+ wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
+ "DER conversion");
+ os_free(der);
+ return -1;
+ }
+
+ os_free(der);
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_set_dhparams - Set Diffie-Hellman parameters
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @dh_file: File or reference name for the DH params in PEM or DER format
+ * @dh_blob: DH params as inlined data or %NULL if not used
+ * @dh_blob_len: dh_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
+ const u8 *dh_blob, size_t dh_blob_len)
+{
+ if (dh_blob)
+ return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
+
+ if (dh_file) {
+ u8 *buf;
+ size_t len;
+ int ret;
+
+ buf = (u8 *) os_readfile(dh_file, &len);
+ if (buf == NULL) {
+ wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+ dh_file);
+ return -1;
+ }
+
+ ret = tlsv1_set_dhparams_blob(cred, buf, len);
+ os_free(buf);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/src/tls/tlsv1_cred.h b/src/tls/tlsv1_cred.h
new file mode 100644
index 0000000..8425fe4
--- /dev/null
+++ b/src/tls/tlsv1_cred.h
@@ -0,0 +1,46 @@
+/*
+ * TLSv1 credentials
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef TLSV1_CRED_H
+#define TLSV1_CRED_H
+
+struct tlsv1_credentials {
+ struct x509_certificate *trusted_certs;
+ struct x509_certificate *cert;
+ struct crypto_private_key *key;
+
+ /* Diffie-Hellman parameters */
+ u8 *dh_p; /* prime */
+ size_t dh_p_len;
+ u8 *dh_g; /* generator */
+ size_t dh_g_len;
+};
+
+
+struct tlsv1_credentials * tlsv1_cred_alloc(void);
+void tlsv1_cred_free(struct tlsv1_credentials *cred);
+int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
+ const u8 *cert_blob, size_t cert_blob_len,
+ const char *path);
+int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
+ const u8 *cert_blob, size_t cert_blob_len);
+int tlsv1_set_private_key(struct tlsv1_credentials *cred,
+ const char *private_key,
+ const char *private_key_passwd,
+ const u8 *private_key_blob,
+ size_t private_key_blob_len);
+int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
+ const u8 *dh_blob, size_t dh_blob_len);
+
+#endif /* TLSV1_CRED_H */
diff --git a/src/tls/tlsv1_record.c b/src/tls/tlsv1_record.c
new file mode 100644
index 0000000..e811f0e
--- /dev/null
+++ b/src/tls/tlsv1_record.c
@@ -0,0 +1,409 @@
+/*
+ * TLSv1 Record Protocol
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+
+
+/**
+ * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite
+ * @rl: Pointer to TLS record layer data
+ * @cipher_suite: New cipher suite
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to prepare TLS record layer for cipher suite change.
+ * tlsv1_record_change_write_cipher() and
+ * tlsv1_record_change_read_cipher() functions can then be used to change the
+ * currently used ciphers.
+ */
+int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
+ u16 cipher_suite)
+{
+ const struct tls_cipher_suite *suite;
+ const struct tls_cipher_data *data;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x",
+ cipher_suite);
+ rl->cipher_suite = cipher_suite;
+
+ suite = tls_get_cipher_suite(cipher_suite);
+ if (suite == NULL)
+ return -1;
+
+ if (suite->hash == TLS_HASH_MD5) {
+ rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5;
+ rl->hash_size = MD5_MAC_LEN;
+ } else if (suite->hash == TLS_HASH_SHA) {
+ rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1;
+ rl->hash_size = SHA1_MAC_LEN;
+ }
+
+ data = tls_get_cipher_data(suite->cipher);
+ if (data == NULL)
+ return -1;
+
+ rl->key_material_len = data->key_material;
+ rl->iv_size = data->block_size;
+ rl->cipher_alg = data->alg;
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher
+ * @rl: Pointer to TLS record layer data
+ * Returns: 0 on success (cipher changed), -1 on failure
+ *
+ * This function changes TLS record layer to use the new cipher suite
+ * configured with tlsv1_record_set_cipher_suite() for writing.
+ */
+int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl)
+{
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite "
+ "0x%04x", rl->cipher_suite);
+ rl->write_cipher_suite = rl->cipher_suite;
+ os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN);
+
+ if (rl->write_cbc) {
+ crypto_cipher_deinit(rl->write_cbc);
+ rl->write_cbc = NULL;
+ }
+ if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
+ rl->write_cbc = crypto_cipher_init(rl->cipher_alg,
+ rl->write_iv, rl->write_key,
+ rl->key_material_len);
+ if (rl->write_cbc == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
+ "cipher");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher
+ * @rl: Pointer to TLS record layer data
+ * Returns: 0 on success (cipher changed), -1 on failure
+ *
+ * This function changes TLS record layer to use the new cipher suite
+ * configured with tlsv1_record_set_cipher_suite() for reading.
+ */
+int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
+{
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite "
+ "0x%04x", rl->cipher_suite);
+ rl->read_cipher_suite = rl->cipher_suite;
+ os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN);
+
+ if (rl->read_cbc) {
+ crypto_cipher_deinit(rl->read_cbc);
+ rl->read_cbc = NULL;
+ }
+ if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
+ rl->read_cbc = crypto_cipher_init(rl->cipher_alg,
+ rl->read_iv, rl->read_key,
+ rl->key_material_len);
+ if (rl->read_cbc == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
+ "cipher");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_record_send - TLS record layer: Send a message
+ * @rl: Pointer to TLS record layer data
+ * @content_type: Content type (TLS_CONTENT_TYPE_*)
+ * @buf: Buffer to send (with TLS_RECORD_HEADER_LEN octets reserved in the
+ * beginning for record layer to fill in; payload filled in after this and
+ * extra space in the end for HMAC).
+ * @buf_size: Maximum buf size
+ * @payload_len: Length of the payload
+ * @out_len: Buffer for returning the used buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function fills in the TLS record layer header, adds HMAC, and encrypts
+ * the data using the current write cipher.
+ */
+int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
+ size_t buf_size, size_t payload_len, size_t *out_len)
+{
+ u8 *pos, *ct_start, *length, *payload;
+ struct crypto_hash *hmac;
+ size_t clen;
+
+ pos = buf;
+ /* ContentType type */
+ ct_start = pos;
+ *pos++ = content_type;
+ /* ProtocolVersion version */
+ WPA_PUT_BE16(pos, TLS_VERSION);
+ pos += 2;
+ /* uint16 length */
+ length = pos;
+ WPA_PUT_BE16(length, payload_len);
+ pos += 2;
+
+ /* opaque fragment[TLSPlaintext.length] */
+ payload = pos;
+ pos += payload_len;
+
+ if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+ hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret,
+ rl->hash_size);
+ if (hmac == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+ "to initialize HMAC");
+ return -1;
+ }
+ crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN);
+ /* type + version + length + fragment */
+ crypto_hash_update(hmac, ct_start, pos - ct_start);
+ clen = buf + buf_size - pos;
+ if (clen < rl->hash_size) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not "
+ "enough room for MAC");
+ crypto_hash_finish(hmac, NULL, NULL);
+ return -1;
+ }
+
+ if (crypto_hash_finish(hmac, pos, &clen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+ "to calculate HMAC");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC",
+ pos, clen);
+ pos += clen;
+ if (rl->iv_size) {
+ size_t len = pos - payload;
+ size_t pad;
+ pad = (len + 1) % rl->iv_size;
+ if (pad)
+ pad = rl->iv_size - pad;
+ if (pos + pad + 1 > buf + buf_size) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No room for "
+ "block cipher padding");
+ return -1;
+ }
+ os_memset(pos, pad, pad + 1);
+ pos += pad + 1;
+ }
+
+ if (crypto_cipher_encrypt(rl->write_cbc, payload,
+ payload, pos - payload) < 0)
+ return -1;
+ }
+
+ WPA_PUT_BE16(length, pos - length - 2);
+ inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN);
+
+ *out_len = pos - buf;
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_record_receive - TLS record layer: Process a received message
+ * @rl: Pointer to TLS record layer data
+ * @in_data: Received data
+ * @in_len: Length of the received data
+ * @out_data: Buffer for output data (must be at least as long as in_data)
+ * @out_len: Set to maximum out_data length by caller; used to return the
+ * length of the used data
+ * @alert: Buffer for returning an alert value on failure
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function decrypts the received message, verifies HMAC and TLS record
+ * layer header.
+ */
+int tlsv1_record_receive(struct tlsv1_record_layer *rl,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t *out_len, u8 *alert)
+{
+ size_t i, rlen, hlen;
+ u8 padlen;
+ struct crypto_hash *hmac;
+ u8 len[2], hash[100];
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+ in_data, in_len);
+
+ if (in_len < TLS_RECORD_HEADER_LEN) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu)",
+ (unsigned long) in_len);
+ *alert = TLS_ALERT_DECODE_ERROR;
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d "
+ "length %d", in_data[0], in_data[1], in_data[2],
+ WPA_GET_BE16(in_data + 3));
+
+ if (in_data[0] != TLS_CONTENT_TYPE_HANDSHAKE &&
+ in_data[0] != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
+ in_data[0] != TLS_CONTENT_TYPE_ALERT &&
+ in_data[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type 0x%x",
+ in_data[0]);
+ *alert = TLS_ALERT_UNEXPECTED_MESSAGE;
+ return -1;
+ }
+
+ if (WPA_GET_BE16(in_data + 1) != TLS_VERSION) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version "
+ "%d.%d", in_data[1], in_data[2]);
+ *alert = TLS_ALERT_PROTOCOL_VERSION;
+ return -1;
+ }
+
+ rlen = WPA_GET_BE16(in_data + 3);
+
+ /* TLSCiphertext must not be more than 2^14+2048 bytes */
+ if (TLS_RECORD_HEADER_LEN + rlen > 18432) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
+ (unsigned long) (TLS_RECORD_HEADER_LEN + rlen));
+ *alert = TLS_ALERT_RECORD_OVERFLOW;
+ return -1;
+ }
+
+ in_data += TLS_RECORD_HEADER_LEN;
+ in_len -= TLS_RECORD_HEADER_LEN;
+
+ if (rlen > in_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included "
+ "(rlen=%lu > in_len=%lu)",
+ (unsigned long) rlen, (unsigned long) in_len);
+ *alert = TLS_ALERT_DECODE_ERROR;
+ return -1;
+ }
+
+ in_len = rlen;
+
+ if (*out_len < in_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for "
+ "processing received record");
+ *alert = TLS_ALERT_INTERNAL_ERROR;
+ return -1;
+ }
+
+ os_memcpy(out_data, in_data, in_len);
+ *out_len = in_len;
+
+ if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+ if (crypto_cipher_decrypt(rl->read_cbc, out_data,
+ out_data, in_len) < 0) {
+ *alert = TLS_ALERT_DECRYPTION_FAILED;
+ return -1;
+ }
+ if (rl->iv_size) {
+ if (in_len == 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short record"
+ " (no pad)");
+ *alert = TLS_ALERT_DECODE_ERROR;
+ return -1;
+ }
+ padlen = out_data[in_len - 1];
+ if (padlen >= in_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad "
+ "length (%u, in_len=%lu) in "
+ "received record",
+ padlen, (unsigned long) in_len);
+ *alert = TLS_ALERT_DECRYPTION_FAILED;
+ return -1;
+ }
+ for (i = in_len - padlen; i < in_len; i++) {
+ if (out_data[i] != padlen) {
+ wpa_hexdump(MSG_DEBUG,
+ "TLSv1: Invalid pad in "
+ "received record",
+ out_data + in_len - padlen,
+ padlen);
+ *alert = TLS_ALERT_DECRYPTION_FAILED;
+ return -1;
+ }
+ }
+
+ *out_len -= padlen + 1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP,
+ "TLSv1: Record Layer - Decrypted data",
+ out_data, in_len);
+
+ if (*out_len < rl->hash_size) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
+ "hash value");
+ *alert = TLS_ALERT_INTERNAL_ERROR;
+ return -1;
+ }
+
+ *out_len -= rl->hash_size;
+
+ hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret,
+ rl->hash_size);
+ if (hmac == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+ "to initialize HMAC");
+ *alert = TLS_ALERT_INTERNAL_ERROR;
+ return -1;
+ }
+
+ crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN);
+ /* type + version + length + fragment */
+ crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3);
+ WPA_PUT_BE16(len, *out_len);
+ crypto_hash_update(hmac, len, 2);
+ crypto_hash_update(hmac, out_data, *out_len);
+ hlen = sizeof(hash);
+ if (crypto_hash_finish(hmac, hash, &hlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+ "to calculate HMAC");
+ return -1;
+ }
+ if (hlen != rl->hash_size ||
+ os_memcmp(hash, out_data + *out_len, hlen) != 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
+ "received message");
+ *alert = TLS_ALERT_BAD_RECORD_MAC;
+ return -1;
+ }
+ }
+
+ /* TLSCompressed must not be more than 2^14+1024 bytes */
+ if (TLS_RECORD_HEADER_LEN + *out_len > 17408) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
+ (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len));
+ *alert = TLS_ALERT_RECORD_OVERFLOW;
+ return -1;
+ }
+
+ inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN);
+
+ return 0;
+}
diff --git a/src/tls/tlsv1_record.h b/src/tls/tlsv1_record.h
new file mode 100644
index 0000000..9c7c0a4
--- /dev/null
+++ b/src/tls/tlsv1_record.h
@@ -0,0 +1,74 @@
+/*
+ * TLSv1 Record Protocol
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef TLSV1_RECORD_H
+#define TLSV1_RECORD_H
+
+#include "crypto/crypto.h"
+
+#define TLS_MAX_WRITE_MAC_SECRET_LEN 20
+#define TLS_MAX_WRITE_KEY_LEN 32
+#define TLS_MAX_IV_LEN 16
+#define TLS_MAX_KEY_BLOCK_LEN (2 * (TLS_MAX_WRITE_MAC_SECRET_LEN + \
+ TLS_MAX_WRITE_KEY_LEN + TLS_MAX_IV_LEN))
+
+#define TLS_SEQ_NUM_LEN 8
+#define TLS_RECORD_HEADER_LEN 5
+
+/* ContentType */
+enum {
+ TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC = 20,
+ TLS_CONTENT_TYPE_ALERT = 21,
+ TLS_CONTENT_TYPE_HANDSHAKE = 22,
+ TLS_CONTENT_TYPE_APPLICATION_DATA = 23
+};
+
+struct tlsv1_record_layer {
+ u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
+ u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
+ u8 write_key[TLS_MAX_WRITE_KEY_LEN];
+ u8 read_key[TLS_MAX_WRITE_KEY_LEN];
+ u8 write_iv[TLS_MAX_IV_LEN];
+ u8 read_iv[TLS_MAX_IV_LEN];
+
+ size_t hash_size;
+ size_t key_material_len;
+ size_t iv_size; /* also block_size */
+
+ enum crypto_hash_alg hash_alg;
+ enum crypto_cipher_alg cipher_alg;
+
+ u8 write_seq_num[TLS_SEQ_NUM_LEN];
+ u8 read_seq_num[TLS_SEQ_NUM_LEN];
+
+ u16 cipher_suite;
+ u16 write_cipher_suite;
+ u16 read_cipher_suite;
+
+ struct crypto_cipher *write_cbc;
+ struct crypto_cipher *read_cbc;
+};
+
+
+int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
+ u16 cipher_suite);
+int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl);
+int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl);
+int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
+ size_t buf_size, size_t payload_len, size_t *out_len);
+int tlsv1_record_receive(struct tlsv1_record_layer *rl,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t *out_len, u8 *alert);
+
+#endif /* TLSV1_RECORD_H */
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
new file mode 100644
index 0000000..6a61235
--- /dev/null
+++ b/src/tls/tlsv1_server.c
@@ -0,0 +1,592 @@
+/*
+ * TLSv1 server (RFC 2246)
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_server.h"
+#include "tlsv1_server_i.h"
+
+/* TODO:
+ * Support for a message fragmented across several records (RFC 2246, 6.2.1)
+ */
+
+
+void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description)
+{
+ conn->alert_level = level;
+ conn->alert_description = description;
+}
+
+
+int tlsv1_server_derive_keys(struct tlsv1_server *conn,
+ const u8 *pre_master_secret,
+ size_t pre_master_secret_len)
+{
+ u8 seed[2 * TLS_RANDOM_LEN];
+ u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
+ u8 *pos;
+ size_t key_block_len;
+
+ if (pre_master_secret) {
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
+ pre_master_secret, pre_master_secret_len);
+ os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+ TLS_RANDOM_LEN);
+ if (tls_prf(pre_master_secret, pre_master_secret_len,
+ "master secret", seed, 2 * TLS_RANDOM_LEN,
+ conn->master_secret, TLS_MASTER_SECRET_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
+ "master_secret");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
+ conn->master_secret, TLS_MASTER_SECRET_LEN);
+ }
+
+ os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
+ key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+ conn->rl.iv_size);
+ if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "key expansion", seed, 2 * TLS_RANDOM_LEN,
+ key_block, key_block_len)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
+ key_block, key_block_len);
+
+ pos = key_block;
+
+ /* client_write_MAC_secret */
+ os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
+ pos += conn->rl.hash_size;
+ /* server_write_MAC_secret */
+ os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
+ pos += conn->rl.hash_size;
+
+ /* client_write_key */
+ os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
+ pos += conn->rl.key_material_len;
+ /* server_write_key */
+ os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
+ pos += conn->rl.key_material_len;
+
+ /* client_write_IV */
+ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+ pos += conn->rl.iv_size;
+ /* server_write_IV */
+ os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+ pos += conn->rl.iv_size;
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_server_handshake - Process TLS handshake
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ * Returns: Pointer to output data, %NULL on failure
+ */
+u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
+ const u8 *in_data, size_t in_len,
+ size_t *out_len)
+{
+ const u8 *pos, *end;
+ u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
+ size_t in_msg_len;
+
+ if (in_data == NULL || in_len == 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No input data to server");
+ return NULL;
+ }
+
+ pos = in_data;
+ end = in_data + in_len;
+ in_msg = os_malloc(in_len);
+ if (in_msg == NULL)
+ return NULL;
+
+ /* Each received packet may include multiple records */
+ while (pos < end) {
+ in_msg_len = in_len;
+ if (tlsv1_record_receive(&conn->rl, pos, end - pos,
+ in_msg, &in_msg_len, &alert)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
+ "record failed");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ goto failed;
+ }
+ ct = pos[0];
+
+ in_pos = in_msg;
+ in_end = in_msg + in_msg_len;
+
+ /* Each received record may include multiple messages of the
+ * same ContentType. */
+ while (in_pos < in_end) {
+ in_msg_len = in_end - in_pos;
+ if (tlsv1_server_process_handshake(conn, ct, in_pos,
+ &in_msg_len) < 0)
+ goto failed;
+ in_pos += in_msg_len;
+ }
+
+ pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+ }
+
+ os_free(in_msg);
+ in_msg = NULL;
+
+ msg = tlsv1_server_handshake_write(conn, out_len);
+
+failed:
+ os_free(in_msg);
+ if (conn->alert_level) {
+ if (conn->state == FAILED) {
+ /* Avoid alert loops */
+ wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop");
+ os_free(msg);
+ return NULL;
+ }
+ conn->state = FAILED;
+ os_free(msg);
+ msg = tlsv1_server_send_alert(conn, conn->alert_level,
+ conn->alert_description,
+ out_len);
+ }
+
+ return msg;
+}
+
+
+/**
+ * tlsv1_server_encrypt - Encrypt data into TLS tunnel
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel.
+ */
+int tlsv1_server_encrypt(struct tlsv1_server *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len)
+{
+ size_t rlen;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
+ in_data, in_len);
+
+ os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
+ out_data, out_len, in_len, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ return rlen;
+}
+
+
+/**
+ * tlsv1_server_decrypt - Decrypt data from TLS tunnel
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Pointer to input buffer (encrypted TLS data)
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel.
+ */
+int tlsv1_server_decrypt(struct tlsv1_server *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len)
+{
+ const u8 *in_end, *pos;
+ int res;
+ u8 alert, *out_end, *out_pos;
+ size_t olen;
+
+ pos = in_data;
+ in_end = in_data + in_len;
+ out_pos = out_data;
+ out_end = out_data + out_len;
+
+ while (pos < in_end) {
+ if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
+ "0x%x", pos[0]);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ olen = out_end - out_pos;
+ res = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+ out_pos, &olen, &alert);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
+ "failed");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ return -1;
+ }
+ out_pos += olen;
+ if (out_pos > out_end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
+ "for processing the received record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+ }
+
+ return out_pos - out_data;
+}
+
+
+/**
+ * tlsv1_server_global_init - Initialize TLSv1 server
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before using any other TLSv1 server functions.
+ */
+int tlsv1_server_global_init(void)
+{
+ return crypto_global_init();
+}
+
+
+/**
+ * tlsv1_server_global_deinit - Deinitialize TLSv1 server
+ *
+ * This function can be used to deinitialize the TLSv1 server that was
+ * initialized by calling tlsv1_server_global_init(). No TLSv1 server functions
+ * can be called after this before calling tlsv1_server_global_init() again.
+ */
+void tlsv1_server_global_deinit(void)
+{
+ crypto_global_deinit();
+}
+
+
+/**
+ * tlsv1_server_init - Initialize TLSv1 server connection
+ * @cred: Pointer to server credentials from tlsv1_server_cred_alloc()
+ * Returns: Pointer to TLSv1 server connection data or %NULL on failure
+ */
+struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred)
+{
+ struct tlsv1_server *conn;
+ size_t count;
+ u16 *suites;
+
+ conn = os_zalloc(sizeof(*conn));
+ if (conn == NULL)
+ return NULL;
+
+ conn->cred = cred;
+
+ conn->state = CLIENT_HELLO;
+
+ if (tls_verify_hash_init(&conn->verify) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
+ "hash");
+ os_free(conn);
+ return NULL;
+ }
+
+ count = 0;
+ suites = conn->cipher_suites;
+#ifndef CONFIG_CRYPTO_INTERNAL
+ suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+#endif /* CONFIG_CRYPTO_INTERNAL */
+ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+ suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+ conn->num_cipher_suites = count;
+
+ return conn;
+}
+
+
+static void tlsv1_server_clear_data(struct tlsv1_server *conn)
+{
+ tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+ tlsv1_record_change_write_cipher(&conn->rl);
+ tlsv1_record_change_read_cipher(&conn->rl);
+ tls_verify_hash_free(&conn->verify);
+
+ crypto_public_key_free(conn->client_rsa_key);
+ conn->client_rsa_key = NULL;
+
+ os_free(conn->session_ticket);
+ conn->session_ticket = NULL;
+ conn->session_ticket_len = 0;
+ conn->use_session_ticket = 0;
+
+ os_free(conn->dh_secret);
+ conn->dh_secret = NULL;
+ conn->dh_secret_len = 0;
+}
+
+
+/**
+ * tlsv1_server_deinit - Deinitialize TLSv1 server connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ */
+void tlsv1_server_deinit(struct tlsv1_server *conn)
+{
+ tlsv1_server_clear_data(conn);
+ os_free(conn);
+}
+
+
+/**
+ * tlsv1_server_established - Check whether connection has been established
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 1 if connection is established, 0 if not
+ */
+int tlsv1_server_established(struct tlsv1_server *conn)
+{
+ return conn->state == ESTABLISHED;
+}
+
+
+/**
+ * tlsv1_server_prf - Use TLS-PRF to derive keying material
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+ int server_random_first, u8 *out, size_t out_len)
+{
+ u8 seed[2 * TLS_RANDOM_LEN];
+
+ if (conn->state != ESTABLISHED)
+ return -1;
+
+ if (server_random_first) {
+ os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
+ TLS_RANDOM_LEN);
+ } else {
+ os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+ os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+ TLS_RANDOM_LEN);
+ }
+
+ return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+}
+
+
+/**
+ * tlsv1_server_get_cipher - Get current cipher name
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
+ size_t buflen)
+{
+ char *cipher;
+
+ switch (conn->rl.cipher_suite) {
+ case TLS_RSA_WITH_RC4_128_MD5:
+ cipher = "RC4-MD5";
+ break;
+ case TLS_RSA_WITH_RC4_128_SHA:
+ cipher = "RC4-SHA";
+ break;
+ case TLS_RSA_WITH_DES_CBC_SHA:
+ cipher = "DES-CBC-SHA";
+ break;
+ case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ cipher = "DES-CBC3-SHA";
+ break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+ cipher = "ADH-AES-128-SHA";
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ cipher = "AES-256-SHA";
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ cipher = "AES-128-SHA";
+ break;
+ default:
+ return -1;
+ }
+
+ if (os_strlcpy(buf, cipher, buflen) >= buflen)
+ return -1;
+ return 0;
+}
+
+
+/**
+ * tlsv1_server_shutdown - Shutdown TLS connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_shutdown(struct tlsv1_server *conn)
+{
+ conn->state = CLIENT_HELLO;
+
+ if (tls_verify_hash_init(&conn->verify) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
+ "hash");
+ return -1;
+ }
+
+ tlsv1_server_clear_data(conn);
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_server_resumed - Was session resumption used
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tlsv1_server_resumed(struct tlsv1_server *conn)
+{
+ return 0;
+}
+
+
+/**
+ * tlsv1_server_get_keys - Get master key and random data from TLS connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @keys: Structure of key/random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys)
+{
+ os_memset(keys, 0, sizeof(*keys));
+ if (conn->state == CLIENT_HELLO)
+ return -1;
+
+ keys->client_random = conn->client_random;
+ keys->client_random_len = TLS_RANDOM_LEN;
+
+ if (conn->state != SERVER_HELLO) {
+ keys->server_random = conn->server_random;
+ keys->server_random_len = TLS_RANDOM_LEN;
+ keys->master_key = conn->master_secret;
+ keys->master_key_len = TLS_MASTER_SECRET_LEN;
+ }
+
+ return 0;
+}
+
+
+/**
+ * tlsv1_server_get_keyblock_size - Get TLS key_block size
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn)
+{
+ if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
+ return -1;
+
+ return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+ conn->rl.iv_size);
+}
+
+
+/**
+ * tlsv1_server_set_cipher_list - Configure acceptable cipher suites
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers)
+{
+ size_t count;
+ u16 *suites;
+
+ /* TODO: implement proper configuration of cipher suites */
+ if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
+ count = 0;
+ suites = conn->cipher_suites;
+#ifndef CONFIG_CRYPTO_INTERNAL
+ suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+#endif /* CONFIG_CRYPTO_INTERNAL */
+ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+ suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+ suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+#ifndef CONFIG_CRYPTO_INTERNAL
+ suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
+#endif /* CONFIG_CRYPTO_INTERNAL */
+ suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+ suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+ suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
+ suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
+ conn->num_cipher_suites = count;
+ }
+
+ return 0;
+}
+
+
+int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer)
+{
+ conn->verify_peer = verify_peer;
+ return 0;
+}
+
+
+void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
+ tlsv1_server_session_ticket_cb cb,
+ void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
+ cb, ctx);
+ conn->session_ticket_cb = cb;
+ conn->session_ticket_cb_ctx = ctx;
+}
diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h
new file mode 100644
index 0000000..00c536c
--- /dev/null
+++ b/src/tls/tlsv1_server.h
@@ -0,0 +1,54 @@
+/*
+ * TLSv1 server (RFC 2246)
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef TLSV1_SERVER_H
+#define TLSV1_SERVER_H
+
+#include "tlsv1_cred.h"
+
+struct tlsv1_server;
+
+int tlsv1_server_global_init(void);
+void tlsv1_server_global_deinit(void);
+struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred);
+void tlsv1_server_deinit(struct tlsv1_server *conn);
+int tlsv1_server_established(struct tlsv1_server *conn);
+int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+ int server_random_first, u8 *out, size_t out_len);
+u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
+ const u8 *in_data, size_t in_len, size_t *out_len);
+int tlsv1_server_encrypt(struct tlsv1_server *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len);
+int tlsv1_server_decrypt(struct tlsv1_server *conn,
+ const u8 *in_data, size_t in_len,
+ u8 *out_data, size_t out_len);
+int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
+ size_t buflen);
+int tlsv1_server_shutdown(struct tlsv1_server *conn);
+int tlsv1_server_resumed(struct tlsv1_server *conn);
+int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys);
+int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn);
+int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers);
+int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer);
+
+typedef int (*tlsv1_server_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
+ tlsv1_server_session_ticket_cb cb,
+ void *ctx);
+
+#endif /* TLSV1_SERVER_H */
diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h
new file mode 100644
index 0000000..d11ea75
--- /dev/null
+++ b/src/tls/tlsv1_server_i.h
@@ -0,0 +1,77 @@
+/*
+ * TLSv1 server - internal structures
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef TLSV1_SERVER_I_H
+#define TLSV1_SERVER_I_H
+
+struct tlsv1_server {
+ enum {
+ CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE,
+ SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST,
+ SERVER_HELLO_DONE, CLIENT_CERTIFICATE, CLIENT_KEY_EXCHANGE,
+ CERTIFICATE_VERIFY, CHANGE_CIPHER_SPEC, CLIENT_FINISHED,
+ SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED,
+ ESTABLISHED, FAILED
+ } state;
+
+ struct tlsv1_record_layer rl;
+
+ u8 session_id[TLS_SESSION_ID_MAX_LEN];
+ size_t session_id_len;
+ u8 client_random[TLS_RANDOM_LEN];
+ u8 server_random[TLS_RANDOM_LEN];
+ u8 master_secret[TLS_MASTER_SECRET_LEN];
+
+ u8 alert_level;
+ u8 alert_description;
+
+ struct crypto_public_key *client_rsa_key;
+
+ struct tls_verify_hash verify;
+
+#define MAX_CIPHER_COUNT 30
+ u16 cipher_suites[MAX_CIPHER_COUNT];
+ size_t num_cipher_suites;
+
+ u16 cipher_suite;
+
+ struct tlsv1_credentials *cred;
+
+ int verify_peer;
+ u16 client_version;
+
+ u8 *session_ticket;
+ size_t session_ticket_len;
+
+ tlsv1_server_session_ticket_cb session_ticket_cb;
+ void *session_ticket_cb_ctx;
+
+ int use_session_ticket;
+
+ u8 *dh_secret;
+ size_t dh_secret_len;
+};
+
+
+void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description);
+int tlsv1_server_derive_keys(struct tlsv1_server *conn,
+ const u8 *pre_master_secret,
+ size_t pre_master_secret_len);
+u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len);
+u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
+ u8 description, size_t *out_len);
+int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
+ const u8 *buf, size_t *len);
+
+#endif /* TLSV1_SERVER_I_H */
diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c
new file mode 100644
index 0000000..49e811f
--- /dev/null
+++ b/src/tls/tlsv1_server_read.c
@@ -0,0 +1,1134 @@
+/*
+ * TLSv1 server - read handshake message
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_server.h"
+#include "tlsv1_server_i.h"
+
+
+static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
+ const u8 *in_data, size_t *in_len);
+static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
+ u8 ct, const u8 *in_data,
+ size_t *in_len);
+
+
+static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end, *c;
+ size_t left, len, i, j;
+ u16 cipher_suite;
+ u16 num_suites;
+ int compr_null_found;
+ u16 ext_type, ext_len;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4)
+ goto decode_error;
+
+ /* HandshakeType msg_type */
+ if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected ClientHello)", *pos);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello");
+ pos++;
+ /* uint24 length */
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left)
+ goto decode_error;
+
+ /* body - ClientHello */
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello", pos, len);
+ end = pos + len;
+
+ /* ProtocolVersion client_version */
+ if (end - pos < 2)
+ goto decode_error;
+ conn->client_version = WPA_GET_BE16(pos);
+ wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d",
+ conn->client_version >> 8, conn->client_version & 0xff);
+ if (conn->client_version < TLS_VERSION) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
+ "ClientHello");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_PROTOCOL_VERSION);
+ return -1;
+ }
+ pos += 2;
+
+ /* Random random */
+ if (end - pos < TLS_RANDOM_LEN)
+ goto decode_error;
+
+ os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN);
+ pos += TLS_RANDOM_LEN;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
+ conn->client_random, TLS_RANDOM_LEN);
+
+ /* SessionID session_id */
+ if (end - pos < 1)
+ goto decode_error;
+ if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
+ goto decode_error;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos);
+ pos += 1 + *pos;
+ /* TODO: add support for session resumption */
+
+ /* CipherSuite cipher_suites<2..2^16-1> */
+ if (end - pos < 2)
+ goto decode_error;
+ num_suites = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < num_suites)
+ goto decode_error;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites",
+ pos, num_suites);
+ if (num_suites & 1)
+ goto decode_error;
+ num_suites /= 2;
+
+ cipher_suite = 0;
+ for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) {
+ c = pos;
+ for (j = 0; j < num_suites; j++) {
+ u16 tmp = WPA_GET_BE16(c);
+ c += 2;
+ if (!cipher_suite && tmp == conn->cipher_suites[i]) {
+ cipher_suite = tmp;
+ break;
+ }
+ }
+ }
+ pos += num_suites * 2;
+ if (!cipher_suite) {
+ wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite "
+ "available");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_ILLEGAL_PARAMETER);
+ return -1;
+ }
+
+ if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
+ "record layer");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ conn->cipher_suite = cipher_suite;
+
+ /* CompressionMethod compression_methods<1..2^8-1> */
+ if (end - pos < 1)
+ goto decode_error;
+ num_suites = *pos++;
+ if (end - pos < num_suites)
+ goto decode_error;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods",
+ pos, num_suites);
+ compr_null_found = 0;
+ for (i = 0; i < num_suites; i++) {
+ if (*pos++ == TLS_COMPRESSION_NULL)
+ compr_null_found = 1;
+ }
+ if (!compr_null_found) {
+ wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL "
+ "compression");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_ILLEGAL_PARAMETER);
+ return -1;
+ }
+
+ if (end - pos == 1) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the "
+ "end of ClientHello: 0x%02x", *pos);
+ goto decode_error;
+ }
+
+ if (end - pos >= 2) {
+ /* Extension client_hello_extension_list<0..2^16-1> */
+ ext_len = WPA_GET_BE16(pos);
+ pos += 2;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello "
+ "extensions", ext_len);
+ if (end - pos != ext_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello "
+ "extension list length %u (expected %u)",
+ ext_len, (unsigned int) (end - pos));
+ goto decode_error;
+ }
+
+ /*
+ * struct {
+ * ExtensionType extension_type (0..65535)
+ * opaque extension_data<0..2^16-1>
+ * } Extension;
+ */
+
+ while (pos < end) {
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
+ "extension_type field");
+ goto decode_error;
+ }
+
+ ext_type = WPA_GET_BE16(pos);
+ pos += 2;
+
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
+ "extension_data length field");
+ goto decode_error;
+ }
+
+ ext_len = WPA_GET_BE16(pos);
+ pos += 2;
+
+ if (end - pos < ext_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
+ "extension_data field");
+ goto decode_error;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension "
+ "type %u", ext_type);
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello "
+ "Extension data", pos, ext_len);
+
+ if (ext_type == TLS_EXT_SESSION_TICKET) {
+ os_free(conn->session_ticket);
+ conn->session_ticket = os_malloc(ext_len);
+ if (conn->session_ticket) {
+ os_memcpy(conn->session_ticket, pos,
+ ext_len);
+ conn->session_ticket_len = ext_len;
+ }
+ }
+
+ pos += ext_len;
+ }
+ }
+
+ *in_len = end - in_data;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to "
+ "ServerHello");
+ conn->state = SERVER_HELLO;
+
+ return 0;
+
+decode_error:
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+}
+
+
+static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len, list_len, cert_len, idx;
+ u8 type;
+ struct x509_certificate *chain = NULL, *last = NULL, *cert;
+ int reason;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
+ "(len=%lu)", (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
+ "length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
+ if (conn->verify_peer) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
+ "Certificate");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ return tls_process_client_key_exchange(conn, ct, in_data,
+ in_len);
+ }
+ if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected Certificate/"
+ "ClientKeyExchange)", type);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "TLSv1: Received Certificate (certificate_list len %lu)",
+ (unsigned long) len);
+
+ /*
+ * opaque ASN.1Cert<2^24-1>;
+ *
+ * struct {
+ * ASN.1Cert certificate_list<1..2^24-1>;
+ * } Certificate;
+ */
+
+ end = pos + len;
+
+ if (end - pos < 3) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
+ "(left=%lu)", (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ list_len = WPA_GET_BE24(pos);
+ pos += 3;
+
+ if ((size_t) (end - pos) != list_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
+ "length (len=%lu left=%lu)",
+ (unsigned long) list_len,
+ (unsigned long) (end - pos));
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ idx = 0;
+ while (pos < end) {
+ if (end - pos < 3) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+ "certificate_list");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ cert_len = WPA_GET_BE24(pos);
+ pos += 3;
+
+ if ((size_t) (end - pos) < cert_len) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
+ "length (len=%lu left=%lu)",
+ (unsigned long) cert_len,
+ (unsigned long) (end - pos));
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
+ (unsigned long) idx, (unsigned long) cert_len);
+
+ if (idx == 0) {
+ crypto_public_key_free(conn->client_rsa_key);
+ if (tls_parse_cert(pos, cert_len,
+ &conn->client_rsa_key)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+ "the certificate");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_BAD_CERTIFICATE);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+ }
+
+ cert = x509_certificate_parse(pos, cert_len);
+ if (cert == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+ "the certificate");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_BAD_CERTIFICATE);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ if (last == NULL)
+ chain = cert;
+ else
+ last->next = cert;
+ last = cert;
+
+ idx++;
+ pos += cert_len;
+ }
+
+ if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
+ &reason) < 0) {
+ int tls_reason;
+ wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
+ "validation failed (reason=%d)", reason);
+ switch (reason) {
+ case X509_VALIDATE_BAD_CERTIFICATE:
+ tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+ break;
+ case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
+ tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
+ break;
+ case X509_VALIDATE_CERTIFICATE_REVOKED:
+ tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+ break;
+ case X509_VALIDATE_CERTIFICATE_EXPIRED:
+ tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+ break;
+ case X509_VALIDATE_CERTIFICATE_UNKNOWN:
+ tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
+ break;
+ case X509_VALIDATE_UNKNOWN_CA:
+ tls_reason = TLS_ALERT_UNKNOWN_CA;
+ break;
+ default:
+ tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+ break;
+ }
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
+ x509_certificate_chain_free(chain);
+
+ *in_len = end - in_data;
+
+ conn->state = CLIENT_KEY_EXCHANGE;
+
+ return 0;
+}
+
+
+static int tls_process_client_key_exchange_rsa(
+ struct tlsv1_server *conn, const u8 *pos, const u8 *end)
+{
+ u8 *out;
+ size_t outlen, outbuflen;
+ u16 encr_len;
+ int res;
+ int use_random = 0;
+
+ if (end - pos < 2) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ encr_len = WPA_GET_BE16(pos);
+ pos += 2;
+
+ outbuflen = outlen = end - pos;
+ out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ?
+ outlen : TLS_PRE_MASTER_SECRET_LEN);
+ if (out == NULL) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ /*
+ * struct {
+ * ProtocolVersion client_version;
+ * opaque random[46];
+ * } PreMasterSecret;
+ *
+ * struct {
+ * public-key-encrypted PreMasterSecret pre_master_secret;
+ * } EncryptedPreMasterSecret;
+ */
+
+ /*
+ * Note: To avoid Bleichenbacher attack, we do not report decryption or
+ * parsing errors from EncryptedPreMasterSecret processing to the
+ * client. Instead, a random pre-master secret is used to force the
+ * handshake to fail.
+ */
+
+ if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key,
+ pos, end - pos,
+ out, &outlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt "
+ "PreMasterSecret (encr_len=%d outlen=%lu)",
+ (int) (end - pos), (unsigned long) outlen);
+ use_random = 1;
+ }
+
+ if (outlen != TLS_PRE_MASTER_SECRET_LEN) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret "
+ "length %lu", (unsigned long) outlen);
+ use_random = 1;
+ }
+
+ if (WPA_GET_BE16(out) != conn->client_version) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Client version in "
+ "ClientKeyExchange does not match with version in "
+ "ClientHello");
+ use_random = 1;
+ }
+
+ if (use_random) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Using random premaster secret "
+ "to avoid revealing information about private key");
+ outlen = TLS_PRE_MASTER_SECRET_LEN;
+ if (os_get_random(out, outlen)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+ "data");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(out);
+ return -1;
+ }
+ }
+
+ res = tlsv1_server_derive_keys(conn, out, outlen);
+
+ /* Clear the pre-master secret since it is not needed anymore */
+ os_memset(out, 0, outbuflen);
+ os_free(out);
+
+ if (res) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int tls_process_client_key_exchange_dh_anon(
+ struct tlsv1_server *conn, const u8 *pos, const u8 *end)
+{
+ const u8 *dh_yc;
+ u16 dh_yc_len;
+ u8 *shared;
+ size_t shared_len;
+ int res;
+
+ /*
+ * struct {
+ * select (PublicValueEncoding) {
+ * case implicit: struct { };
+ * case explicit: opaque dh_Yc<1..2^16-1>;
+ * } dh_public;
+ * } ClientDiffieHellmanPublic;
+ */
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic",
+ pos, end - pos);
+
+ if (end == pos) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Implicit public value encoding "
+ "not supported");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ if (end - pos < 3) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value "
+ "length");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ dh_yc_len = WPA_GET_BE16(pos);
+ dh_yc = pos + 2;
+
+ if (dh_yc + dh_yc_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow "
+ "(length %d)", dh_yc_len);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
+ dh_yc, dh_yc_len);
+
+ if (conn->cred == NULL || conn->cred->dh_p == NULL ||
+ conn->dh_secret == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ shared_len = conn->cred->dh_p_len;
+ shared = os_malloc(shared_len);
+ if (shared == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
+ "DH");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ /* shared = Yc^secret mod p */
+ if (crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret,
+ conn->dh_secret_len,
+ conn->cred->dh_p, conn->cred->dh_p_len,
+ shared, &shared_len)) {
+ os_free(shared);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
+ shared, shared_len);
+
+ os_memset(conn->dh_secret, 0, conn->dh_secret_len);
+ os_free(conn->dh_secret);
+ conn->dh_secret = NULL;
+
+ res = tlsv1_server_derive_keys(conn, shared, shared_len);
+
+ /* Clear the pre-master secret since it is not needed anymore */
+ os_memset(shared, 0, shared_len);
+ os_free(shared);
+
+ if (res) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len;
+ u8 type;
+ tls_key_exchange keyx;
+ const struct tls_cipher_suite *suite;
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange "
+ "(Left=%lu)", (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange "
+ "length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ end = pos + len;
+
+ if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected ClientKeyExchange)", type);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange");
+
+ wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len);
+
+ suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+ if (suite == NULL)
+ keyx = TLS_KEY_X_NULL;
+ else
+ keyx = suite->key_exchange;
+
+ if (keyx == TLS_KEY_X_DH_anon &&
+ tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0)
+ return -1;
+
+ if (keyx != TLS_KEY_X_DH_anon &&
+ tls_process_client_key_exchange_rsa(conn, pos, end) < 0)
+ return -1;
+
+ *in_len = end - in_data;
+
+ conn->state = CERTIFICATE_VERIFY;
+
+ return 0;
+}
+
+
+static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len;
+ u8 type;
+ size_t hlen, buflen;
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf;
+ enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+ u16 slen;
+
+ if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+ if (conn->verify_peer) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
+ "CertificateVerify");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ return tls_process_change_cipher_spec(conn, ct, in_data,
+ in_len);
+ }
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+ "received content type 0x%x", ct);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify "
+ "message (len=%lu)", (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ type = *pos++;
+ len = WPA_GET_BE24(pos);
+ pos += 3;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify "
+ "message length (len=%lu != left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ end = pos + len;
+
+ if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+ "message %d (expected CertificateVerify)", type);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify");
+
+ /*
+ * struct {
+ * Signature signature;
+ * } CertificateVerify;
+ */
+
+ hpos = hash;
+
+ if (alg == SIGN_ALG_RSA) {
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_cert == NULL ||
+ crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
+ {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_cert = NULL;
+ crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+ conn->verify.sha1_cert = NULL;
+ return -1;
+ }
+ hpos += MD5_MAC_LEN;
+ } else
+ crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+
+ conn->verify.md5_cert = NULL;
+ hlen = SHA1_MAC_LEN;
+ if (conn->verify.sha1_cert == NULL ||
+ crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
+ conn->verify.sha1_cert = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha1_cert = NULL;
+
+ if (alg == SIGN_ALG_RSA)
+ hlen += MD5_MAC_LEN;
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+
+ if (end - pos < 2) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ slen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < slen) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos);
+ if (conn->client_rsa_key == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify "
+ "signature");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ buflen = end - pos;
+ buf = os_malloc(end - pos);
+ if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key,
+ pos, end - pos, buf, &buflen) < 0)
+ {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature");
+ os_free(buf);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
+ buf, buflen);
+
+ if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in "
+ "CertificateVerify - did not match with calculated "
+ "hash");
+ os_free(buf);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
+ return -1;
+ }
+
+ os_free(buf);
+
+ *in_len = end - in_data;
+
+ conn->state = CHANGE_CIPHER_SPEC;
+
+ return 0;
+}
+
+
+static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
+ u8 ct, const u8 *in_data,
+ size_t *in_len)
+{
+ const u8 *pos;
+ size_t left;
+
+ if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+ "received content type 0x%x", ct);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 1) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ if (*pos != TLS_CHANGE_CIPHER_SPEC) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+ "received data 0x%x", *pos);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
+ if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
+ "for record layer");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ *in_len = pos + 1 - in_data;
+
+ conn->state = CLIENT_FINISHED;
+
+ return 0;
+}
+
+
+static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
+ const u8 *in_data, size_t *in_len)
+{
+ const u8 *pos, *end;
+ size_t left, len, hlen;
+ u8 verify_data[TLS_VERIFY_DATA_LEN];
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
+ "received content type 0x%x", ct);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ pos = in_data;
+ left = *in_len;
+
+ if (left < 4) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
+ "Finished",
+ (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+
+ if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
+ "type 0x%x", pos[0]);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+
+ len = WPA_GET_BE24(pos + 1);
+
+ pos += 4;
+ left -= 4;
+
+ if (len > left) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
+ "(len=%lu > left=%lu)",
+ (unsigned long) len, (unsigned long) left);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ end = pos + len;
+ if (len != TLS_VERIFY_DATA_LEN) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
+ "in Finished: %lu (expected %d)",
+ (unsigned long) len, TLS_VERIFY_DATA_LEN);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
+ pos, TLS_VERIFY_DATA_LEN);
+
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_client == NULL ||
+ crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_client = NULL;
+ crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
+ conn->verify.sha1_client = NULL;
+ return -1;
+ }
+ conn->verify.md5_client = NULL;
+ hlen = SHA1_MAC_LEN;
+ if (conn->verify.sha1_client == NULL ||
+ crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
+ &hlen) < 0) {
+ conn->verify.sha1_client = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha1_client = NULL;
+
+ if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+ verify_data, TLS_VERIFY_DATA_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
+ verify_data, TLS_VERIFY_DATA_LEN);
+
+ if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+ wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+
+ *in_len = end - in_data;
+
+ if (conn->use_session_ticket) {
+ /* Abbreviated handshake using session ticket; RFC 4507 */
+ wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed "
+ "successfully");
+ conn->state = ESTABLISHED;
+ } else {
+ /* Full handshake */
+ conn->state = SERVER_CHANGE_CIPHER_SPEC;
+ }
+
+ return 0;
+}
+
+
+int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
+ const u8 *buf, size_t *len)
+{
+ if (ct == TLS_CONTENT_TYPE_ALERT) {
+ if (*len < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+ buf[0], buf[1]);
+ *len = 2;
+ conn->state = FAILED;
+ return -1;
+ }
+
+ switch (conn->state) {
+ case CLIENT_HELLO:
+ if (tls_process_client_hello(conn, ct, buf, len))
+ return -1;
+ break;
+ case CLIENT_CERTIFICATE:
+ if (tls_process_certificate(conn, ct, buf, len))
+ return -1;
+ break;
+ case CLIENT_KEY_EXCHANGE:
+ if (tls_process_client_key_exchange(conn, ct, buf, len))
+ return -1;
+ break;
+ case CERTIFICATE_VERIFY:
+ if (tls_process_certificate_verify(conn, ct, buf, len))
+ return -1;
+ break;
+ case CHANGE_CIPHER_SPEC:
+ if (tls_process_change_cipher_spec(conn, ct, buf, len))
+ return -1;
+ break;
+ case CLIENT_FINISHED:
+ if (tls_process_client_finished(conn, ct, buf, len))
+ return -1;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
+ "while processing received message",
+ conn->state);
+ return -1;
+ }
+
+ if (ct == TLS_CONTENT_TYPE_HANDSHAKE)
+ tls_verify_hash_add(&conn->verify, buf, *len);
+
+ return 0;
+}
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
new file mode 100644
index 0000000..e89e52e
--- /dev/null
+++ b/src/tls/tlsv1_server_write.c
@@ -0,0 +1,791 @@
+/*
+ * TLSv1 server - write handshake message
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "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, TLS_VERSION);
+ pos += 2;
+ /* Random random: uint32 gmt_unix_time, opaque random_bytes */
+ os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN);
+ pos += TLS_RANDOM_LEN;
+ /* SessionID session_id */
+ *pos++ = conn->session_id_len;
+ os_memcpy(pos, conn->session_id, conn->session_id_len);
+ pos += conn->session_id_len;
+ /* CipherSuite cipher_suite */
+ WPA_PUT_BE16(pos, conn->cipher_suite);
+ pos += 2;
+ /* CompressionMethod compression_method */
+ *pos++ = TLS_COMPRESSION_NULL;
+
+ if (conn->session_ticket && conn->session_ticket_cb) {
+ int res = conn->session_ticket_cb(
+ conn->session_ticket_cb_ctx,
+ conn->session_ticket, conn->session_ticket_len,
+ conn->client_random, conn->server_random,
+ conn->master_secret);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
+ "indicated failure");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_HANDSHAKE_FAILURE);
+ return -1;
+ }
+ conn->use_session_ticket = res;
+
+ if (conn->use_session_ticket) {
+ if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+ "derive keys");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ }
+
+ /*
+ * RFC 4507 specifies that server would include an empty
+ * SessionTicket extension in ServerHello and a
+ * NewSessionTicket message after the ServerHello. However,
+ * EAP-FAST (RFC 4851), i.e., the only user of SessionTicket
+ * extension at the moment, does not use such extensions.
+ *
+ * TODO: Add support for configuring RFC 4507 behavior and make
+ * EAP-FAST disable it.
+ */
+ }
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_server_certificate(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
+ size_t rlen;
+ struct x509_certificate *cert;
+ const struct tls_cipher_suite *suite;
+
+ suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+ if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when "
+ "using anonymous DH");
+ return 0;
+ }
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+ /* body - Certificate */
+ /* uint24 length (to be filled) */
+ cert_start = pos;
+ pos += 3;
+ cert = conn->cred->cert;
+ while (cert) {
+ if (pos + 3 + cert->cert_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
+ "for Certificate (cert_len=%lu left=%lu)",
+ (unsigned long) cert->cert_len,
+ (unsigned long) (end - pos));
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ WPA_PUT_BE24(pos, cert->cert_len);
+ pos += 3;
+ os_memcpy(pos, cert->cert_start, cert->cert_len);
+ pos += cert->cert_len;
+
+ if (x509_certificate_self_signed(cert))
+ break;
+ cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+ &cert->issuer);
+ }
+ if (cert == conn->cred->cert || cert == NULL) {
+ /*
+ * Server was not configured with all the needed certificates
+ * to form a full certificate chain. The client may fail to
+ * validate the chain unless it is configured with all the
+ * missing CA certificates.
+ */
+ wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain "
+ "not configured - validation may fail");
+ }
+ WPA_PUT_BE24(cert_start, pos - cert_start - 3);
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_server_key_exchange(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ tls_key_exchange keyx;
+ const struct tls_cipher_suite *suite;
+ u8 *pos, *rhdr, *hs_start, *hs_length;
+ size_t rlen;
+ u8 *dh_ys;
+ size_t dh_ys_len;
+
+ suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+ if (suite == NULL)
+ keyx = TLS_KEY_X_NULL;
+ else
+ keyx = suite->key_exchange;
+
+ if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed");
+ return 0;
+ }
+
+ if (keyx != TLS_KEY_X_DH_anon) {
+ /* TODO? */
+ wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet "
+ "supported with key exchange type %d", keyx);
+ return -1;
+ }
+
+ if (conn->cred == NULL || conn->cred->dh_p == NULL ||
+ conn->cred->dh_g == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for "
+ "ServerKeyExhcange");
+ return -1;
+ }
+
+ os_free(conn->dh_secret);
+ conn->dh_secret_len = conn->cred->dh_p_len;
+ conn->dh_secret = os_malloc(conn->dh_secret_len);
+ if (conn->dh_secret == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+ "memory for secret (Diffie-Hellman)");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ if (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, pos - hs_start, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_server_certificate_request(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length;
+ size_t rlen;
+
+ if (!conn->verify_peer) {
+ wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed");
+ return 0;
+ }
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+ /* body - CertificateRequest */
+
+ /*
+ * enum {
+ * rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
+ * (255)
+ * } ClientCertificateType;
+ * ClientCertificateType certificate_types<1..2^8-1>
+ */
+ *pos++ = 1;
+ *pos++ = 1; /* rsa_sign */
+
+ /*
+ * opaque DistinguishedName<1..2^16-1>
+ * DistinguishedName certificate_authorities<3..2^16-1>
+ */
+ /* TODO: add support for listing DNs for trusted CAs */
+ WPA_PUT_BE16(pos, 0);
+ pos += 2;
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_server_hello_done(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length;
+ size_t rlen;
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+ /* body - ServerHelloDone (empty) */
+
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos = rhdr + rlen;
+
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr;
+ size_t rlen;
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+ *pos = TLS_CHANGE_CIPHER_SPEC;
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
+ rhdr, end - rhdr, 1, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
+ "record layer");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ *msgpos = rhdr + rlen;
+
+ return 0;
+}
+
+
+static int tls_write_server_finished(struct tlsv1_server *conn,
+ u8 **msgpos, u8 *end)
+{
+ u8 *pos, *rhdr, *hs_start, *hs_length;
+ size_t rlen, hlen;
+ u8 verify_data[TLS_VERIFY_DATA_LEN];
+ u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+ pos = *msgpos;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+
+ /* Encrypted Handshake Message: Finished */
+
+ hlen = MD5_MAC_LEN;
+ if (conn->verify.md5_server == NULL ||
+ crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.md5_server = NULL;
+ crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
+ conn->verify.sha1_server = NULL;
+ return -1;
+ }
+ conn->verify.md5_server = NULL;
+ hlen = SHA1_MAC_LEN;
+ if (conn->verify.sha1_server == NULL ||
+ crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
+ &hlen) < 0) {
+ conn->verify.sha1_server = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha1_server = NULL;
+
+ if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+ verify_data, TLS_VERIFY_DATA_LEN)) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
+ verify_data, TLS_VERIFY_DATA_LEN);
+
+ rhdr = pos;
+ pos += TLS_RECORD_HEADER_LEN;
+ /* Handshake */
+ hs_start = pos;
+ /* HandshakeType msg_type */
+ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
+ /* uint24 length (to be filled) */
+ hs_length = pos;
+ pos += 3;
+ os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN);
+ pos += TLS_VERIFY_DATA_LEN;
+ WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+ tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+ if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+ rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+
+ pos = rhdr + rlen;
+
+ *msgpos = pos;
+
+ return 0;
+}
+
+
+static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len)
+{
+ u8 *msg, *end, *pos;
+ size_t msglen;
+
+ *out_len = 0;
+
+ msglen = 1000 + tls_server_cert_chain_der_len(conn);
+
+ msg = os_malloc(msglen);
+ if (msg == NULL)
+ return NULL;
+
+ pos = msg;
+ end = msg + msglen;
+
+ if (tls_write_server_hello(conn, &pos, end) < 0) {
+ os_free(msg);
+ return NULL;
+ }
+
+ if (conn->use_session_ticket) {
+ /* Abbreviated handshake using session ticket; RFC 4507 */
+ if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
+ tls_write_server_finished(conn, &pos, end) < 0) {
+ os_free(msg);
+ return NULL;
+ }
+
+ *out_len = pos - msg;
+
+ conn->state = CHANGE_CIPHER_SPEC;
+
+ return msg;
+ }
+
+ /* Full handshake */
+ if (tls_write_server_certificate(conn, &pos, end) < 0 ||
+ tls_write_server_key_exchange(conn, &pos, end) < 0 ||
+ tls_write_server_certificate_request(conn, &pos, end) < 0 ||
+ tls_write_server_hello_done(conn, &pos, end) < 0) {
+ os_free(msg);
+ return NULL;
+ }
+
+ *out_len = pos - msg;
+
+ conn->state = CLIENT_CERTIFICATE;
+
+ return msg;
+}
+
+
+static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn,
+ size_t *out_len)
+{
+ u8 *msg, *end, *pos;
+
+ *out_len = 0;
+
+ msg = os_malloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ pos = msg;
+ end = msg + 1000;
+
+ if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
+ tls_write_server_finished(conn, &pos, end) < 0) {
+ os_free(msg);
+ return NULL;
+ }
+
+ *out_len = pos - msg;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully");
+ conn->state = ESTABLISHED;
+
+ return msg;
+}
+
+
+u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len)
+{
+ switch (conn->state) {
+ case SERVER_HELLO:
+ return tls_send_server_hello(conn, out_len);
+ case SERVER_CHANGE_CIPHER_SPEC:
+ return tls_send_change_cipher_spec(conn, out_len);
+ default:
+ if (conn->state == ESTABLISHED && conn->use_session_ticket) {
+ /* Abbreviated handshake was already completed. */
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
+ "generating reply", conn->state);
+ return NULL;
+ }
+}
+
+
+u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
+ u8 description, size_t *out_len)
+{
+ u8 *alert, *pos, *length;
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
+ *out_len = 0;
+
+ alert = os_malloc(10);
+ if (alert == NULL)
+ return NULL;
+
+ pos = alert;
+
+ /* TLSPlaintext */
+ /* ContentType type */
+ *pos++ = TLS_CONTENT_TYPE_ALERT;
+ /* ProtocolVersion version */
+ WPA_PUT_BE16(pos, TLS_VERSION);
+ pos += 2;
+ /* uint16 length (to be filled) */
+ length = pos;
+ pos += 2;
+ /* opaque fragment[TLSPlaintext.length] */
+
+ /* Alert */
+ /* AlertLevel level */
+ *pos++ = level;
+ /* AlertDescription description */
+ *pos++ = description;
+
+ WPA_PUT_BE16(length, pos - length - 2);
+ *out_len = pos - alert;
+
+ return alert;
+}
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
new file mode 100644
index 0000000..bc93df6
--- /dev/null
+++ b/src/tls/x509v3.c
@@ -0,0 +1,1985 @@
+/*
+ * X.509v3 certificate parsing and processing (RFC 3280 profile)
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "asn1.h"
+#include "x509v3.h"
+
+
+static void x509_free_name(struct x509_name *name)
+{
+ size_t i;
+
+ for (i = 0; i < name->num_attr; i++) {
+ os_free(name->attr[i].value);
+ name->attr[i].value = NULL;
+ name->attr[i].type = X509_NAME_ATTR_NOT_USED;
+ }
+ name->num_attr = 0;
+ os_free(name->email);
+ name->email = NULL;
+
+ os_free(name->alt_email);
+ os_free(name->dns);
+ os_free(name->uri);
+ os_free(name->ip);
+ name->alt_email = name->dns = name->uri = NULL;
+ name->ip = NULL;
+ name->ip_len = 0;
+ os_memset(&name->rid, 0, sizeof(name->rid));
+}
+
+
+/**
+ * x509_certificate_free - Free an X.509 certificate
+ * @cert: Certificate to be freed
+ */
+void x509_certificate_free(struct x509_certificate *cert)
+{
+ if (cert == NULL)
+ return;
+ if (cert->next) {
+ wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p "
+ "was still on a list (next=%p)\n",
+ cert, cert->next);
+ }
+ x509_free_name(&cert->issuer);
+ x509_free_name(&cert->subject);
+ os_free(cert->public_key);
+ os_free(cert->sign_value);
+ os_free(cert);
+}
+
+
+/**
+ * x509_certificate_free - Free an X.509 certificate chain
+ * @cert: Pointer to the first certificate in the chain
+ */
+void x509_certificate_chain_free(struct x509_certificate *cert)
+{
+ struct x509_certificate *next;
+
+ while (cert) {
+ next = cert->next;
+ cert->next = NULL;
+ x509_certificate_free(cert);
+ cert = next;
+ }
+}
+
+
+static int x509_whitespace(char c)
+{
+ return c == ' ' || c == '\t';
+}
+
+
+static void x509_str_strip_whitespace(char *a)
+{
+ char *ipos, *opos;
+ int remove_whitespace = 1;
+
+ ipos = opos = a;
+
+ while (*ipos) {
+ if (remove_whitespace && x509_whitespace(*ipos))
+ ipos++;
+ else {
+ remove_whitespace = x509_whitespace(*ipos);
+ *opos++ = *ipos++;
+ }
+ }
+
+ *opos-- = '\0';
+ if (opos > a && x509_whitespace(*opos))
+ *opos = '\0';
+}
+
+
+static int x509_str_compare(const char *a, const char *b)
+{
+ char *aa, *bb;
+ int ret;
+
+ if (!a && b)
+ return -1;
+ if (a && !b)
+ return 1;
+ if (!a && !b)
+ return 0;
+
+ aa = os_strdup(a);
+ bb = os_strdup(b);
+
+ if (aa == NULL || bb == NULL) {
+ os_free(aa);
+ os_free(bb);
+ return os_strcasecmp(a, b);
+ }
+
+ x509_str_strip_whitespace(aa);
+ x509_str_strip_whitespace(bb);
+
+ ret = os_strcasecmp(aa, bb);
+
+ os_free(aa);
+ os_free(bb);
+
+ return ret;
+}
+
+
+/**
+ * x509_name_compare - Compare X.509 certificate names
+ * @a: Certificate name
+ * @b: Certificate name
+ * Returns: <0, 0, or >0 based on whether a is less than, equal to, or
+ * greater than b
+ */
+int x509_name_compare(struct x509_name *a, struct x509_name *b)
+{
+ int res;
+ size_t i;
+
+ if (!a && b)
+ return -1;
+ if (a && !b)
+ return 1;
+ if (!a && !b)
+ return 0;
+ if (a->num_attr < b->num_attr)
+ return -1;
+ if (a->num_attr > b->num_attr)
+ return 1;
+
+ for (i = 0; i < a->num_attr; i++) {
+ if (a->attr[i].type < b->attr[i].type)
+ return -1;
+ if (a->attr[i].type > b->attr[i].type)
+ return -1;
+ res = x509_str_compare(a->attr[i].value, b->attr[i].value);
+ if (res)
+ return res;
+ }
+ res = x509_str_compare(a->email, b->email);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+
+static int x509_parse_algorithm_identifier(
+ const u8 *buf, size_t len,
+ struct x509_algorithm_identifier *id, const u8 **next)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+
+ /*
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ */
+
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(AlgorithmIdentifier) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ if (end > buf + len)
+ return -1;
+
+ *next = end;
+
+ if (asn1_get_oid(pos, end - pos, &id->oid, &pos))
+ return -1;
+
+ /* TODO: optional parameters */
+
+ return 0;
+}
+
+
+static int x509_parse_public_key(const u8 *buf, size_t len,
+ struct x509_certificate *cert,
+ const u8 **next)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+
+ /*
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING
+ * }
+ */
+
+ pos = buf;
+ end = buf + len;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(SubjectPublicKeyInfo) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+
+ if (pos + hdr.length > end)
+ return -1;
+ end = pos + hdr.length;
+ *next = end;
+
+ if (x509_parse_algorithm_identifier(pos, end - pos,
+ &cert->public_key_alg, &pos))
+ return -1;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_BITSTRING) {
+ wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
+ "(subjectPublicKey) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ if (hdr.length < 1)
+ return -1;
+ pos = hdr.payload;
+ if (*pos) {
+ wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
+ *pos);
+ /*
+ * TODO: should this be rejected? X.509 certificates are
+ * unlikely to use such a construction. Now we would end up
+ * including the extra bits in the buffer which may also be
+ * ok.
+ */
+ }
+ os_free(cert->public_key);
+ cert->public_key = os_malloc(hdr.length - 1);
+ if (cert->public_key == NULL) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
+ "public key");
+ return -1;
+ }
+ os_memcpy(cert->public_key, pos + 1, hdr.length - 1);
+ cert->public_key_len = hdr.length - 1;
+ wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey",
+ cert->public_key, cert->public_key_len);
+
+ return 0;
+}
+
+
+static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
+ const u8 **next)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end;
+ struct asn1_oid oid;
+ char *val;
+
+ /*
+ * Name ::= CHOICE { RDNSequence }
+ * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type AttributeType,
+ * value AttributeValue
+ * }
+ * AttributeType ::= OBJECT IDENTIFIER
+ * AttributeValue ::= ANY DEFINED BY AttributeType
+ */
+
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(Name / RDNSequencer) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+
+ if (pos + hdr.length > buf + len)
+ return -1;
+
+ end = *next = pos + hdr.length;
+
+ while (pos < end) {
+ enum x509_name_attr_type type;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SET) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SET "
+ "(RelativeDistinguishedName) - found class "
+ "%d tag 0x%x", hdr.class, hdr.tag);
+ x509_free_name(name);
+ return -1;
+ }
+
+ set_pos = hdr.payload;
+ pos = set_end = hdr.payload + hdr.length;
+
+ if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(AttributeTypeAndValue) - found class %d "
+ "tag 0x%x", hdr.class, hdr.tag);
+ x509_free_name(name);
+ return -1;
+ }
+
+ seq_pos = hdr.payload;
+ seq_end = hdr.payload + hdr.length;
+
+ if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) {
+ x509_free_name(name);
+ return -1;
+ }
+
+ if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+ "AttributeValue");
+ x509_free_name(name);
+ return -1;
+ }
+
+ /* RFC 3280:
+ * MUST: country, organization, organizational-unit,
+ * distinguished name qualifier, state or province name,
+ * common name, serial number.
+ * SHOULD: locality, title, surname, given name, initials,
+ * pseudonym, generation qualifier.
+ * MUST: domainComponent (RFC 2247).
+ */
+ type = X509_NAME_ATTR_NOT_USED;
+ if (oid.len == 4 &&
+ oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) {
+ /* id-at ::= 2.5.4 */
+ switch (oid.oid[3]) {
+ case 3:
+ /* commonName */
+ type = X509_NAME_ATTR_CN;
+ break;
+ case 6:
+ /* countryName */
+ type = X509_NAME_ATTR_C;
+ break;
+ case 7:
+ /* localityName */
+ type = X509_NAME_ATTR_L;
+ break;
+ case 8:
+ /* stateOrProvinceName */
+ type = X509_NAME_ATTR_ST;
+ break;
+ case 10:
+ /* organizationName */
+ type = X509_NAME_ATTR_O;
+ break;
+ case 11:
+ /* organizationalUnitName */
+ type = X509_NAME_ATTR_OU;
+ break;
+ }
+ } else if (oid.len == 7 &&
+ oid.oid[0] == 1 && oid.oid[1] == 2 &&
+ oid.oid[2] == 840 && oid.oid[3] == 113549 &&
+ oid.oid[4] == 1 && oid.oid[5] == 9 &&
+ oid.oid[6] == 1) {
+ /* 1.2.840.113549.1.9.1 - e-mailAddress */
+ os_free(name->email);
+ name->email = os_malloc(hdr.length + 1);
+ if (name->email == NULL) {
+ x509_free_name(name);
+ return -1;
+ }
+ os_memcpy(name->email, hdr.payload, hdr.length);
+ name->email[hdr.length] = '\0';
+ continue;
+ } else if (oid.len == 7 &&
+ oid.oid[0] == 0 && oid.oid[1] == 9 &&
+ oid.oid[2] == 2342 && oid.oid[3] == 19200300 &&
+ oid.oid[4] == 100 && oid.oid[5] == 1 &&
+ oid.oid[6] == 25) {
+ /* 0.9.2342.19200300.100.1.25 - domainComponent */
+ type = X509_NAME_ATTR_DC;
+ }
+
+ if (type == X509_NAME_ATTR_NOT_USED) {
+ wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID",
+ (u8 *) oid.oid,
+ oid.len * sizeof(oid.oid[0]));
+ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data",
+ hdr.payload, hdr.length);
+ continue;
+ }
+
+ if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) {
+ wpa_printf(MSG_INFO, "X509: Too many Name attributes");
+ x509_free_name(name);
+ return -1;
+ }
+
+ val = os_malloc(hdr.length + 1);
+ if (val == NULL) {
+ x509_free_name(name);
+ return -1;
+ }
+ os_memcpy(val, hdr.payload, hdr.length);
+ val[hdr.length] = '\0';
+ if (os_strlen(val) != hdr.length) {
+ wpa_printf(MSG_INFO, "X509: Reject certificate with "
+ "embedded NUL byte in a string (%s[NUL])",
+ val);
+ x509_free_name(name);
+ return -1;
+ }
+
+ name->attr[name->num_attr].type = type;
+ name->attr[name->num_attr].value = val;
+ name->num_attr++;
+ }
+
+ return 0;
+}
+
+
+static char * x509_name_attr_str(enum x509_name_attr_type type)
+{
+ switch (type) {
+ case X509_NAME_ATTR_NOT_USED:
+ return "[N/A]";
+ case X509_NAME_ATTR_DC:
+ return "DC";
+ case X509_NAME_ATTR_CN:
+ return "CN";
+ case X509_NAME_ATTR_C:
+ return "C";
+ case X509_NAME_ATTR_L:
+ return "L";
+ case X509_NAME_ATTR_ST:
+ return "ST";
+ case X509_NAME_ATTR_O:
+ return "O";
+ case X509_NAME_ATTR_OU:
+ return "OU";
+ }
+ return "?";
+}
+
+
+/**
+ * x509_name_string - Convert an X.509 certificate name into a string
+ * @name: Name to convert
+ * @buf: Buffer for the string
+ * @len: Maximum buffer length
+ */
+void x509_name_string(struct x509_name *name, char *buf, size_t len)
+{
+ char *pos, *end;
+ int ret;
+ size_t i;
+
+ if (len == 0)
+ return;
+
+ pos = buf;
+ end = buf + len;
+
+ for (i = 0; i < name->num_attr; i++) {
+ ret = os_snprintf(pos, end - pos, "%s=%s, ",
+ x509_name_attr_str(name->attr[i].type),
+ name->attr[i].value);
+ if (ret < 0 || ret >= end - pos)
+ goto done;
+ pos += ret;
+ }
+
+ if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') {
+ pos--;
+ *pos = '\0';
+ pos--;
+ *pos = '\0';
+ }
+
+ if (name->email) {
+ ret = os_snprintf(pos, end - pos, "/emailAddress=%s",
+ name->email);
+ if (ret < 0 || ret >= end - pos)
+ goto done;
+ pos += ret;
+ }
+
+done:
+ end[-1] = '\0';
+}
+
+
+static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag,
+ os_time_t *val)
+{
+ const char *pos;
+ int year, month, day, hour, min, sec;
+
+ /*
+ * Time ::= CHOICE {
+ * utcTime UTCTime,
+ * generalTime GeneralizedTime
+ * }
+ *
+ * UTCTime: YYMMDDHHMMSSZ
+ * GeneralizedTime: YYYYMMDDHHMMSSZ
+ */
+
+ pos = (const char *) buf;
+
+ switch (asn1_tag) {
+ case ASN1_TAG_UTCTIME:
+ if (len != 13 || buf[12] != 'Z') {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
+ "UTCTime format", buf, len);
+ return -1;
+ }
+ if (sscanf(pos, "%02d", &year) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
+ "UTCTime year", buf, len);
+ return -1;
+ }
+ if (year < 50)
+ year += 2000;
+ else
+ year += 1900;
+ pos += 2;
+ break;
+ case ASN1_TAG_GENERALIZEDTIME:
+ if (len != 15 || buf[14] != 'Z') {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
+ "GeneralizedTime format", buf, len);
+ return -1;
+ }
+ if (sscanf(pos, "%04d", &year) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
+ "GeneralizedTime year", buf, len);
+ return -1;
+ }
+ pos += 4;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or "
+ "GeneralizedTime - found tag 0x%x", asn1_tag);
+ return -1;
+ }
+
+ if (sscanf(pos, "%02d", &month) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+ "(month)", buf, len);
+ return -1;
+ }
+ pos += 2;
+
+ if (sscanf(pos, "%02d", &day) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+ "(day)", buf, len);
+ return -1;
+ }
+ pos += 2;
+
+ if (sscanf(pos, "%02d", &hour) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+ "(hour)", buf, len);
+ return -1;
+ }
+ pos += 2;
+
+ if (sscanf(pos, "%02d", &min) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+ "(min)", buf, len);
+ return -1;
+ }
+ pos += 2;
+
+ if (sscanf(pos, "%02d", &sec) != 1) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+ "(sec)", buf, len);
+ return -1;
+ }
+
+ if (os_mktime(year, month, day, hour, min, sec, val) < 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time",
+ buf, len);
+ if (year < 1970) {
+ /*
+ * At least some test certificates have been configured
+ * to use dates prior to 1970. Set the date to
+ * beginning of 1970 to handle these case.
+ */
+ wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - "
+ "assume epoch as the time", year);
+ *val = 0;
+ return 0;
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int x509_parse_validity(const u8 *buf, size_t len,
+ struct x509_certificate *cert, const u8 **next)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos;
+ size_t plen;
+
+ /*
+ * Validity ::= SEQUENCE {
+ * notBefore Time,
+ * notAfter Time
+ * }
+ *
+ * RFC 3280, 4.1.2.5:
+ * CAs conforming to this profile MUST always encode certificate
+ * validity dates through the year 2049 as UTCTime; certificate
+ * validity dates in 2050 or later MUST be encoded as GeneralizedTime.
+ */
+
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(Validity) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+ plen = hdr.length;
+
+ if (pos + plen > buf + len)
+ return -1;
+
+ *next = pos + plen;
+
+ if (asn1_get_next(pos, plen, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+ &cert->not_before) < 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore "
+ "Time", hdr.payload, hdr.length);
+ return -1;
+ }
+
+ pos = hdr.payload + hdr.length;
+ plen = *next - pos;
+
+ if (asn1_get_next(pos, plen, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+ &cert->not_after) < 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter "
+ "Time", hdr.payload, hdr.length);
+ return -1;
+ }
+
+ wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu",
+ (unsigned long) cert->not_before,
+ (unsigned long) cert->not_after);
+
+ return 0;
+}
+
+
+static int x509_id_ce_oid(struct asn1_oid *oid)
+{
+ /* id-ce arc from X.509 for standard X.509v3 extensions */
+ return oid->len >= 4 &&
+ oid->oid[0] == 2 /* joint-iso-ccitt */ &&
+ oid->oid[1] == 5 /* ds */ &&
+ oid->oid[2] == 29 /* id-ce */;
+}
+
+
+static int x509_parse_ext_key_usage(struct x509_certificate *cert,
+ const u8 *pos, size_t len)
+{
+ struct asn1_hdr hdr;
+
+ /*
+ * KeyUsage ::= BIT STRING {
+ * digitalSignature (0),
+ * nonRepudiation (1),
+ * keyEncipherment (2),
+ * dataEncipherment (3),
+ * keyAgreement (4),
+ * keyCertSign (5),
+ * cRLSign (6),
+ * encipherOnly (7),
+ * decipherOnly (8) }
+ */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_BITSTRING ||
+ hdr.length < 1) {
+ wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in "
+ "KeyUsage; found %d tag 0x%x len %d",
+ hdr.class, hdr.tag, hdr.length);
+ return -1;
+ }
+
+ cert->extensions_present |= X509_EXT_KEY_USAGE;
+ cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length);
+
+ wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage);
+
+ return 0;
+}
+
+
+static int x509_parse_ext_basic_constraints(struct x509_certificate *cert,
+ const u8 *pos, size_t len)
+{
+ struct asn1_hdr hdr;
+ unsigned long value;
+ size_t left;
+
+ /*
+ * BasicConstraints ::= SEQUENCE {
+ * cA BOOLEAN DEFAULT FALSE,
+ * pathLenConstraint INTEGER (0..MAX) OPTIONAL }
+ */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+ "BasicConstraints; found %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS;
+
+ if (hdr.length == 0)
+ return 0;
+
+ if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+ "BasicConstraints");
+ return -1;
+ }
+
+ if (hdr.tag == ASN1_TAG_BOOLEAN) {
+ if (hdr.length != 1) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected "
+ "Boolean length (%u) in BasicConstraints",
+ hdr.length);
+ return -1;
+ }
+ cert->ca = hdr.payload[0];
+
+ if (hdr.payload + hdr.length == pos + len) {
+ wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d",
+ cert->ca);
+ return 0;
+ }
+
+ if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length,
+ &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+ "BasicConstraints");
+ return -1;
+ }
+ }
+
+ if (hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in "
+ "BasicConstraints; found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ pos = hdr.payload;
+ left = hdr.length;
+ value = 0;
+ while (left) {
+ value <<= 8;
+ value |= *pos++;
+ left--;
+ }
+
+ cert->path_len_constraint = value;
+ cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT;
+
+ wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d "
+ "pathLenConstraint=%lu",
+ cert->ca, cert->path_len_constraint);
+
+ return 0;
+}
+
+
+static int x509_parse_alt_name_rfc8222(struct x509_name *name,
+ const u8 *pos, size_t len)
+{
+ /* rfc822Name IA5String */
+ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len);
+ os_free(name->alt_email);
+ name->alt_email = os_zalloc(len + 1);
+ if (name->alt_email == NULL)
+ return -1;
+ os_memcpy(name->alt_email, pos, len);
+ if (os_strlen(name->alt_email) != len) {
+ wpa_printf(MSG_INFO, "X509: Reject certificate with "
+ "embedded NUL byte in rfc822Name (%s[NUL])",
+ name->alt_email);
+ os_free(name->alt_email);
+ name->alt_email = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+
+static int x509_parse_alt_name_dns(struct x509_name *name,
+ const u8 *pos, size_t len)
+{
+ /* dNSName IA5String */
+ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len);
+ os_free(name->dns);
+ name->dns = os_zalloc(len + 1);
+ if (name->dns == NULL)
+ return -1;
+ os_memcpy(name->dns, pos, len);
+ if (os_strlen(name->dns) != len) {
+ wpa_printf(MSG_INFO, "X509: Reject certificate with "
+ "embedded NUL byte in dNSName (%s[NUL])",
+ name->dns);
+ os_free(name->dns);
+ name->dns = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+
+static int x509_parse_alt_name_uri(struct x509_name *name,
+ const u8 *pos, size_t len)
+{
+ /* uniformResourceIdentifier IA5String */
+ wpa_hexdump_ascii(MSG_MSGDUMP,
+ "X509: altName - uniformResourceIdentifier",
+ pos, len);
+ os_free(name->uri);
+ name->uri = os_zalloc(len + 1);
+ if (name->uri == NULL)
+ return -1;
+ os_memcpy(name->uri, pos, len);
+ if (os_strlen(name->uri) != len) {
+ wpa_printf(MSG_INFO, "X509: Reject certificate with "
+ "embedded NUL byte in uniformResourceIdentifier "
+ "(%s[NUL])", name->uri);
+ os_free(name->uri);
+ name->uri = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+
+static int x509_parse_alt_name_ip(struct x509_name *name,
+ const u8 *pos, size_t len)
+{
+ /* iPAddress OCTET STRING */
+ wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len);
+ os_free(name->ip);
+ name->ip = os_malloc(len);
+ if (name->ip == NULL)
+ return -1;
+ os_memcpy(name->ip, pos, len);
+ name->ip_len = len;
+ return 0;
+}
+
+
+static int x509_parse_alt_name_rid(struct x509_name *name,
+ const u8 *pos, size_t len)
+{
+ char buf[80];
+
+ /* registeredID OBJECT IDENTIFIER */
+ if (asn1_parse_oid(pos, len, &name->rid) < 0)
+ return -1;
+
+ asn1_oid_to_str(&name->rid, buf, sizeof(buf));
+ wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf);
+
+ return 0;
+}
+
+
+static int x509_parse_ext_alt_name(struct x509_name *name,
+ const u8 *pos, size_t len)
+{
+ struct asn1_hdr hdr;
+ const u8 *p, *end;
+
+ /*
+ * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+ *
+ * GeneralName ::= CHOICE {
+ * otherName [0] OtherName,
+ * rfc822Name [1] IA5String,
+ * dNSName [2] IA5String,
+ * x400Address [3] ORAddress,
+ * directoryName [4] Name,
+ * ediPartyName [5] EDIPartyName,
+ * uniformResourceIdentifier [6] IA5String,
+ * iPAddress [7] OCTET STRING,
+ * registeredID [8] OBJECT IDENTIFIER }
+ *
+ * OtherName ::= SEQUENCE {
+ * type-id OBJECT IDENTIFIER,
+ * value [0] EXPLICIT ANY DEFINED BY type-id }
+ *
+ * EDIPartyName ::= SEQUENCE {
+ * nameAssigner [0] DirectoryString OPTIONAL,
+ * partyName [1] DirectoryString }
+ */
+
+ for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) {
+ int res;
+
+ if (asn1_get_next(p, end - p, &hdr) < 0) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+ "SubjectAltName item");
+ return -1;
+ }
+
+ if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC)
+ continue;
+
+ switch (hdr.tag) {
+ case 1:
+ res = x509_parse_alt_name_rfc8222(name, hdr.payload,
+ hdr.length);
+ break;
+ case 2:
+ res = x509_parse_alt_name_dns(name, hdr.payload,
+ hdr.length);
+ break;
+ case 6:
+ res = x509_parse_alt_name_uri(name, hdr.payload,
+ hdr.length);
+ break;
+ case 7:
+ res = x509_parse_alt_name_ip(name, hdr.payload,
+ hdr.length);
+ break;
+ case 8:
+ res = x509_parse_alt_name_rid(name, hdr.payload,
+ hdr.length);
+ break;
+ case 0: /* TODO: otherName */
+ case 3: /* TODO: x500Address */
+ case 4: /* TODO: directoryName */
+ case 5: /* TODO: ediPartyName */
+ default:
+ res = 0;
+ break;
+ }
+ if (res < 0)
+ return res;
+ }
+
+ return 0;
+}
+
+
+static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert,
+ const u8 *pos, size_t len)
+{
+ struct asn1_hdr hdr;
+
+ /* SubjectAltName ::= GeneralNames */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+ "SubjectAltName; found %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "X509: SubjectAltName");
+ cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME;
+
+ if (hdr.length == 0)
+ return 0;
+
+ return x509_parse_ext_alt_name(&cert->subject, hdr.payload,
+ hdr.length);
+}
+
+
+static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert,
+ const u8 *pos, size_t len)
+{
+ struct asn1_hdr hdr;
+
+ /* IssuerAltName ::= GeneralNames */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+ "IssuerAltName; found %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "X509: IssuerAltName");
+ cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME;
+
+ if (hdr.length == 0)
+ return 0;
+
+ return x509_parse_ext_alt_name(&cert->issuer, hdr.payload,
+ hdr.length);
+}
+
+
+static int x509_parse_extension_data(struct x509_certificate *cert,
+ struct asn1_oid *oid,
+ const u8 *pos, size_t len)
+{
+ if (!x509_id_ce_oid(oid))
+ return 1;
+
+ /* TODO: add other extensions required by RFC 3280, Ch 4.2:
+ * certificate policies (section 4.2.1.5)
+ * name constraints (section 4.2.1.11)
+ * policy constraints (section 4.2.1.12)
+ * extended key usage (section 4.2.1.13)
+ * inhibit any-policy (section 4.2.1.15)
+ */
+ switch (oid->oid[3]) {
+ case 15: /* id-ce-keyUsage */
+ return x509_parse_ext_key_usage(cert, pos, len);
+ case 17: /* id-ce-subjectAltName */
+ return x509_parse_ext_subject_alt_name(cert, pos, len);
+ case 18: /* id-ce-issuerAltName */
+ return x509_parse_ext_issuer_alt_name(cert, pos, len);
+ case 19: /* id-ce-basicConstraints */
+ return x509_parse_ext_basic_constraints(cert, pos, len);
+ default:
+ return 1;
+ }
+}
+
+
+static int x509_parse_extension(struct x509_certificate *cert,
+ const u8 *pos, size_t len, const u8 **next)
+{
+ const u8 *end;
+ struct asn1_hdr hdr;
+ struct asn1_oid oid;
+ int critical_ext = 0, res;
+ char buf[80];
+
+ /*
+ * Extension ::= SEQUENCE {
+ * extnID OBJECT IDENTIFIER,
+ * critical BOOLEAN DEFAULT FALSE,
+ * extnValue OCTET STRING
+ * }
+ */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
+ "Extensions: class %d tag 0x%x; expected SEQUENCE",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+ *next = end = pos + hdr.length;
+
+ if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for "
+ "Extension (expected OID)");
+ return -1;
+ }
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ (hdr.tag != ASN1_TAG_BOOLEAN &&
+ hdr.tag != ASN1_TAG_OCTETSTRING)) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
+ "Extensions: class %d tag 0x%x; expected BOOLEAN "
+ "or OCTET STRING", hdr.class, hdr.tag);
+ return -1;
+ }
+
+ if (hdr.tag == ASN1_TAG_BOOLEAN) {
+ if (hdr.length != 1) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected "
+ "Boolean length (%u)", hdr.length);
+ return -1;
+ }
+ critical_ext = hdr.payload[0];
+ pos = hdr.payload;
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ (hdr.class != ASN1_CLASS_UNIVERSAL &&
+ hdr.class != ASN1_CLASS_PRIVATE) ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header "
+ "in Extensions: class %d tag 0x%x; "
+ "expected OCTET STRING",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ }
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d",
+ buf, critical_ext);
+ wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length);
+
+ res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length);
+ if (res < 0)
+ return res;
+ if (res == 1 && critical_ext) {
+ wpa_printf(MSG_INFO, "X509: Unknown critical extension %s",
+ buf);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int x509_parse_extensions(struct x509_certificate *cert,
+ const u8 *pos, size_t len)
+{
+ const u8 *end;
+ struct asn1_hdr hdr;
+
+ /* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data "
+ "for Extensions: class %d tag 0x%x; "
+ "expected SEQUENCE", hdr.class, hdr.tag);
+ return -1;
+ }
+
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ while (pos < end) {
+ if (x509_parse_extension(cert, pos, end - pos, &pos)
+ < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int x509_parse_tbs_certificate(const u8 *buf, size_t len,
+ struct x509_certificate *cert,
+ const u8 **next)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+ size_t left;
+ char sbuf[128];
+ unsigned long value;
+
+ /* tbsCertificate TBSCertificate ::= SEQUENCE */
+ if (asn1_get_next(buf, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start "
+ "with a valid SEQUENCE - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+ end = *next = pos + hdr.length;
+
+ /*
+ * version [0] EXPLICIT Version DEFAULT v1
+ * Version ::= INTEGER { v1(0), v2(1), v3(2) }
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0)
+ return -1;
+ pos = hdr.payload;
+
+ if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) {
+ if (asn1_get_next(pos, end - pos, &hdr) < 0)
+ return -1;
+
+ if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
+ "version field - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ if (hdr.length != 1) {
+ wpa_printf(MSG_DEBUG, "X509: Unexpected version field "
+ "length %u (expected 1)", hdr.length);
+ return -1;
+ }
+ pos = hdr.payload;
+ left = hdr.length;
+ value = 0;
+ while (left) {
+ value <<= 8;
+ value |= *pos++;
+ left--;
+ }
+
+ cert->version = value;
+ if (cert->version != X509_CERT_V1 &&
+ cert->version != X509_CERT_V2 &&
+ cert->version != X509_CERT_V3) {
+ wpa_printf(MSG_DEBUG, "X509: Unsupported version %d",
+ cert->version + 1);
+ return -1;
+ }
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0)
+ return -1;
+ } else
+ cert->version = X509_CERT_V1;
+ wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1);
+
+ /* serialNumber CertificateSerialNumber ::= INTEGER */
+ if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_INTEGER) {
+ wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
+ "serialNumber; class=%d tag=0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ pos = hdr.payload;
+ left = hdr.length;
+ while (left) {
+ cert->serial_number <<= 8;
+ cert->serial_number |= *pos++;
+ left--;
+ }
+ wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number);
+
+ /* signature AlgorithmIdentifier */
+ if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature,
+ &pos))
+ return -1;
+
+ /* issuer Name */
+ if (x509_parse_name(pos, end - pos, &cert->issuer, &pos))
+ return -1;
+ x509_name_string(&cert->issuer, sbuf, sizeof(sbuf));
+ wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf);
+
+ /* validity Validity */
+ if (x509_parse_validity(pos, end - pos, cert, &pos))
+ return -1;
+
+ /* subject Name */
+ if (x509_parse_name(pos, end - pos, &cert->subject, &pos))
+ return -1;
+ x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
+ wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf);
+
+ /* subjectPublicKeyInfo SubjectPublicKeyInfo */
+ if (x509_parse_public_key(pos, end - pos, cert, &pos))
+ return -1;
+
+ if (pos == end)
+ return 0;
+
+ if (cert->version == X509_CERT_V1)
+ return 0;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+ wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+ " tag to parse optional tbsCertificate "
+ "field(s); parsed class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ if (hdr.tag == 1) {
+ /* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL */
+ wpa_printf(MSG_DEBUG, "X509: issuerUniqueID");
+ /* TODO: parse UniqueIdentifier ::= BIT STRING */
+
+ if (hdr.payload + hdr.length == end)
+ return 0;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+ wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+ " tag to parse optional tbsCertificate "
+ "field(s); parsed class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ }
+
+ if (hdr.tag == 2) {
+ /* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL */
+ wpa_printf(MSG_DEBUG, "X509: subjectUniqueID");
+ /* TODO: parse UniqueIdentifier ::= BIT STRING */
+
+ if (hdr.payload + hdr.length == end)
+ return 0;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+ wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+ " tag to parse optional tbsCertificate "
+ "field(s); parsed class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ }
+
+ if (hdr.tag != 3) {
+ wpa_printf(MSG_DEBUG, "X509: Ignored unexpected "
+ "Context-Specific tag %d in optional "
+ "tbsCertificate fields", hdr.tag);
+ return 0;
+ }
+
+ /* extensions [3] EXPLICIT Extensions OPTIONAL */
+
+ if (cert->version != X509_CERT_V3) {
+ wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and "
+ "Extensions data which are only allowed for "
+ "version 3", cert->version + 1);
+ return -1;
+ }
+
+ if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0)
+ return -1;
+
+ pos = hdr.payload + hdr.length;
+ if (pos < end) {
+ wpa_hexdump(MSG_DEBUG,
+ "X509: Ignored extra tbsCertificate data",
+ pos, end - pos);
+ }
+
+ return 0;
+}
+
+
+static int x509_rsadsi_oid(struct asn1_oid *oid)
+{
+ return oid->len >= 4 &&
+ oid->oid[0] == 1 /* iso */ &&
+ oid->oid[1] == 2 /* member-body */ &&
+ oid->oid[2] == 840 /* us */ &&
+ oid->oid[3] == 113549 /* rsadsi */;
+}
+
+
+static int x509_pkcs_oid(struct asn1_oid *oid)
+{
+ return oid->len >= 5 &&
+ x509_rsadsi_oid(oid) &&
+ oid->oid[4] == 1 /* pkcs */;
+}
+
+
+static int x509_digest_oid(struct asn1_oid *oid)
+{
+ return oid->len >= 5 &&
+ x509_rsadsi_oid(oid) &&
+ oid->oid[4] == 2 /* digestAlgorithm */;
+}
+
+
+static int x509_sha1_oid(struct asn1_oid *oid)
+{
+ return oid->len == 6 &&
+ oid->oid[0] == 1 /* iso */ &&
+ oid->oid[1] == 3 /* identified-organization */ &&
+ oid->oid[2] == 14 /* oiw */ &&
+ oid->oid[3] == 3 /* secsig */ &&
+ oid->oid[4] == 2 /* algorithms */ &&
+ oid->oid[5] == 26 /* id-sha1 */;
+}
+
+
+static int x509_sha256_oid(struct asn1_oid *oid)
+{
+ return oid->len == 9 &&
+ oid->oid[0] == 2 /* joint-iso-itu-t */ &&
+ oid->oid[1] == 16 /* country */ &&
+ oid->oid[2] == 840 /* us */ &&
+ oid->oid[3] == 1 /* organization */ &&
+ oid->oid[4] == 101 /* gov */ &&
+ oid->oid[5] == 3 /* csor */ &&
+ oid->oid[6] == 4 /* nistAlgorithm */ &&
+ oid->oid[7] == 2 /* hashAlgs */ &&
+ oid->oid[8] == 1 /* sha256 */;
+}
+
+
+/**
+ * x509_certificate_parse - Parse a X.509 certificate in DER format
+ * @buf: Pointer to the X.509 certificate in DER format
+ * @len: Buffer length
+ * Returns: Pointer to the parsed certificate or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned certificate by calling
+ * x509_certificate_free().
+ */
+struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end, *hash_start;
+ struct x509_certificate *cert;
+
+ cert = os_zalloc(sizeof(*cert) + len);
+ if (cert == NULL)
+ return NULL;
+ os_memcpy(cert + 1, buf, len);
+ cert->cert_start = (u8 *) (cert + 1);
+ cert->cert_len = len;
+
+ pos = buf;
+ end = buf + len;
+
+ /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */
+
+ /* Certificate ::= SEQUENCE */
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Certificate did not start with "
+ "a valid SEQUENCE - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ x509_certificate_free(cert);
+ return NULL;
+ }
+ pos = hdr.payload;
+
+ if (pos + hdr.length > end) {
+ x509_certificate_free(cert);
+ return NULL;
+ }
+
+ if (pos + hdr.length < end) {
+ wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER "
+ "encoded certificate",
+ pos + hdr.length, end - pos + hdr.length);
+ end = pos + hdr.length;
+ }
+
+ hash_start = pos;
+ cert->tbs_cert_start = cert->cert_start + (hash_start - buf);
+ if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) {
+ x509_certificate_free(cert);
+ return NULL;
+ }
+ cert->tbs_cert_len = pos - hash_start;
+
+ /* signatureAlgorithm AlgorithmIdentifier */
+ if (x509_parse_algorithm_identifier(pos, end - pos,
+ &cert->signature_alg, &pos)) {
+ x509_certificate_free(cert);
+ return NULL;
+ }
+
+ /* signatureValue BIT STRING */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_BITSTRING) {
+ wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
+ "(signatureValue) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ x509_certificate_free(cert);
+ return NULL;
+ }
+ if (hdr.length < 1) {
+ x509_certificate_free(cert);
+ return NULL;
+ }
+ pos = hdr.payload;
+ if (*pos) {
+ wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
+ *pos);
+ /* PKCS #1 v1.5 10.2.1:
+ * It is an error if the length in bits of the signature S is
+ * not a multiple of eight.
+ */
+ x509_certificate_free(cert);
+ return NULL;
+ }
+ os_free(cert->sign_value);
+ cert->sign_value = os_malloc(hdr.length - 1);
+ if (cert->sign_value == NULL) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
+ "signatureValue");
+ x509_certificate_free(cert);
+ return NULL;
+ }
+ os_memcpy(cert->sign_value, pos + 1, hdr.length - 1);
+ cert->sign_value_len = hdr.length - 1;
+ wpa_hexdump(MSG_MSGDUMP, "X509: signature",
+ cert->sign_value, cert->sign_value_len);
+
+ return cert;
+}
+
+
+/**
+ * x509_certificate_check_signature - Verify certificate signature
+ * @issuer: Issuer certificate
+ * @cert: Certificate to be verified
+ * Returns: 0 if cert has a valid signature that was signed by the issuer,
+ * -1 if not
+ */
+int x509_certificate_check_signature(struct x509_certificate *issuer,
+ struct x509_certificate *cert)
+{
+ struct crypto_public_key *pk;
+ u8 *data;
+ const u8 *pos, *end, *next, *da_end;
+ size_t data_len;
+ struct asn1_hdr hdr;
+ struct asn1_oid oid;
+ u8 hash[32];
+ size_t hash_len;
+
+ if (!x509_pkcs_oid(&cert->signature.oid) ||
+ cert->signature.oid.len != 7 ||
+ cert->signature.oid.oid[5] != 1 /* pkcs-1 */) {
+ wpa_printf(MSG_DEBUG, "X509: Unrecognized signature "
+ "algorithm");
+ return -1;
+ }
+
+ pk = crypto_public_key_import(issuer->public_key,
+ issuer->public_key_len);
+ if (pk == NULL)
+ return -1;
+
+ data_len = cert->sign_value_len;
+ data = os_malloc(data_len);
+ if (data == NULL) {
+ crypto_public_key_free(pk);
+ return -1;
+ }
+
+ if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value,
+ cert->sign_value_len, data,
+ &data_len) < 0) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature");
+ crypto_public_key_free(pk);
+ os_free(data);
+ return -1;
+ }
+ crypto_public_key_free(pk);
+
+ wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len);
+
+ /*
+ * PKCS #1 v1.5, 10.1.2:
+ *
+ * DigestInfo ::= SEQUENCE {
+ * digestAlgorithm DigestAlgorithmIdentifier,
+ * digest Digest
+ * }
+ *
+ * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * Digest ::= OCTET STRING
+ *
+ */
+ if (asn1_get_next(data, data_len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(DigestInfo) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ os_free(data);
+ return -1;
+ }
+
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ /*
+ * X.509:
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL
+ * }
+ */
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(AlgorithmIdentifier) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ os_free(data);
+ return -1;
+ }
+ da_end = hdr.payload + hdr.length;
+
+ if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) {
+ wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm");
+ os_free(data);
+ return -1;
+ }
+
+ if (x509_sha1_oid(&oid)) {
+ if (cert->signature.oid.oid[6] !=
+ 5 /* sha-1WithRSAEncryption */) {
+ wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 "
+ "does not match with certificate "
+ "signatureAlgorithm (%lu)",
+ cert->signature.oid.oid[6]);
+ os_free(data);
+ return -1;
+ }
+ goto skip_digest_oid;
+ }
+
+ if (x509_sha256_oid(&oid)) {
+ if (cert->signature.oid.oid[6] !=
+ 11 /* sha2561WithRSAEncryption */) {
+ wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 "
+ "does not match with certificate "
+ "signatureAlgorithm (%lu)",
+ cert->signature.oid.oid[6]);
+ os_free(data);
+ return -1;
+ }
+ goto skip_digest_oid;
+ }
+
+ if (!x509_digest_oid(&oid)) {
+ wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm");
+ os_free(data);
+ return -1;
+ }
+ switch (oid.oid[5]) {
+ case 5: /* md5 */
+ if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */)
+ {
+ wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does "
+ "not match with certificate "
+ "signatureAlgorithm (%lu)",
+ cert->signature.oid.oid[6]);
+ os_free(data);
+ return -1;
+ }
+ break;
+ case 2: /* md2 */
+ case 4: /* md4 */
+ default:
+ wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm "
+ "(%lu)", oid.oid[5]);
+ os_free(data);
+ return -1;
+ }
+
+skip_digest_oid:
+ /* Digest ::= OCTET STRING */
+ pos = da_end;
+ end = data + data_len;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING "
+ "(Digest) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ os_free(data);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest",
+ hdr.payload, hdr.length);
+
+ switch (cert->signature.oid.oid[6]) {
+ case 4: /* md5WithRSAEncryption */
+ md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+ hash);
+ hash_len = 16;
+ wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)",
+ hash, hash_len);
+ break;
+ case 5: /* sha-1WithRSAEncryption */
+ sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+ hash);
+ hash_len = 20;
+ wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)",
+ hash, hash_len);
+ break;
+ case 11: /* sha256WithRSAEncryption */
+ sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+ hash);
+ hash_len = 32;
+ wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)",
+ hash, hash_len);
+ break;
+ case 2: /* md2WithRSAEncryption */
+ case 12: /* sha384WithRSAEncryption */
+ case 13: /* sha512WithRSAEncryption */
+ default:
+ wpa_printf(MSG_INFO, "X509: Unsupported certificate signature "
+ "algorithm (%lu)", cert->signature.oid.oid[6]);
+ os_free(data);
+ return -1;
+ }
+
+ if (hdr.length != hash_len ||
+ os_memcmp(hdr.payload, hash, hdr.length) != 0) {
+ wpa_printf(MSG_INFO, "X509: Certificate Digest does not match "
+ "with calculated tbsCertificate hash");
+ os_free(data);
+ return -1;
+ }
+
+ os_free(data);
+
+ wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with "
+ "calculated tbsCertificate hash");
+
+ return 0;
+}
+
+
+static int x509_valid_issuer(const struct x509_certificate *cert)
+{
+ if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) &&
+ !cert->ca) {
+ wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an "
+ "issuer");
+ return -1;
+ }
+
+ if (cert->version == X509_CERT_V3 &&
+ !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) {
+ wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not "
+ "include BasicConstraints extension");
+ return -1;
+ }
+
+ if ((cert->extensions_present & X509_EXT_KEY_USAGE) &&
+ !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) {
+ wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have "
+ "keyCertSign bit in Key Usage");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * x509_certificate_chain_validate - Validate X.509 certificate chain
+ * @trusted: List of trusted certificates
+ * @chain: Certificate chain to be validated (first chain must be issued by
+ * signed by the second certificate in the chain and so on)
+ * @reason: Buffer for returning failure reason (X509_VALIDATE_*)
+ * Returns: 0 if chain is valid, -1 if not
+ */
+int x509_certificate_chain_validate(struct x509_certificate *trusted,
+ struct x509_certificate *chain,
+ int *reason)
+{
+ long unsigned idx;
+ int chain_trusted = 0;
+ struct x509_certificate *cert, *trust;
+ char buf[128];
+ struct os_time now;
+
+ *reason = X509_VALIDATE_OK;
+
+ wpa_printf(MSG_DEBUG, "X509: Validate certificate chain");
+ os_get_time(&now);
+
+ for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
+ x509_name_string(&cert->subject, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
+
+ if (chain_trusted)
+ continue;
+
+ if ((unsigned long) now.sec <
+ (unsigned long) cert->not_before ||
+ (unsigned long) now.sec >
+ (unsigned long) cert->not_after) {
+ wpa_printf(MSG_INFO, "X509: Certificate not valid "
+ "(now=%lu not_before=%lu not_after=%lu)",
+ now.sec, cert->not_before, cert->not_after);
+ *reason = X509_VALIDATE_CERTIFICATE_EXPIRED;
+ return -1;
+ }
+
+ if (cert->next) {
+ if (x509_name_compare(&cert->issuer,
+ &cert->next->subject) != 0) {
+ wpa_printf(MSG_DEBUG, "X509: Certificate "
+ "chain issuer name mismatch");
+ x509_name_string(&cert->issuer, buf,
+ sizeof(buf));
+ wpa_printf(MSG_DEBUG, "X509: cert issuer: %s",
+ buf);
+ x509_name_string(&cert->next->subject, buf,
+ sizeof(buf));
+ wpa_printf(MSG_DEBUG, "X509: next cert "
+ "subject: %s", buf);
+ *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN;
+ return -1;
+ }
+
+ if (x509_valid_issuer(cert->next) < 0) {
+ *reason = X509_VALIDATE_BAD_CERTIFICATE;
+ return -1;
+ }
+
+ if ((cert->next->extensions_present &
+ X509_EXT_PATH_LEN_CONSTRAINT) &&
+ idx > cert->next->path_len_constraint) {
+ wpa_printf(MSG_DEBUG, "X509: pathLenConstraint"
+ " not met (idx=%lu issuer "
+ "pathLenConstraint=%lu)", idx,
+ cert->next->path_len_constraint);
+ *reason = X509_VALIDATE_BAD_CERTIFICATE;
+ return -1;
+ }
+
+ if (x509_certificate_check_signature(cert->next, cert)
+ < 0) {
+ wpa_printf(MSG_DEBUG, "X509: Invalid "
+ "certificate signature within "
+ "chain");
+ *reason = X509_VALIDATE_BAD_CERTIFICATE;
+ return -1;
+ }
+ }
+
+ for (trust = trusted; trust; trust = trust->next) {
+ if (x509_name_compare(&cert->issuer, &trust->subject)
+ == 0)
+ break;
+ }
+
+ if (trust) {
+ wpa_printf(MSG_DEBUG, "X509: Found issuer from the "
+ "list of trusted certificates");
+ if (x509_valid_issuer(trust) < 0) {
+ *reason = X509_VALIDATE_BAD_CERTIFICATE;
+ return -1;
+ }
+
+ if (x509_certificate_check_signature(trust, cert) < 0)
+ {
+ wpa_printf(MSG_DEBUG, "X509: Invalid "
+ "certificate signature");
+ *reason = X509_VALIDATE_BAD_CERTIFICATE;
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "X509: Trusted certificate "
+ "found to complete the chain");
+ chain_trusted = 1;
+ }
+ }
+
+ if (!chain_trusted) {
+ wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers "
+ "from the list of trusted certificates");
+ if (trusted) {
+ *reason = X509_VALIDATE_UNKNOWN_CA;
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "X509: Certificate chain validation "
+ "disabled - ignore unknown CA issue");
+ }
+
+ wpa_printf(MSG_DEBUG, "X509: Certificate chain valid");
+
+ return 0;
+}
+
+
+/**
+ * x509_certificate_get_subject - Get a certificate based on Subject name
+ * @chain: Certificate chain to search through
+ * @name: Subject name to search for
+ * Returns: Pointer to the certificate with the given Subject name or
+ * %NULL on failure
+ */
+struct x509_certificate *
+x509_certificate_get_subject(struct x509_certificate *chain,
+ struct x509_name *name)
+{
+ struct x509_certificate *cert;
+
+ for (cert = chain; cert; cert = cert->next) {
+ if (x509_name_compare(&cert->subject, name) == 0)
+ return cert;
+ }
+ return NULL;
+}
+
+
+/**
+ * x509_certificate_self_signed - Is the certificate self-signed?
+ * @cert: Certificate
+ * Returns: 1 if certificate is self-signed, 0 if not
+ */
+int x509_certificate_self_signed(struct x509_certificate *cert)
+{
+ return x509_name_compare(&cert->issuer, &cert->subject) == 0;
+}
diff --git a/src/tls/x509v3.h b/src/tls/x509v3.h
new file mode 100644
index 0000000..37292d7
--- /dev/null
+++ b/src/tls/x509v3.h
@@ -0,0 +1,129 @@
+/*
+ * X.509v3 certificate parsing and processing
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef X509V3_H
+#define X509V3_H
+
+#include "asn1.h"
+
+struct x509_algorithm_identifier {
+ struct asn1_oid oid;
+};
+
+struct x509_name_attr {
+ enum x509_name_attr_type {
+ X509_NAME_ATTR_NOT_USED,
+ X509_NAME_ATTR_DC,
+ X509_NAME_ATTR_CN,
+ X509_NAME_ATTR_C,
+ X509_NAME_ATTR_L,
+ X509_NAME_ATTR_ST,
+ X509_NAME_ATTR_O,
+ X509_NAME_ATTR_OU
+ } type;
+ char *value;
+};
+
+#define X509_MAX_NAME_ATTRIBUTES 20
+
+struct x509_name {
+ struct x509_name_attr attr[X509_MAX_NAME_ATTRIBUTES];
+ size_t num_attr;
+ char *email; /* emailAddress */
+
+ /* from alternative name extension */
+ char *alt_email; /* rfc822Name */
+ char *dns; /* dNSName */
+ char *uri; /* uniformResourceIdentifier */
+ u8 *ip; /* iPAddress */
+ size_t ip_len; /* IPv4: 4, IPv6: 16 */
+ struct asn1_oid rid; /* registeredID */
+};
+
+struct x509_certificate {
+ struct x509_certificate *next;
+ enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version;
+ unsigned long serial_number;
+ struct x509_algorithm_identifier signature;
+ struct x509_name issuer;
+ struct x509_name subject;
+ os_time_t not_before;
+ os_time_t not_after;
+ struct x509_algorithm_identifier public_key_alg;
+ u8 *public_key;
+ size_t public_key_len;
+ struct x509_algorithm_identifier signature_alg;
+ u8 *sign_value;
+ size_t sign_value_len;
+
+ /* Extensions */
+ unsigned int extensions_present;
+#define X509_EXT_BASIC_CONSTRAINTS (1 << 0)
+#define X509_EXT_PATH_LEN_CONSTRAINT (1 << 1)
+#define X509_EXT_KEY_USAGE (1 << 2)
+#define X509_EXT_SUBJECT_ALT_NAME (1 << 3)
+#define X509_EXT_ISSUER_ALT_NAME (1 << 4)
+
+ /* BasicConstraints */
+ int ca; /* cA */
+ unsigned long path_len_constraint; /* pathLenConstraint */
+
+ /* KeyUsage */
+ unsigned long key_usage;
+#define X509_KEY_USAGE_DIGITAL_SIGNATURE (1 << 0)
+#define X509_KEY_USAGE_NON_REPUDIATION (1 << 1)
+#define X509_KEY_USAGE_KEY_ENCIPHERMENT (1 << 2)
+#define X509_KEY_USAGE_DATA_ENCIPHERMENT (1 << 3)
+#define X509_KEY_USAGE_KEY_AGREEMENT (1 << 4)
+#define X509_KEY_USAGE_KEY_CERT_SIGN (1 << 5)
+#define X509_KEY_USAGE_CRL_SIGN (1 << 6)
+#define X509_KEY_USAGE_ENCIPHER_ONLY (1 << 7)
+#define X509_KEY_USAGE_DECIPHER_ONLY (1 << 8)
+
+ /*
+ * The DER format certificate follows struct x509_certificate. These
+ * pointers point to that buffer.
+ */
+ const u8 *cert_start;
+ size_t cert_len;
+ const u8 *tbs_cert_start;
+ size_t tbs_cert_len;
+};
+
+enum {
+ X509_VALIDATE_OK,
+ X509_VALIDATE_BAD_CERTIFICATE,
+ X509_VALIDATE_UNSUPPORTED_CERTIFICATE,
+ X509_VALIDATE_CERTIFICATE_REVOKED,
+ X509_VALIDATE_CERTIFICATE_EXPIRED,
+ X509_VALIDATE_CERTIFICATE_UNKNOWN,
+ X509_VALIDATE_UNKNOWN_CA
+};
+
+void x509_certificate_free(struct x509_certificate *cert);
+struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len);
+void x509_name_string(struct x509_name *name, char *buf, size_t len);
+int x509_name_compare(struct x509_name *a, struct x509_name *b);
+void x509_certificate_chain_free(struct x509_certificate *cert);
+int x509_certificate_check_signature(struct x509_certificate *issuer,
+ struct x509_certificate *cert);
+int x509_certificate_chain_validate(struct x509_certificate *trusted,
+ struct x509_certificate *chain,
+ int *reason);
+struct x509_certificate *
+x509_certificate_get_subject(struct x509_certificate *chain,
+ struct x509_name *name);
+int x509_certificate_self_signed(struct x509_certificate *cert);
+
+#endif /* X509V3_H */
diff --git a/src/utils/.gitignore b/src/utils/.gitignore
new file mode 100644
index 0000000..833734f
--- /dev/null
+++ b/src/utils/.gitignore
@@ -0,0 +1 @@
+libutils.a
diff --git a/src/utils/Makefile b/src/utils/Makefile
new file mode 100644
index 0000000..0f1f191
--- /dev/null
+++ b/src/utils/Makefile
@@ -0,0 +1,39 @@
+all: libutils.a
+
+clean:
+ rm -f *~ *.o *.d libutils.a
+
+install:
+ @echo Nothing to be made.
+
+
+include ../lib.rules
+
+#CFLAGS += -DWPA_TRACE
+CFLAGS += -DCONFIG_IPV6
+
+LIB_OBJS= \
+ base64.o \
+ common.o \
+ ip_addr.o \
+ radiotap.o \
+ trace.o \
+ uuid.o \
+ wpa_debug.o \
+ wpabuf.o
+
+# Pick correct OS wrapper implementation
+LIB_OBJS += os_unix.o
+
+# Pick correct event loop implementation
+LIB_OBJS += eloop.o
+
+# 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/src/utils/base64.c b/src/utils/base64.c
new file mode 100644
index 0000000..155bfce
--- /dev/null
+++ b/src/utils/base64.c
@@ -0,0 +1,154 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "os.h"
+#include "base64.h"
+
+static const unsigned char base64_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char *out, *pos;
+ const unsigned char *end, *in;
+ size_t olen;
+ int line_len;
+
+ olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+ olen += olen / 72; /* line feeds */
+ olen++; /* nul termination */
+ if (olen < len)
+ return NULL; /* integer overflow */
+ out = os_malloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ end = src + len;
+ in = src;
+ pos = out;
+ line_len = 0;
+ while (end - in >= 3) {
+ *pos++ = base64_table[in[0] >> 2];
+ *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+ *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+ *pos++ = base64_table[in[2] & 0x3f];
+ in += 3;
+ line_len += 4;
+ if (line_len >= 72) {
+ *pos++ = '\n';
+ line_len = 0;
+ }
+ }
+
+ if (end - in) {
+ *pos++ = base64_table[in[0] >> 2];
+ if (end - in == 1) {
+ *pos++ = base64_table[(in[0] & 0x03) << 4];
+ *pos++ = '=';
+ } else {
+ *pos++ = base64_table[((in[0] & 0x03) << 4) |
+ (in[1] >> 4)];
+ *pos++ = base64_table[(in[1] & 0x0f) << 2];
+ }
+ *pos++ = '=';
+ line_len += 4;
+ }
+
+ if (line_len)
+ *pos++ = '\n';
+
+ *pos = '\0';
+ if (out_len)
+ *out_len = pos - out;
+ return out;
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char dtable[256], *out, *pos, in[4], block[4], tmp;
+ size_t i, count, olen;
+
+ os_memset(dtable, 0x80, 256);
+ for (i = 0; i < sizeof(base64_table) - 1; i++)
+ dtable[base64_table[i]] = (unsigned char) i;
+ dtable['='] = 0;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ if (dtable[src[i]] != 0x80)
+ count++;
+ }
+
+ if (count == 0 || count % 4)
+ return NULL;
+
+ olen = count / 4 * 3;
+ pos = out = os_malloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ tmp = dtable[src[i]];
+ if (tmp == 0x80)
+ continue;
+
+ in[count] = src[i];
+ block[count] = tmp;
+ count++;
+ if (count == 4) {
+ *pos++ = (block[0] << 2) | (block[1] >> 4);
+ *pos++ = (block[1] << 4) | (block[2] >> 2);
+ *pos++ = (block[2] << 6) | block[3];
+ count = 0;
+ }
+ }
+
+ if (pos > out) {
+ if (in[2] == '=')
+ pos -= 2;
+ else if (in[3] == '=')
+ pos--;
+ }
+
+ *out_len = pos - out;
+ return out;
+}
diff --git a/src/utils/base64.h b/src/utils/base64.h
new file mode 100644
index 0000000..b87a168
--- /dev/null
+++ b/src/utils/base64.h
@@ -0,0 +1,23 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef BASE64_H
+#define BASE64_H
+
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len);
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len);
+
+#endif /* BASE64_H */
diff --git a/src/utils/build_config.h b/src/utils/build_config.h
new file mode 100644
index 0000000..3666778
--- /dev/null
+++ b/src/utils/build_config.h
@@ -0,0 +1,105 @@
+/*
+ * wpa_supplicant/hostapd - Build time configuration defines
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This header file can be used to define configuration defines that were
+ * originally defined in Makefile. This is mainly meant for IDE use or for
+ * systems that do not have suitable 'make' tool. In these cases, it may be
+ * easier to have a single place for defining all the needed C pre-processor
+ * defines.
+ */
+
+#ifndef BUILD_CONFIG_H
+#define BUILD_CONFIG_H
+
+/* Insert configuration defines, e.g., #define EAP_MD5, here, if needed. */
+
+#ifdef CONFIG_WIN32_DEFAULTS
+#define CONFIG_NATIVE_WINDOWS
+#define CONFIG_ANSI_C_EXTRA
+#define CONFIG_WINPCAP
+#define IEEE8021X_EAPOL
+#define PKCS12_FUNCS
+#define PCSC_FUNCS
+#define CONFIG_CTRL_IFACE
+#define CONFIG_CTRL_IFACE_NAMED_PIPE
+#define CONFIG_DRIVER_NDIS
+#define CONFIG_NDIS_EVENTS_INTEGRATED
+#define CONFIG_DEBUG_FILE
+#define EAP_MD5
+#define EAP_TLS
+#define EAP_MSCHAPv2
+#define EAP_PEAP
+#define EAP_TTLS
+#define EAP_GTC
+#define EAP_OTP
+#define EAP_LEAP
+#define EAP_TNC
+#define _CRT_SECURE_NO_DEPRECATE
+
+#ifdef USE_INTERNAL_CRYPTO
+#define CONFIG_TLS_INTERNAL_CLIENT
+#define CONFIG_INTERNAL_LIBTOMMATH
+#define CONFIG_CRYPTO_INTERNAL
+#endif /* USE_INTERNAL_CRYPTO */
+#endif /* CONFIG_WIN32_DEFAULTS */
+
+#ifdef __SYMBIAN32__
+#define OS_NO_C_LIB_DEFINES
+#define CONFIG_ANSI_C_EXTRA
+#define CONFIG_NO_WPA_MSG
+#define CONFIG_NO_HOSTAPD_LOGGER
+#define CONFIG_NO_STDOUT_DEBUG
+#define CONFIG_BACKEND_FILE
+#define CONFIG_INTERNAL_LIBTOMMATH
+#define CONFIG_CRYPTO_INTERNAL
+#define IEEE8021X_EAPOL
+#define PKCS12_FUNCS
+#define EAP_MD5
+#define EAP_TLS
+#define EAP_MSCHAPv2
+#define EAP_PEAP
+#define EAP_TTLS
+#define EAP_GTC
+#define EAP_OTP
+#define EAP_LEAP
+#define EAP_FAST
+#endif /* __SYMBIAN32__ */
+
+#ifdef CONFIG_XCODE_DEFAULTS
+#define CONFIG_DRIVER_OSX
+#define CONFIG_BACKEND_FILE
+#define IEEE8021X_EAPOL
+#define PKCS12_FUNCS
+#define CONFIG_CTRL_IFACE
+#define CONFIG_CTRL_IFACE_UNIX
+#define CONFIG_DEBUG_FILE
+#define EAP_MD5
+#define EAP_TLS
+#define EAP_MSCHAPv2
+#define EAP_PEAP
+#define EAP_TTLS
+#define EAP_GTC
+#define EAP_OTP
+#define EAP_LEAP
+#define EAP_TNC
+#define CONFIG_WPS
+#define EAP_WSC
+
+#ifdef USE_INTERNAL_CRYPTO
+#define CONFIG_TLS_INTERNAL_CLIENT
+#define CONFIG_INTERNAL_LIBTOMMATH
+#define CONFIG_CRYPTO_INTERNAL
+#endif /* USE_INTERNAL_CRYPTO */
+#endif /* CONFIG_XCODE_DEFAULTS */
+
+#endif /* BUILD_CONFIG_H */
diff --git a/src/utils/common.c b/src/utils/common.c
new file mode 100644
index 0000000..89eca1c
--- /dev/null
+++ b/src/utils/common.c
@@ -0,0 +1,387 @@
+/*
+ * wpa_supplicant/hostapd / common helper functions, etc.
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+
+
+static int hex2num(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return -1;
+}
+
+
+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 */
+
+
+/**
+ * wpa_ssid_txt - Convert SSID to a printable string
+ * @ssid: SSID (32-octet string)
+ * @ssid_len: Length of ssid in octets
+ * Returns: Pointer to a printable string
+ *
+ * This function can be used to convert SSIDs into printable form. In most
+ * cases, SSIDs do not use unprintable characters, but IEEE 802.11 standard
+ * does not limit the used character set, so anything could be used in an SSID.
+ *
+ * This function uses a static buffer, so only one call can be used at the
+ * time, i.e., this is not re-entrant and the returned buffer must be used
+ * before calling this again.
+ */
+const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
+{
+ static char ssid_txt[33];
+ char *pos;
+
+ if (ssid_len > 32)
+ ssid_len = 32;
+ os_memcpy(ssid_txt, ssid, ssid_len);
+ ssid_txt[ssid_len] = '\0';
+ for (pos = ssid_txt; *pos != '\0'; pos++) {
+ if ((u8) *pos < 32 || (u8) *pos >= 127)
+ *pos = '_';
+ }
+ return ssid_txt;
+}
+
+
+void * __hide_aliasing_typecast(void *foo)
+{
+ return foo;
+}
diff --git a/src/utils/common.h b/src/utils/common.h
new file mode 100644
index 0000000..14ab297
--- /dev/null
+++ b/src/utils/common.h
@@ -0,0 +1,502 @@
+/*
+ * wpa_supplicant/hostapd / common helper functions, etc.
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "os.h"
+
+#if defined(__linux__) || defined(__GLIBC__)
+#include <endian.h>
+#include <byteswap.h>
+#endif /* __linux__ */
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
+ defined(__OpenBSD__)
+#include <sys/types.h>
+#include <sys/endian.h>
+#define __BYTE_ORDER _BYTE_ORDER
+#define __LITTLE_ENDIAN _LITTLE_ENDIAN
+#define __BIG_ENDIAN _BIG_ENDIAN
+#ifdef __OpenBSD__
+#define bswap_16 swap16
+#define bswap_32 swap32
+#define bswap_64 swap64
+#else /* __OpenBSD__ */
+#define bswap_16 bswap16
+#define bswap_32 bswap32
+#define bswap_64 bswap64
+#endif /* __OpenBSD__ */
+#endif /* defined(__FreeBSD__) || defined(__NetBSD__) ||
+ * defined(__DragonFly__) || defined(__OpenBSD__) */
+
+#ifdef __APPLE__
+#include <sys/types.h>
+#include <machine/endian.h>
+#define __BYTE_ORDER _BYTE_ORDER
+#define __LITTLE_ENDIAN _LITTLE_ENDIAN
+#define __BIG_ENDIAN _BIG_ENDIAN
+static inline unsigned short bswap_16(unsigned short v)
+{
+ return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int bswap_32(unsigned int v)
+{
+ return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+ ((v & 0xff0000) >> 8) | (v >> 24);
+}
+#endif /* __APPLE__ */
+
+#ifdef CONFIG_TI_COMPILER
+#define __BIG_ENDIAN 4321
+#define __LITTLE_ENDIAN 1234
+#ifdef __big_endian__
+#define __BYTE_ORDER __BIG_ENDIAN
+#else
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif
+#endif /* CONFIG_TI_COMPILER */
+
+#ifdef __SYMBIAN32__
+#define __BIG_ENDIAN 4321
+#define __LITTLE_ENDIAN 1234
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif /* __SYMBIAN32__ */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <winsock.h>
+
+typedef int socklen_t;
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0 /* not supported */
+#endif
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#ifdef _MSC_VER
+#define inline __inline
+
+#undef vsnprintf
+#define vsnprintf _vsnprintf
+#undef close
+#define close closesocket
+#endif /* _MSC_VER */
+
+
+/* Define platform specific integer types */
+
+#ifdef _MSC_VER
+typedef UINT64 u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef INT64 s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define WPA_TYPES_DEFINED
+#endif /* _MSC_VER */
+
+#ifdef __vxworks
+typedef unsigned long long u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef long long s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define WPA_TYPES_DEFINED
+#endif /* __vxworks */
+
+#ifdef CONFIG_TI_COMPILER
+#ifdef _LLONG_AVAILABLE
+typedef unsigned long long u64;
+#else
+/*
+ * TODO: 64-bit variable not available. Using long as a workaround to test the
+ * build, but this will likely not work for all operations.
+ */
+typedef unsigned long u64;
+#endif
+typedef unsigned int u32;
+typedef unsigned short u16;
+typedef unsigned char u8;
+#define WPA_TYPES_DEFINED
+#endif /* CONFIG_TI_COMPILER */
+
+#ifdef __SYMBIAN32__
+#define __REMOVE_PLATSEC_DIAGNOSTICS__
+#include <e32def.h>
+typedef TUint64 u64;
+typedef TUint32 u32;
+typedef TUint16 u16;
+typedef TUint8 u8;
+#define WPA_TYPES_DEFINED
+#endif /* __SYMBIAN32__ */
+
+#ifndef WPA_TYPES_DEFINED
+#ifdef CONFIG_USE_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+#define WPA_TYPES_DEFINED
+#endif /* !WPA_TYPES_DEFINED */
+
+
+/* Define platform specific byte swapping macros */
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+
+static inline unsigned short wpa_swap_16(unsigned short v)
+{
+ return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int wpa_swap_32(unsigned int v)
+{
+ return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+ ((v & 0xff0000) >> 8) | (v >> 24);
+}
+
+#define le_to_host16(n) (n)
+#define host_to_le16(n) (n)
+#define be_to_host16(n) wpa_swap_16(n)
+#define host_to_be16(n) wpa_swap_16(n)
+#define le_to_host32(n) (n)
+#define be_to_host32(n) wpa_swap_32(n)
+#define host_to_be32(n) wpa_swap_32(n)
+
+#define WPA_BYTE_SWAP_DEFINED
+
+#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */
+
+
+#ifndef WPA_BYTE_SWAP_DEFINED
+
+#ifndef __BYTE_ORDER
+#ifndef __LITTLE_ENDIAN
+#ifndef __BIG_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#if defined(sparc)
+#define __BYTE_ORDER __BIG_ENDIAN
+#endif
+#endif /* __BIG_ENDIAN */
+#endif /* __LITTLE_ENDIAN */
+#endif /* __BYTE_ORDER */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define le_to_host16(n) ((__force u16) (le16) (n))
+#define host_to_le16(n) ((__force le16) (u16) (n))
+#define be_to_host16(n) bswap_16((__force u16) (be16) (n))
+#define host_to_be16(n) ((__force be16) bswap_16((n)))
+#define le_to_host32(n) ((__force u32) (le32) (n))
+#define host_to_le32(n) ((__force le32) (u32) (n))
+#define be_to_host32(n) bswap_32((__force u32) (be32) (n))
+#define host_to_be32(n) ((__force be32) bswap_32((n)))
+#define le_to_host64(n) ((__force u64) (le64) (n))
+#define host_to_le64(n) ((__force le64) (u64) (n))
+#define be_to_host64(n) bswap_64((__force u64) (be64) (n))
+#define host_to_be64(n) ((__force be64) bswap_64((n)))
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define le_to_host16(n) bswap_16(n)
+#define host_to_le16(n) bswap_16(n)
+#define be_to_host16(n) (n)
+#define host_to_be16(n) (n)
+#define le_to_host32(n) bswap_32(n)
+#define be_to_host32(n) (n)
+#define host_to_be32(n) (n)
+#define le_to_host64(n) bswap_64(n)
+#define host_to_le64(n) bswap_64(n)
+#define be_to_host64(n) (n)
+#define host_to_be64(n) (n)
+#ifndef WORDS_BIGENDIAN
+#define WORDS_BIGENDIAN
+#endif
+#else
+#error Could not determine CPU byte order
+#endif
+
+#define WPA_BYTE_SWAP_DEFINED
+#endif /* !WPA_BYTE_SWAP_DEFINED */
+
+
+/* Macros for handling unaligned memory accesses */
+
+#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1]))
+#define WPA_PUT_BE16(a, val) \
+ do { \
+ (a)[0] = ((u16) (val)) >> 8; \
+ (a)[1] = ((u16) (val)) & 0xff; \
+ } while (0)
+
+#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0]))
+#define WPA_PUT_LE16(a, val) \
+ do { \
+ (a)[1] = ((u16) (val)) >> 8; \
+ (a)[0] = ((u16) (val)) & 0xff; \
+ } while (0)
+
+#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \
+ ((u32) (a)[2]))
+#define WPA_PUT_BE24(a, val) \
+ do { \
+ (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \
+ (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \
+ (a)[2] = (u8) (((u32) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \
+ (((u32) (a)[2]) << 8) | ((u32) (a)[3]))
+#define WPA_PUT_BE32(a, val) \
+ do { \
+ (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \
+ (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \
+ (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \
+ (a)[3] = (u8) (((u32) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \
+ (((u32) (a)[1]) << 8) | ((u32) (a)[0]))
+#define WPA_PUT_LE32(a, val) \
+ do { \
+ (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \
+ (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \
+ (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \
+ (a)[0] = (u8) (((u32) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \
+ (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \
+ (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \
+ (((u64) (a)[6]) << 8) | ((u64) (a)[7]))
+#define WPA_PUT_BE64(a, val) \
+ do { \
+ (a)[0] = (u8) (((u64) (val)) >> 56); \
+ (a)[1] = (u8) (((u64) (val)) >> 48); \
+ (a)[2] = (u8) (((u64) (val)) >> 40); \
+ (a)[3] = (u8) (((u64) (val)) >> 32); \
+ (a)[4] = (u8) (((u64) (val)) >> 24); \
+ (a)[5] = (u8) (((u64) (val)) >> 16); \
+ (a)[6] = (u8) (((u64) (val)) >> 8); \
+ (a)[7] = (u8) (((u64) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \
+ (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \
+ (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \
+ (((u64) (a)[1]) << 8) | ((u64) (a)[0]))
+
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+#ifndef ETH_P_ALL
+#define ETH_P_ALL 0x0003
+#endif
+#ifndef ETH_P_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 */
+
+const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len);
+
+static inline int is_zero_ether_addr(const u8 *a)
+{
+ return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
+}
+
+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"
+
+
+/*
+ * 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 <valgrind/memcheck.h>
+#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/src/utils/edit.c b/src/utils/edit.c
new file mode 100644
index 0000000..8f9e4ed
--- /dev/null
+++ b/src/utils/edit.c
@@ -0,0 +1,1161 @@
+/*
+ * Command line editing and history
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <termios.h>
+
+#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;
+
+#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; 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)) {
+ cmdbuf[cmdbuf_len] = '\0';
+ history_add(cmdbuf);
+ }
+
+ history_use();
+
+ 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);
+}
+
+
+static void history_next(void)
+{
+ if (history_curr == NULL ||
+ history_curr ==
+ dl_list_first(&history_list, struct edit_history, list))
+ 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);
+ 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)
+{
+
+ if (cmdbuf_len == 0) {
+ printf("\n> ");
+ 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("> ");
+ 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-[<param1>;<param2><last> */
+
+ 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<param1>;<param2><last> */
+
+ 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)
+{
+ 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);
+
+ printf("> ");
+ 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", cmdbuf);
+ if (cmdbuf_pos != cmdbuf_len) {
+ tmp = cmdbuf[cmdbuf_pos];
+ cmdbuf[cmdbuf_pos] = '\0';
+ printf("\r> %s", cmdbuf);
+ cmdbuf[cmdbuf_pos] = tmp;
+ }
+ fflush(stdout);
+}
diff --git a/src/utils/edit.h b/src/utils/edit.h
new file mode 100644
index 0000000..fc4474b
--- /dev/null
+++ b/src/utils/edit.h
@@ -0,0 +1,27 @@
+/*
+ * Command line editing and history
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#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);
+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/src/utils/edit_readline.c b/src/utils/edit_readline.c
new file mode 100644
index 0000000..1fef7b9
--- /dev/null
+++ b/src/utils/edit_readline.c
@@ -0,0 +1,184 @@
+/*
+ * Command line editing and history wrapper for readline
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#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)
+{
+ 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);
+
+ 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_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) {
+ os_free(h->line);
+ free(h->data);
+ os_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/src/utils/edit_simple.c b/src/utils/edit_simple.c
new file mode 100644
index 0000000..61fb24e
--- /dev/null
+++ b/src/utils/edit_simple.c
@@ -0,0 +1,96 @@
+/*
+ * Minimal command line editing
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "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 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("> ");
+ 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)
+{
+ 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);
+
+ printf("> ");
+ 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/src/utils/eloop.c b/src/utils/eloop.c
new file mode 100644
index 0000000..b550c63
--- /dev/null
+++ b/src/utils/eloop.c
@@ -0,0 +1,627 @@
+/*
+ * Event loop based on select() loop
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "trace.h"
+#include "list.h"
+#include "eloop.h"
+
+
+struct eloop_sock {
+ int sock;
+ void *eloop_data;
+ void *user_data;
+ eloop_sock_handler handler;
+ WPA_TRACE_REF(eloop);
+ WPA_TRACE_REF(user);
+ WPA_TRACE_INFO
+};
+
+struct eloop_timeout {
+ struct dl_list list;
+ struct os_time time;
+ void *eloop_data;
+ void *user_data;
+ eloop_timeout_handler handler;
+ WPA_TRACE_REF(eloop);
+ WPA_TRACE_REF(user);
+ WPA_TRACE_INFO
+};
+
+struct eloop_signal {
+ int sig;
+ void *user_data;
+ eloop_signal_handler handler;
+ int signaled;
+};
+
+struct eloop_sock_table {
+ int count;
+ struct eloop_sock *table;
+ int changed;
+};
+
+struct eloop_data {
+ int max_sock;
+
+ struct eloop_sock_table readers;
+ struct eloop_sock_table writers;
+ struct eloop_sock_table exceptions;
+
+ struct dl_list timeout;
+
+ int signal_count;
+ struct eloop_signal *signals;
+ int signaled;
+ int pending_terminate;
+
+ int terminate;
+ int reader_table_changed;
+};
+
+static struct eloop_data eloop;
+
+
+#ifdef WPA_TRACE
+
+static void eloop_sigsegv_handler(int sig)
+{
+ wpa_trace_show("eloop SIGSEGV");
+ abort();
+}
+
+static void eloop_trace_sock_add_ref(struct eloop_sock_table *table)
+{
+ int i;
+ if (table == NULL || table->table == NULL)
+ return;
+ for (i = 0; i < table->count; i++) {
+ wpa_trace_add_ref(&table->table[i], eloop,
+ table->table[i].eloop_data);
+ wpa_trace_add_ref(&table->table[i], user,
+ table->table[i].user_data);
+ }
+}
+
+
+static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table)
+{
+ int i;
+ if (table == NULL || table->table == NULL)
+ return;
+ for (i = 0; i < table->count; i++) {
+ wpa_trace_remove_ref(&table->table[i], eloop,
+ table->table[i].eloop_data);
+ wpa_trace_remove_ref(&table->table[i], user,
+ table->table[i].user_data);
+ }
+}
+
+#else /* WPA_TRACE */
+
+#define eloop_trace_sock_add_ref(table) do { } while (0)
+#define eloop_trace_sock_remove_ref(table) do { } while (0)
+
+#endif /* WPA_TRACE */
+
+
+int eloop_init(void)
+{
+ os_memset(&eloop, 0, sizeof(eloop));
+ dl_list_init(&eloop.timeout);
+#ifdef WPA_TRACE
+ signal(SIGSEGV, eloop_sigsegv_handler);
+#endif /* WPA_TRACE */
+ return 0;
+}
+
+
+static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
+ int sock, eloop_sock_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_sock *tmp;
+
+ if (table == NULL)
+ return -1;
+
+ eloop_trace_sock_remove_ref(table);
+ tmp = (struct eloop_sock *)
+ os_realloc(table->table,
+ (table->count + 1) * sizeof(struct eloop_sock));
+ if (tmp == NULL)
+ return -1;
+
+ tmp[table->count].sock = sock;
+ tmp[table->count].eloop_data = eloop_data;
+ tmp[table->count].user_data = user_data;
+ tmp[table->count].handler = handler;
+ wpa_trace_record(&tmp[table->count]);
+ table->count++;
+ table->table = tmp;
+ if (sock > eloop.max_sock)
+ eloop.max_sock = sock;
+ table->changed = 1;
+ eloop_trace_sock_add_ref(table);
+
+ return 0;
+}
+
+
+static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
+ int sock)
+{
+ int i;
+
+ if (table == NULL || table->table == NULL || table->count == 0)
+ return;
+
+ for (i = 0; i < table->count; i++) {
+ if (table->table[i].sock == sock)
+ break;
+ }
+ if (i == table->count)
+ return;
+ eloop_trace_sock_remove_ref(table);
+ if (i != table->count - 1) {
+ os_memmove(&table->table[i], &table->table[i + 1],
+ (table->count - i - 1) *
+ sizeof(struct eloop_sock));
+ }
+ table->count--;
+ table->changed = 1;
+ eloop_trace_sock_add_ref(table);
+}
+
+
+static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
+ fd_set *fds)
+{
+ int i;
+
+ FD_ZERO(fds);
+
+ if (table->table == NULL)
+ return;
+
+ for (i = 0; i < table->count; i++)
+ FD_SET(table->table[i].sock, fds);
+}
+
+
+static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
+ fd_set *fds)
+{
+ int i;
+
+ if (table == NULL || table->table == NULL)
+ return;
+
+ table->changed = 0;
+ for (i = 0; i < table->count; i++) {
+ if (FD_ISSET(table->table[i].sock, fds)) {
+ table->table[i].handler(table->table[i].sock,
+ table->table[i].eloop_data,
+ table->table[i].user_data);
+ if (table->changed)
+ break;
+ }
+ }
+}
+
+
+static void eloop_sock_table_destroy(struct eloop_sock_table *table)
+{
+ if (table) {
+ int i;
+ for (i = 0; i < table->count && table->table; i++) {
+ wpa_printf(MSG_INFO, "ELOOP: remaining socket: "
+ "sock=%d eloop_data=%p user_data=%p "
+ "handler=%p",
+ table->table[i].sock,
+ table->table[i].eloop_data,
+ table->table[i].user_data,
+ table->table[i].handler);
+ wpa_trace_dump_funcname("eloop unregistered socket "
+ "handler",
+ table->table[i].handler);
+ wpa_trace_dump("eloop sock", &table->table[i]);
+ }
+ os_free(table->table);
+ }
+}
+
+
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+ void *eloop_data, void *user_data)
+{
+ return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
+ eloop_data, user_data);
+}
+
+
+void eloop_unregister_read_sock(int sock)
+{
+ eloop_unregister_sock(sock, EVENT_TYPE_READ);
+}
+
+
+static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type)
+{
+ switch (type) {
+ case EVENT_TYPE_READ:
+ return &eloop.readers;
+ case EVENT_TYPE_WRITE:
+ return &eloop.writers;
+ case EVENT_TYPE_EXCEPTION:
+ return &eloop.exceptions;
+ }
+
+ return NULL;
+}
+
+
+int eloop_register_sock(int sock, eloop_event_type type,
+ eloop_sock_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_sock_table *table;
+
+ table = eloop_get_sock_table(type);
+ return eloop_sock_table_add_sock(table, sock, handler,
+ eloop_data, user_data);
+}
+
+
+void eloop_unregister_sock(int sock, eloop_event_type type)
+{
+ struct eloop_sock_table *table;
+
+ table = eloop_get_sock_table(type);
+ eloop_sock_table_remove_sock(table, sock);
+}
+
+
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+ eloop_timeout_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *timeout, *tmp;
+ os_time_t now_sec;
+
+ timeout = os_zalloc(sizeof(*timeout));
+ if (timeout == NULL)
+ return -1;
+ if (os_get_time(&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_time_before(&timeout->time, &tmp->time)) {
+ dl_list_add(tmp->list.prev, &timeout->list);
+ return 0;
+ }
+ }
+ dl_list_add_tail(&eloop.timeout, &timeout->list);
+
+ return 0;
+}
+
+
+static void eloop_remove_timeout(struct eloop_timeout *timeout)
+{
+ dl_list_del(&timeout->list);
+ wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data);
+ wpa_trace_remove_ref(timeout, user, timeout->user_data);
+ os_free(timeout);
+}
+
+
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *timeout, *prev;
+ int removed = 0;
+
+ dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+ struct eloop_timeout, list) {
+ if (timeout->handler == handler &&
+ (timeout->eloop_data == eloop_data ||
+ eloop_data == ELOOP_ALL_CTX) &&
+ (timeout->user_data == user_data ||
+ user_data == ELOOP_ALL_CTX)) {
+ eloop_remove_timeout(timeout);
+ removed++;
+ }
+ }
+
+ return removed;
+}
+
+
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *tmp;
+
+ dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+ if (tmp->handler == handler &&
+ tmp->eloop_data == eloop_data &&
+ tmp->user_data == user_data)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static void eloop_handle_alarm(int sig)
+{
+ wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in "
+ "two seconds. Looks like there\n"
+ "is a bug that ends up in a busy loop that "
+ "prevents clean shutdown.\n"
+ "Killing program forcefully.\n");
+ exit(1);
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static void eloop_handle_signal(int sig)
+{
+ int i;
+
+#ifndef CONFIG_NATIVE_WINDOWS
+ if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
+ /* Use SIGALRM to break out from potential busy loops that
+ * would not allow the program to be killed. */
+ eloop.pending_terminate = 1;
+ signal(SIGALRM, eloop_handle_alarm);
+ alarm(2);
+ }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ eloop.signaled++;
+ for (i = 0; i < eloop.signal_count; i++) {
+ if (eloop.signals[i].sig == sig) {
+ eloop.signals[i].signaled++;
+ break;
+ }
+ }
+}
+
+
+static void eloop_process_pending_signals(void)
+{
+ int i;
+
+ if (eloop.signaled == 0)
+ return;
+ eloop.signaled = 0;
+
+ if (eloop.pending_terminate) {
+#ifndef CONFIG_NATIVE_WINDOWS
+ alarm(0);
+#endif /* CONFIG_NATIVE_WINDOWS */
+ eloop.pending_terminate = 0;
+ }
+
+ for (i = 0; i < eloop.signal_count; i++) {
+ if (eloop.signals[i].signaled) {
+ eloop.signals[i].signaled = 0;
+ eloop.signals[i].handler(eloop.signals[i].sig,
+ eloop.signals[i].user_data);
+ }
+ }
+}
+
+
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+ void *user_data)
+{
+ struct eloop_signal *tmp;
+
+ tmp = (struct eloop_signal *)
+ os_realloc(eloop.signals,
+ (eloop.signal_count + 1) *
+ sizeof(struct eloop_signal));
+ if (tmp == NULL)
+ return -1;
+
+ tmp[eloop.signal_count].sig = sig;
+ tmp[eloop.signal_count].user_data = user_data;
+ tmp[eloop.signal_count].handler = handler;
+ tmp[eloop.signal_count].signaled = 0;
+ eloop.signal_count++;
+ eloop.signals = tmp;
+ signal(sig, eloop_handle_signal);
+
+ return 0;
+}
+
+
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+ void *user_data)
+{
+ int ret = eloop_register_signal(SIGINT, handler, user_data);
+ if (ret == 0)
+ ret = eloop_register_signal(SIGTERM, handler, user_data);
+ return ret;
+}
+
+
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+ void *user_data)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+ return 0;
+#else /* CONFIG_NATIVE_WINDOWS */
+ return eloop_register_signal(SIGHUP, handler, user_data);
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+void eloop_run(void)
+{
+ fd_set *rfds, *wfds, *efds;
+ int res;
+ struct timeval _tv;
+ struct os_time tv, now;
+
+ rfds = os_malloc(sizeof(*rfds));
+ wfds = os_malloc(sizeof(*wfds));
+ efds = os_malloc(sizeof(*efds));
+ if (rfds == NULL || wfds == NULL || efds == NULL)
+ goto out;
+
+ while (!eloop.terminate &&
+ (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
+ eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
+ struct eloop_timeout *timeout;
+ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+ list);
+ if (timeout) {
+ os_get_time(&now);
+ if (os_time_before(&now, &timeout->time))
+ os_time_sub(&timeout->time, &now, &tv);
+ else
+ tv.sec = tv.usec = 0;
+ _tv.tv_sec = tv.sec;
+ _tv.tv_usec = tv.usec;
+ }
+
+ eloop_sock_table_set_fds(&eloop.readers, rfds);
+ eloop_sock_table_set_fds(&eloop.writers, wfds);
+ eloop_sock_table_set_fds(&eloop.exceptions, efds);
+ res = select(eloop.max_sock + 1, rfds, wfds, efds,
+ timeout ? &_tv : NULL);
+ if (res < 0 && errno != EINTR && errno != 0) {
+ perror("select");
+ goto out;
+ }
+ eloop_process_pending_signals();
+
+ /* check if some registered timeouts have occurred */
+ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+ list);
+ if (timeout) {
+ os_get_time(&now);
+ if (!os_time_before(&now, &timeout->time)) {
+ void *eloop_data = timeout->eloop_data;
+ void *user_data = timeout->user_data;
+ eloop_timeout_handler handler =
+ timeout->handler;
+ eloop_remove_timeout(timeout);
+ handler(eloop_data, user_data);
+ }
+
+ }
+
+ if (res <= 0)
+ continue;
+
+ eloop_sock_table_dispatch(&eloop.readers, rfds);
+ eloop_sock_table_dispatch(&eloop.writers, wfds);
+ eloop_sock_table_dispatch(&eloop.exceptions, efds);
+ }
+
+out:
+ os_free(rfds);
+ os_free(wfds);
+ os_free(efds);
+}
+
+
+void eloop_terminate(void)
+{
+ eloop.terminate = 1;
+}
+
+
+void eloop_destroy(void)
+{
+ struct eloop_timeout *timeout, *prev;
+ struct os_time now;
+
+ os_get_time(&now);
+ dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+ struct eloop_timeout, list) {
+ int sec, usec;
+ sec = timeout->time.sec - now.sec;
+ usec = timeout->time.usec - now.usec;
+ if (timeout->time.usec < now.usec) {
+ sec--;
+ usec += 1000000;
+ }
+ wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d "
+ "eloop_data=%p user_data=%p handler=%p",
+ sec, usec, timeout->eloop_data, timeout->user_data,
+ timeout->handler);
+ wpa_trace_dump_funcname("eloop unregistered timeout handler",
+ timeout->handler);
+ wpa_trace_dump("eloop timeout", timeout);
+ eloop_remove_timeout(timeout);
+ }
+ eloop_sock_table_destroy(&eloop.readers);
+ eloop_sock_table_destroy(&eloop.writers);
+ eloop_sock_table_destroy(&eloop.exceptions);
+ os_free(eloop.signals);
+}
+
+
+int eloop_terminated(void)
+{
+ return eloop.terminate;
+}
+
+
+void eloop_wait_for_read_sock(int sock)
+{
+ fd_set rfds;
+
+ if (sock < 0)
+ return;
+
+ FD_ZERO(&rfds);
+ FD_SET(sock, &rfds);
+ select(sock + 1, &rfds, NULL, NULL, NULL);
+}
diff --git a/src/utils/eloop.h b/src/utils/eloop.h
new file mode 100644
index 0000000..1228f24
--- /dev/null
+++ b/src/utils/eloop.h
@@ -0,0 +1,316 @@
+/*
+ * Event loop
+ * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file defines an event loop interface that supports processing events
+ * from registered timeouts (i.e., do something after N seconds), sockets
+ * (e.g., a new packet available for reading), and signals. eloop.c is an
+ * implementation of this interface using select() and sockets. This is
+ * suitable for most UNIX/POSIX systems. When porting to other operating
+ * systems, it may be necessary to replace that implementation with OS specific
+ * mechanisms.
+ */
+
+#ifndef ELOOP_H
+#define ELOOP_H
+
+/**
+ * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts
+ */
+#define ELOOP_ALL_CTX (void *) -1
+
+/**
+ * eloop_event_type - eloop socket event type for eloop_register_sock()
+ * @EVENT_TYPE_READ: Socket has data available for reading
+ * @EVENT_TYPE_WRITE: Socket has room for new data to be written
+ * @EVENT_TYPE_EXCEPTION: An exception has been reported
+ */
+typedef enum {
+ EVENT_TYPE_READ = 0,
+ EVENT_TYPE_WRITE,
+ EVENT_TYPE_EXCEPTION
+} eloop_event_type;
+
+/**
+ * eloop_sock_handler - eloop socket event callback type
+ * @sock: File descriptor number for the socket
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx);
+
+/**
+ * eloop_event_handler - eloop generic event callback type
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx);
+
+/**
+ * eloop_timeout_handler - eloop timeout event callback type
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx);
+
+/**
+ * eloop_signal_handler - eloop signal event callback type
+ * @sig: Signal number
+ * @signal_ctx: Registered callback context data (user_data from
+ * eloop_register_signal(), eloop_register_signal_terminate(), or
+ * eloop_register_signal_reconfig() call)
+ */
+typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+
+/**
+ * eloop_init() - Initialize global event loop data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before any other eloop_* function.
+ */
+int eloop_init(void);
+
+/**
+ * eloop_register_read_sock - Register handler for read events
+ * @sock: File descriptor number for the socket
+ * @handler: Callback function to be called when data is available for reading
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a read socket notifier for the given file descriptor. The handler
+ * function will be called whenever data is available for reading from the
+ * socket. The handler function is responsible for clearing the event after
+ * having processed it in order to avoid eloop from calling the handler again
+ * for the same event.
+ */
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_read_sock - Unregister handler for read events
+ * @sock: File descriptor number for the socket
+ *
+ * Unregister a read socket notifier that was previously registered with
+ * eloop_register_read_sock().
+ */
+void eloop_unregister_read_sock(int sock);
+
+/**
+ * eloop_register_sock - Register handler for socket events
+ * @sock: File descriptor number for the socket
+ * @type: Type of event to wait for
+ * @handler: Callback function to be called when the event is triggered
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register an event notifier for the given socket's file descriptor. The
+ * handler function will be called whenever the that event is triggered for the
+ * socket. The handler function is responsible for clearing the event after
+ * having processed it in order to avoid eloop from calling the handler again
+ * for the same event.
+ */
+int eloop_register_sock(int sock, eloop_event_type type,
+ eloop_sock_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_sock - Unregister handler for socket events
+ * @sock: File descriptor number for the socket
+ * @type: Type of event for which sock was registered
+ *
+ * Unregister a socket event notifier that was previously registered with
+ * eloop_register_sock().
+ */
+void eloop_unregister_sock(int sock, eloop_event_type type);
+
+/**
+ * eloop_register_event - Register handler for generic events
+ * @event: Event to wait (eloop implementation specific)
+ * @event_size: Size of event data
+ * @handler: Callback function to be called when event is triggered
+ * @eloop_data: Callback context data (eloop_data)
+ * @user_data: Callback context data (user_data)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register an event handler for the given event. This function is used to
+ * register eloop implementation specific events which are mainly targetted for
+ * operating system specific code (driver interface and l2_packet) since the
+ * portable code will not be able to use such an OS-specific call. The handler
+ * function will be called whenever the event is triggered. The handler
+ * function is responsible for clearing the event after having processed it in
+ * order to avoid eloop from calling the handler again for the same event.
+ *
+ * In case of Windows implementation (eloop_win.c), event pointer is of HANDLE
+ * type, i.e., void*. The callers are likely to have 'HANDLE h' type variable,
+ * and they would call this function with eloop_register_event(h, sizeof(h),
+ * ...).
+ */
+int eloop_register_event(void *event, size_t event_size,
+ eloop_event_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_event - Unregister handler for a generic event
+ * @event: Event to cancel (eloop implementation specific)
+ * @event_size: Size of event data
+ *
+ * Unregister a generic event notifier that was previously registered with
+ * eloop_register_event().
+ */
+void eloop_unregister_event(void *event, size_t event_size);
+
+/**
+ * eloop_register_timeout - Register timeout
+ * @secs: Number of seconds to the timeout
+ * @usecs: Number of microseconds to the timeout
+ * @handler: Callback function to be called when timeout occurs
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a timeout that will cause the handler function to be called after
+ * given time.
+ */
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+ eloop_timeout_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_cancel_timeout - Cancel timeouts
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all
+ * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all
+ * Returns: Number of cancelled timeouts
+ *
+ * Cancel matching <handler,eloop_data,user_data> timeouts registered with
+ * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for
+ * cancelling all timeouts regardless of eloop_data/user_data.
+ */
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_is_timeout_registered - Check if a timeout is already registered
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is registered, 0 if the timeout is not registered
+ *
+ * Determine if a matching <handler,eloop_data,user_data> timeout is registered
+ * with eloop_register_timeout().
+ */
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data);
+
+/**
+ * eloop_register_signal - Register handler for signals
+ * @sig: Signal number (e.g., SIGHUP)
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a signal is received.
+ * The callback function is actually called only after the system signal
+ * handler has returned. This means that the normal limits for sighandlers
+ * (i.e., only "safe functions" allowed) do not apply for the registered
+ * callback.
+ */
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+ void *user_data);
+
+/**
+ * eloop_register_signal_terminate - Register handler for terminate signals
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a process termination
+ * signal is received. The callback function is actually called only after the
+ * system signal handler has returned. This means that the normal limits for
+ * sighandlers (i.e., only "safe functions" allowed) do not apply for the
+ * registered callback.
+ *
+ * This function is a more portable version of eloop_register_signal() since
+ * the knowledge of exact details of the signals is hidden in eloop
+ * implementation. In case of operating systems using signal(), this function
+ * registers handlers for SIGINT and SIGTERM.
+ */
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+ void *user_data);
+
+/**
+ * eloop_register_signal_reconfig - Register handler for reconfig signals
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a reconfiguration /
+ * hangup signal is received. The callback function is actually called only
+ * after the system signal handler has returned. This means that the normal
+ * limits for sighandlers (i.e., only "safe functions" allowed) do not apply
+ * for the registered callback.
+ *
+ * This function is a more portable version of eloop_register_signal() since
+ * the knowledge of exact details of the signals is hidden in eloop
+ * implementation. In case of operating systems using signal(), this function
+ * registers a handler for SIGHUP.
+ */
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+ void *user_data);
+
+/**
+ * eloop_run - Start the event loop
+ *
+ * Start the event loop and continue running as long as there are any
+ * registered event handlers. This function is run after event loop has been
+ * initialized with event_init() and one or more events have been registered.
+ */
+void eloop_run(void);
+
+/**
+ * eloop_terminate - Terminate event loop
+ *
+ * Terminate event loop even if there are registered events. This can be used
+ * to request the program to be terminated cleanly.
+ */
+void eloop_terminate(void);
+
+/**
+ * eloop_destroy - Free any resources allocated for the event loop
+ *
+ * After calling eloop_destroy(), other eloop_* functions must not be called
+ * before re-running eloop_init().
+ */
+void eloop_destroy(void);
+
+/**
+ * eloop_terminated - Check whether event loop has been terminated
+ * Returns: 1 = event loop terminate, 0 = event loop still running
+ *
+ * This function can be used to check whether eloop_terminate() has been called
+ * to request termination of the event loop. This is normally used to abort
+ * operations that may still be queued to be run when eloop_terminate() was
+ * called.
+ */
+int eloop_terminated(void);
+
+/**
+ * eloop_wait_for_read_sock - Wait for a single reader
+ * @sock: File descriptor number for the socket
+ *
+ * Do a blocking wait for a single read socket.
+ */
+void eloop_wait_for_read_sock(int sock);
+
+#endif /* ELOOP_H */
diff --git a/src/utils/eloop_none.c b/src/utils/eloop_none.c
new file mode 100644
index 0000000..18eae4e
--- /dev/null
+++ b/src/utils/eloop_none.c
@@ -0,0 +1,401 @@
+/*
+ * Event loop - empty template (basic structure, but no OS specific operations)
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+
+
+struct eloop_sock {
+ int sock;
+ void *eloop_data;
+ void *user_data;
+ void (*handler)(int sock, void *eloop_ctx, void *sock_ctx);
+};
+
+struct eloop_timeout {
+ struct os_time time;
+ void *eloop_data;
+ void *user_data;
+ void (*handler)(void *eloop_ctx, void *sock_ctx);
+ struct eloop_timeout *next;
+};
+
+struct eloop_signal {
+ int sig;
+ void *user_data;
+ void (*handler)(int sig, void *eloop_ctx, void *signal_ctx);
+ int signaled;
+};
+
+struct eloop_data {
+ int max_sock, reader_count;
+ struct eloop_sock *readers;
+
+ struct eloop_timeout *timeout;
+
+ int signal_count;
+ struct eloop_signal *signals;
+ int signaled;
+ int pending_terminate;
+
+ int terminate;
+ int reader_table_changed;
+};
+
+static struct eloop_data eloop;
+
+
+int eloop_init(void)
+{
+ memset(&eloop, 0, sizeof(eloop));
+ return 0;
+}
+
+
+int eloop_register_read_sock(int sock,
+ void (*handler)(int sock, void *eloop_ctx,
+ void *sock_ctx),
+ void *eloop_data, void *user_data)
+{
+ struct eloop_sock *tmp;
+
+ tmp = (struct eloop_sock *)
+ realloc(eloop.readers,
+ (eloop.reader_count + 1) * sizeof(struct eloop_sock));
+ if (tmp == NULL)
+ return -1;
+
+ tmp[eloop.reader_count].sock = sock;
+ tmp[eloop.reader_count].eloop_data = eloop_data;
+ tmp[eloop.reader_count].user_data = user_data;
+ tmp[eloop.reader_count].handler = handler;
+ eloop.reader_count++;
+ eloop.readers = tmp;
+ if (sock > eloop.max_sock)
+ eloop.max_sock = sock;
+ eloop.reader_table_changed = 1;
+
+ return 0;
+}
+
+
+void eloop_unregister_read_sock(int sock)
+{
+ int i;
+
+ if (eloop.readers == NULL || eloop.reader_count == 0)
+ return;
+
+ for (i = 0; i < eloop.reader_count; i++) {
+ if (eloop.readers[i].sock == sock)
+ break;
+ }
+ if (i == eloop.reader_count)
+ return;
+ if (i != eloop.reader_count - 1) {
+ memmove(&eloop.readers[i], &eloop.readers[i + 1],
+ (eloop.reader_count - i - 1) *
+ sizeof(struct eloop_sock));
+ }
+ eloop.reader_count--;
+ eloop.reader_table_changed = 1;
+}
+
+
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+ void (*handler)(void *eloop_ctx, void *timeout_ctx),
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *timeout, *tmp, *prev;
+
+ timeout = (struct eloop_timeout *) malloc(sizeof(*timeout));
+ if (timeout == NULL)
+ return -1;
+ os_get_time(&timeout->time);
+ timeout->time.sec += secs;
+ timeout->time.usec += usecs;
+ while (timeout->time.usec >= 1000000) {
+ timeout->time.sec++;
+ timeout->time.usec -= 1000000;
+ }
+ timeout->eloop_data = eloop_data;
+ timeout->user_data = user_data;
+ timeout->handler = handler;
+ timeout->next = NULL;
+
+ if (eloop.timeout == NULL) {
+ eloop.timeout = timeout;
+ return 0;
+ }
+
+ prev = NULL;
+ tmp = eloop.timeout;
+ while (tmp != NULL) {
+ if (os_time_before(&timeout->time, &tmp->time))
+ break;
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ if (prev == NULL) {
+ timeout->next = eloop.timeout;
+ eloop.timeout = timeout;
+ } else {
+ timeout->next = prev->next;
+ prev->next = timeout;
+ }
+
+ return 0;
+}
+
+
+int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx),
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *timeout, *prev, *next;
+ int removed = 0;
+
+ prev = NULL;
+ timeout = eloop.timeout;
+ while (timeout != NULL) {
+ next = timeout->next;
+
+ if (timeout->handler == handler &&
+ (timeout->eloop_data == eloop_data ||
+ eloop_data == ELOOP_ALL_CTX) &&
+ (timeout->user_data == user_data ||
+ user_data == ELOOP_ALL_CTX)) {
+ if (prev == NULL)
+ eloop.timeout = next;
+ else
+ prev->next = next;
+ free(timeout);
+ removed++;
+ } else
+ prev = timeout;
+
+ timeout = next;
+ }
+
+ return removed;
+}
+
+
+int eloop_is_timeout_registered(void (*handler)(void *eloop_ctx,
+ void *timeout_ctx),
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *tmp;
+
+ tmp = eloop.timeout;
+ while (tmp != NULL) {
+ if (tmp->handler == handler &&
+ tmp->eloop_data == eloop_data &&
+ tmp->user_data == user_data)
+ return 1;
+
+ tmp = tmp->next;
+ }
+
+ return 0;
+}
+
+
+/* TODO: replace with suitable signal handler */
+#if 0
+static void eloop_handle_signal(int sig)
+{
+ int i;
+
+ eloop.signaled++;
+ for (i = 0; i < eloop.signal_count; i++) {
+ if (eloop.signals[i].sig == sig) {
+ eloop.signals[i].signaled++;
+ break;
+ }
+ }
+}
+#endif
+
+
+static void eloop_process_pending_signals(void)
+{
+ int i;
+
+ if (eloop.signaled == 0)
+ return;
+ eloop.signaled = 0;
+
+ if (eloop.pending_terminate) {
+ eloop.pending_terminate = 0;
+ }
+
+ for (i = 0; i < eloop.signal_count; i++) {
+ if (eloop.signals[i].signaled) {
+ eloop.signals[i].signaled = 0;
+ eloop.signals[i].handler(eloop.signals[i].sig,
+ eloop.user_data,
+ eloop.signals[i].user_data);
+ }
+ }
+}
+
+
+int eloop_register_signal(int sig,
+ void (*handler)(int sig, void *eloop_ctx,
+ void *signal_ctx),
+ void *user_data)
+{
+ struct eloop_signal *tmp;
+
+ tmp = (struct eloop_signal *)
+ realloc(eloop.signals,
+ (eloop.signal_count + 1) *
+ sizeof(struct eloop_signal));
+ if (tmp == NULL)
+ return -1;
+
+ tmp[eloop.signal_count].sig = sig;
+ tmp[eloop.signal_count].user_data = user_data;
+ tmp[eloop.signal_count].handler = handler;
+ tmp[eloop.signal_count].signaled = 0;
+ eloop.signal_count++;
+ eloop.signals = tmp;
+
+ /* TODO: register signal handler */
+
+ return 0;
+}
+
+
+int eloop_register_signal_terminate(void (*handler)(int sig, void *eloop_ctx,
+ void *signal_ctx),
+ void *user_data)
+{
+#if 0
+ /* TODO: for example */
+ int ret = eloop_register_signal(SIGINT, handler, user_data);
+ if (ret == 0)
+ ret = eloop_register_signal(SIGTERM, handler, user_data);
+ return ret;
+#endif
+ return 0;
+}
+
+
+int eloop_register_signal_reconfig(void (*handler)(int sig, void *eloop_ctx,
+ void *signal_ctx),
+ void *user_data)
+{
+#if 0
+ /* TODO: for example */
+ return eloop_register_signal(SIGHUP, handler, user_data);
+#endif
+ return 0;
+}
+
+
+void eloop_run(void)
+{
+ int i;
+ struct os_time tv, now;
+
+ while (!eloop.terminate &&
+ (eloop.timeout || eloop.reader_count > 0)) {
+ if (eloop.timeout) {
+ os_get_time(&now);
+ if (os_time_before(&now, &eloop.timeout->time))
+ os_time_sub(&eloop.timeout->time, &now, &tv);
+ else
+ tv.sec = tv.usec = 0;
+ }
+
+ /*
+ * TODO: wait for any event (read socket ready, timeout (tv),
+ * signal
+ */
+ os_sleep(1, 0); /* just a dummy wait for testing */
+
+ eloop_process_pending_signals();
+
+ /* check if some registered timeouts have occurred */
+ if (eloop.timeout) {
+ struct eloop_timeout *tmp;
+
+ os_get_time(&now);
+ if (!os_time_before(&now, &eloop.timeout->time)) {
+ tmp = eloop.timeout;
+ eloop.timeout = eloop.timeout->next;
+ tmp->handler(tmp->eloop_data,
+ tmp->user_data);
+ free(tmp);
+ }
+
+ }
+
+ eloop.reader_table_changed = 0;
+ for (i = 0; i < eloop.reader_count; i++) {
+ /*
+ * TODO: call each handler that has pending data to
+ * read
+ */
+ if (0 /* TODO: eloop.readers[i].sock ready */) {
+ eloop.readers[i].handler(
+ eloop.readers[i].sock,
+ eloop.readers[i].eloop_data,
+ eloop.readers[i].user_data);
+ if (eloop.reader_table_changed)
+ break;
+ }
+ }
+ }
+}
+
+
+void eloop_terminate(void)
+{
+ eloop.terminate = 1;
+}
+
+
+void eloop_destroy(void)
+{
+ struct eloop_timeout *timeout, *prev;
+
+ timeout = eloop.timeout;
+ while (timeout != NULL) {
+ prev = timeout;
+ timeout = timeout->next;
+ free(prev);
+ }
+ free(eloop.readers);
+ free(eloop.signals);
+}
+
+
+int eloop_terminated(void)
+{
+ return eloop.terminate;
+}
+
+
+void eloop_wait_for_read_sock(int sock)
+{
+ /*
+ * TODO: wait for the file descriptor to have something available for
+ * reading
+ */
+}
diff --git a/src/utils/eloop_win.c b/src/utils/eloop_win.c
new file mode 100644
index 0000000..c726ece
--- /dev/null
+++ b/src/utils/eloop_win.c
@@ -0,0 +1,623 @@
+/*
+ * Event loop based on Windows events and WaitForMultipleObjects
+ * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <winsock2.h>
+
+#include "common.h"
+#include "eloop.h"
+
+
+struct eloop_sock {
+ int sock;
+ void *eloop_data;
+ void *user_data;
+ eloop_sock_handler handler;
+ WSAEVENT event;
+};
+
+struct eloop_event {
+ void *eloop_data;
+ void *user_data;
+ eloop_event_handler handler;
+ HANDLE event;
+};
+
+struct eloop_timeout {
+ struct os_time time;
+ void *eloop_data;
+ void *user_data;
+ eloop_timeout_handler handler;
+ struct eloop_timeout *next;
+};
+
+struct eloop_signal {
+ int sig;
+ void *user_data;
+ eloop_signal_handler handler;
+ int signaled;
+};
+
+struct eloop_data {
+ int max_sock;
+ size_t reader_count;
+ struct eloop_sock *readers;
+
+ size_t event_count;
+ struct eloop_event *events;
+
+ struct eloop_timeout *timeout;
+
+ int signal_count;
+ struct eloop_signal *signals;
+ int signaled;
+ int pending_terminate;
+
+ int terminate;
+ int reader_table_changed;
+
+ struct eloop_signal term_signal;
+ HANDLE term_event;
+
+ HANDLE *handles;
+ size_t num_handles;
+};
+
+static struct eloop_data eloop;
+
+
+int eloop_init(void)
+{
+ os_memset(&eloop, 0, sizeof(eloop));
+ eloop.num_handles = 1;
+ eloop.handles = os_malloc(eloop.num_handles *
+ sizeof(eloop.handles[0]));
+ if (eloop.handles == NULL)
+ return -1;
+
+ eloop.term_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (eloop.term_event == NULL) {
+ printf("CreateEvent() failed: %d\n",
+ (int) GetLastError());
+ os_free(eloop.handles);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eloop_prepare_handles(void)
+{
+ HANDLE *n;
+
+ if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8)
+ return 0;
+ n = os_realloc(eloop.handles,
+ eloop.num_handles * 2 * sizeof(eloop.handles[0]));
+ if (n == NULL)
+ return -1;
+ eloop.handles = n;
+ eloop.num_handles *= 2;
+ return 0;
+}
+
+
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+ void *eloop_data, void *user_data)
+{
+ WSAEVENT event;
+ struct eloop_sock *tmp;
+
+ if (eloop_prepare_handles())
+ return -1;
+
+ event = WSACreateEvent();
+ if (event == WSA_INVALID_EVENT) {
+ printf("WSACreateEvent() failed: %d\n", WSAGetLastError());
+ return -1;
+ }
+
+ if (WSAEventSelect(sock, event, FD_READ)) {
+ printf("WSAEventSelect() failed: %d\n", WSAGetLastError());
+ WSACloseEvent(event);
+ return -1;
+ }
+ tmp = os_realloc(eloop.readers,
+ (eloop.reader_count + 1) * sizeof(struct eloop_sock));
+ if (tmp == NULL) {
+ WSAEventSelect(sock, event, 0);
+ WSACloseEvent(event);
+ return -1;
+ }
+
+ tmp[eloop.reader_count].sock = sock;
+ tmp[eloop.reader_count].eloop_data = eloop_data;
+ tmp[eloop.reader_count].user_data = user_data;
+ tmp[eloop.reader_count].handler = handler;
+ tmp[eloop.reader_count].event = event;
+ eloop.reader_count++;
+ eloop.readers = tmp;
+ if (sock > eloop.max_sock)
+ eloop.max_sock = sock;
+ eloop.reader_table_changed = 1;
+
+ return 0;
+}
+
+
+void eloop_unregister_read_sock(int sock)
+{
+ size_t i;
+
+ if (eloop.readers == NULL || eloop.reader_count == 0)
+ return;
+
+ for (i = 0; i < eloop.reader_count; i++) {
+ if (eloop.readers[i].sock == sock)
+ break;
+ }
+ if (i == eloop.reader_count)
+ return;
+
+ WSAEventSelect(eloop.readers[i].sock, eloop.readers[i].event, 0);
+ WSACloseEvent(eloop.readers[i].event);
+
+ if (i != eloop.reader_count - 1) {
+ os_memmove(&eloop.readers[i], &eloop.readers[i + 1],
+ (eloop.reader_count - i - 1) *
+ sizeof(struct eloop_sock));
+ }
+ eloop.reader_count--;
+ eloop.reader_table_changed = 1;
+}
+
+
+int eloop_register_event(void *event, size_t event_size,
+ eloop_event_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_event *tmp;
+ HANDLE h = event;
+
+ if (event_size != sizeof(HANDLE) || h == INVALID_HANDLE_VALUE)
+ return -1;
+
+ if (eloop_prepare_handles())
+ return -1;
+
+ tmp = os_realloc(eloop.events,
+ (eloop.event_count + 1) * sizeof(struct eloop_event));
+ if (tmp == NULL)
+ return -1;
+
+ tmp[eloop.event_count].eloop_data = eloop_data;
+ tmp[eloop.event_count].user_data = user_data;
+ tmp[eloop.event_count].handler = handler;
+ tmp[eloop.event_count].event = h;
+ eloop.event_count++;
+ eloop.events = tmp;
+
+ return 0;
+}
+
+
+void eloop_unregister_event(void *event, size_t event_size)
+{
+ size_t i;
+ HANDLE h = event;
+
+ if (eloop.events == NULL || eloop.event_count == 0 ||
+ event_size != sizeof(HANDLE))
+ return;
+
+ for (i = 0; i < eloop.event_count; i++) {
+ if (eloop.events[i].event == h)
+ break;
+ }
+ if (i == eloop.event_count)
+ return;
+
+ if (i != eloop.event_count - 1) {
+ os_memmove(&eloop.events[i], &eloop.events[i + 1],
+ (eloop.event_count - i - 1) *
+ sizeof(struct eloop_event));
+ }
+ eloop.event_count--;
+}
+
+
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+ eloop_timeout_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *timeout, *tmp, *prev;
+ os_time_t now_sec;
+
+ timeout = os_malloc(sizeof(*timeout));
+ if (timeout == NULL)
+ return -1;
+ os_get_time(&timeout->time);
+ 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;
+ timeout->next = NULL;
+
+ if (eloop.timeout == NULL) {
+ eloop.timeout = timeout;
+ return 0;
+ }
+
+ prev = NULL;
+ tmp = eloop.timeout;
+ while (tmp != NULL) {
+ if (os_time_before(&timeout->time, &tmp->time))
+ break;
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ if (prev == NULL) {
+ timeout->next = eloop.timeout;
+ eloop.timeout = timeout;
+ } else {
+ timeout->next = prev->next;
+ prev->next = timeout;
+ }
+
+ return 0;
+}
+
+
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *timeout, *prev, *next;
+ int removed = 0;
+
+ prev = NULL;
+ timeout = eloop.timeout;
+ while (timeout != NULL) {
+ next = timeout->next;
+
+ if (timeout->handler == handler &&
+ (timeout->eloop_data == eloop_data ||
+ eloop_data == ELOOP_ALL_CTX) &&
+ (timeout->user_data == user_data ||
+ user_data == ELOOP_ALL_CTX)) {
+ if (prev == NULL)
+ eloop.timeout = next;
+ else
+ prev->next = next;
+ os_free(timeout);
+ removed++;
+ } else
+ prev = timeout;
+
+ timeout = next;
+ }
+
+ return removed;
+}
+
+
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *tmp;
+
+ tmp = eloop.timeout;
+ while (tmp != NULL) {
+ if (tmp->handler == handler &&
+ tmp->eloop_data == eloop_data &&
+ tmp->user_data == user_data)
+ return 1;
+
+ tmp = tmp->next;
+ }
+
+ return 0;
+}
+
+
+/* TODO: replace with suitable signal handler */
+#if 0
+static void eloop_handle_signal(int sig)
+{
+ int i;
+
+ eloop.signaled++;
+ for (i = 0; i < eloop.signal_count; i++) {
+ if (eloop.signals[i].sig == sig) {
+ eloop.signals[i].signaled++;
+ break;
+ }
+ }
+}
+#endif
+
+
+static void eloop_process_pending_signals(void)
+{
+ int i;
+
+ if (eloop.signaled == 0)
+ return;
+ eloop.signaled = 0;
+
+ if (eloop.pending_terminate) {
+ eloop.pending_terminate = 0;
+ }
+
+ for (i = 0; i < eloop.signal_count; i++) {
+ if (eloop.signals[i].signaled) {
+ eloop.signals[i].signaled = 0;
+ eloop.signals[i].handler(eloop.signals[i].sig,
+ eloop.signals[i].user_data);
+ }
+ }
+
+ if (eloop.term_signal.signaled) {
+ eloop.term_signal.signaled = 0;
+ eloop.term_signal.handler(eloop.term_signal.sig,
+ eloop.term_signal.user_data);
+ }
+}
+
+
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+ void *user_data)
+{
+ struct eloop_signal *tmp;
+
+ tmp = os_realloc(eloop.signals,
+ (eloop.signal_count + 1) *
+ sizeof(struct eloop_signal));
+ if (tmp == NULL)
+ return -1;
+
+ tmp[eloop.signal_count].sig = sig;
+ tmp[eloop.signal_count].user_data = user_data;
+ tmp[eloop.signal_count].handler = handler;
+ tmp[eloop.signal_count].signaled = 0;
+ eloop.signal_count++;
+ eloop.signals = tmp;
+
+ /* TODO: register signal handler */
+
+ return 0;
+}
+
+
+#ifndef _WIN32_WCE
+static BOOL eloop_handle_console_ctrl(DWORD type)
+{
+ switch (type) {
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ eloop.signaled++;
+ eloop.term_signal.signaled++;
+ SetEvent(eloop.term_event);
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+#endif /* _WIN32_WCE */
+
+
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+ void *user_data)
+{
+#ifndef _WIN32_WCE
+ if (SetConsoleCtrlHandler((PHANDLER_ROUTINE) eloop_handle_console_ctrl,
+ TRUE) == 0) {
+ printf("SetConsoleCtrlHandler() failed: %d\n",
+ (int) GetLastError());
+ return -1;
+ }
+#endif /* _WIN32_WCE */
+
+ eloop.term_signal.handler = handler;
+ eloop.term_signal.user_data = user_data;
+
+ return 0;
+}
+
+
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+ void *user_data)
+{
+ /* TODO */
+ return 0;
+}
+
+
+void eloop_run(void)
+{
+ struct os_time tv, now;
+ DWORD count, ret, timeout, err;
+ size_t i;
+
+ while (!eloop.terminate &&
+ (eloop.timeout || eloop.reader_count > 0 ||
+ eloop.event_count > 0)) {
+ tv.sec = tv.usec = 0;
+ if (eloop.timeout) {
+ os_get_time(&now);
+ if (os_time_before(&now, &eloop.timeout->time))
+ os_time_sub(&eloop.timeout->time, &now, &tv);
+ }
+
+ count = 0;
+ for (i = 0; i < eloop.event_count; i++)
+ eloop.handles[count++] = eloop.events[i].event;
+
+ for (i = 0; i < eloop.reader_count; i++)
+ eloop.handles[count++] = eloop.readers[i].event;
+
+ if (eloop.term_event)
+ eloop.handles[count++] = eloop.term_event;
+
+ if (eloop.timeout)
+ timeout = tv.sec * 1000 + tv.usec / 1000;
+ else
+ timeout = INFINITE;
+
+ if (count > MAXIMUM_WAIT_OBJECTS) {
+ printf("WaitForMultipleObjects: Too many events: "
+ "%d > %d (ignoring extra events)\n",
+ (int) count, MAXIMUM_WAIT_OBJECTS);
+ count = MAXIMUM_WAIT_OBJECTS;
+ }
+#ifdef _WIN32_WCE
+ ret = WaitForMultipleObjects(count, eloop.handles, FALSE,
+ timeout);
+#else /* _WIN32_WCE */
+ ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE,
+ timeout, TRUE);
+#endif /* _WIN32_WCE */
+ err = GetLastError();
+
+ eloop_process_pending_signals();
+
+ /* check if some registered timeouts have occurred */
+ if (eloop.timeout) {
+ struct eloop_timeout *tmp;
+
+ os_get_time(&now);
+ if (!os_time_before(&now, &eloop.timeout->time)) {
+ tmp = eloop.timeout;
+ eloop.timeout = eloop.timeout->next;
+ tmp->handler(tmp->eloop_data,
+ tmp->user_data);
+ os_free(tmp);
+ }
+
+ }
+
+ if (ret == WAIT_FAILED) {
+ printf("WaitForMultipleObjects(count=%d) failed: %d\n",
+ (int) count, (int) err);
+ os_sleep(1, 0);
+ continue;
+ }
+
+#ifndef _WIN32_WCE
+ if (ret == WAIT_IO_COMPLETION)
+ continue;
+#endif /* _WIN32_WCE */
+
+ if (ret == WAIT_TIMEOUT)
+ continue;
+
+ while (ret >= WAIT_OBJECT_0 &&
+ ret < WAIT_OBJECT_0 + eloop.event_count) {
+ eloop.events[ret].handler(
+ eloop.events[ret].eloop_data,
+ eloop.events[ret].user_data);
+ ret = WaitForMultipleObjects(eloop.event_count,
+ eloop.handles, FALSE, 0);
+ }
+
+ eloop.reader_table_changed = 0;
+ for (i = 0; i < eloop.reader_count; i++) {
+ WSANETWORKEVENTS events;
+ if (WSAEnumNetworkEvents(eloop.readers[i].sock,
+ eloop.readers[i].event,
+ &events) == 0 &&
+ (events.lNetworkEvents & FD_READ)) {
+ eloop.readers[i].handler(
+ eloop.readers[i].sock,
+ eloop.readers[i].eloop_data,
+ eloop.readers[i].user_data);
+ if (eloop.reader_table_changed)
+ break;
+ }
+ }
+ }
+}
+
+
+void eloop_terminate(void)
+{
+ eloop.terminate = 1;
+ SetEvent(eloop.term_event);
+}
+
+
+void eloop_destroy(void)
+{
+ struct eloop_timeout *timeout, *prev;
+
+ timeout = eloop.timeout;
+ while (timeout != NULL) {
+ prev = timeout;
+ timeout = timeout->next;
+ os_free(prev);
+ }
+ os_free(eloop.readers);
+ os_free(eloop.signals);
+ if (eloop.term_event)
+ CloseHandle(eloop.term_event);
+ os_free(eloop.handles);
+ eloop.handles = NULL;
+ os_free(eloop.events);
+ eloop.events = NULL;
+}
+
+
+int eloop_terminated(void)
+{
+ return eloop.terminate;
+}
+
+
+void eloop_wait_for_read_sock(int sock)
+{
+ WSAEVENT event;
+
+ event = WSACreateEvent();
+ if (event == WSA_INVALID_EVENT) {
+ printf("WSACreateEvent() failed: %d\n", WSAGetLastError());
+ return;
+ }
+
+ if (WSAEventSelect(sock, event, FD_READ)) {
+ printf("WSAEventSelect() failed: %d\n", WSAGetLastError());
+ WSACloseEvent(event);
+ return ;
+ }
+
+ WaitForSingleObject(event, INFINITE);
+ WSAEventSelect(sock, event, 0);
+ WSACloseEvent(event);
+}
diff --git a/src/utils/includes.h b/src/utils/includes.h
new file mode 100644
index 0000000..63b5c23
--- /dev/null
+++ b/src/utils/includes.h
@@ -0,0 +1,59 @@
+/*
+ * wpa_supplicant/hostapd - Default include files
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This header file is included into all C files so that commonly used header
+ * files can be selected with OS specific ifdef blocks in one place instead of
+ * having to have OS/C library specific selection in many files.
+ */
+
+#ifndef INCLUDES_H
+#define INCLUDES_H
+
+/* Include possible build time configuration before including anything else */
+#include "build_config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#ifndef _WIN32_WCE
+#ifndef CONFIG_TI_COMPILER
+#include <signal.h>
+#include <sys/types.h>
+#endif /* CONFIG_TI_COMPILER */
+#include <errno.h>
+#endif /* _WIN32_WCE */
+#include <ctype.h>
+#include <time.h>
+
+#ifndef CONFIG_TI_COMPILER
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif /* _MSC_VER */
+#endif /* CONFIG_TI_COMPILER */
+
+#ifndef CONFIG_NATIVE_WINDOWS
+#ifndef CONFIG_TI_COMPILER
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifndef __vxworks
+#ifndef __SYMBIAN32__
+#include <sys/uio.h>
+#endif /* __SYMBIAN32__ */
+#include <sys/time.h>
+#endif /* __vxworks */
+#endif /* CONFIG_TI_COMPILER */
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#endif /* INCLUDES_H */
diff --git a/src/utils/ip_addr.c b/src/utils/ip_addr.c
new file mode 100644
index 0000000..158fd57
--- /dev/null
+++ b/src/utils/ip_addr.c
@@ -0,0 +1,83 @@
+/*
+ * IP address processing
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ip_addr.h"
+
+const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
+ size_t buflen)
+{
+ if (buflen == 0 || addr == NULL)
+ return NULL;
+
+ if (addr->af == AF_INET) {
+ os_strlcpy(buf, inet_ntoa(addr->u.v4), buflen);
+ } else {
+ buf[0] = '\0';
+ }
+#ifdef CONFIG_IPV6
+ if (addr->af == AF_INET6) {
+ if (inet_ntop(AF_INET6, &addr->u.v6, buf, buflen) == NULL)
+ buf[0] = '\0';
+ }
+#endif /* CONFIG_IPV6 */
+
+ return buf;
+}
+
+
+int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b)
+{
+ if (a == NULL && b == NULL)
+ return 0;
+ if (a == NULL || b == NULL)
+ return 1;
+
+ switch (a->af) {
+ case AF_INET:
+ if (a->u.v4.s_addr != b->u.v4.s_addr)
+ return 1;
+ break;
+#ifdef CONFIG_IPV6
+ case AF_INET6:
+ if (os_memcmp(&a->u.v6, &b->u.v6, sizeof(a->u.v6)) != 0)
+ return 1;
+ break;
+#endif /* CONFIG_IPV6 */
+ }
+
+ return 0;
+}
+
+
+int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr)
+{
+#ifndef CONFIG_NATIVE_WINDOWS
+ if (inet_aton(txt, &addr->u.v4)) {
+ addr->af = AF_INET;
+ return 0;
+ }
+
+#ifdef CONFIG_IPV6
+ if (inet_pton(AF_INET6, txt, &addr->u.v6) > 0) {
+ addr->af = AF_INET6;
+ return 0;
+ }
+#endif /* CONFIG_IPV6 */
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+ return -1;
+}
diff --git a/src/utils/ip_addr.h b/src/utils/ip_addr.h
new file mode 100644
index 0000000..28ccaef
--- /dev/null
+++ b/src/utils/ip_addr.h
@@ -0,0 +1,34 @@
+/*
+ * IP address processing
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef IP_ADDR_H
+#define IP_ADDR_H
+
+struct hostapd_ip_addr {
+ int af; /* AF_INET / AF_INET6 */
+ union {
+ struct in_addr v4;
+#ifdef CONFIG_IPV6
+ struct in6_addr v6;
+#endif /* CONFIG_IPV6 */
+ u8 max_len[16];
+ } u;
+};
+
+const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
+ size_t buflen);
+int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b);
+int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr);
+
+#endif /* IP_ADDR_H */
diff --git a/src/utils/list.h b/src/utils/list.h
new file mode 100644
index 0000000..ded7846
--- /dev/null
+++ b/src/utils/list.h
@@ -0,0 +1,98 @@
+/*
+ * Doubly-linked list
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+/**
+ * struct dl_list - Doubly-linked list
+ */
+struct dl_list {
+ struct dl_list *next;
+ struct dl_list *prev;
+};
+
+static inline void dl_list_init(struct dl_list *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+static inline void dl_list_add(struct dl_list *list, struct dl_list *item)
+{
+ item->next = list->next;
+ item->prev = list;
+ list->next->prev = item;
+ list->next = item;
+}
+
+static inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item)
+{
+ dl_list_add(list->prev, item);
+}
+
+static inline void dl_list_del(struct dl_list *item)
+{
+ item->next->prev = item->prev;
+ item->prev->next = item->next;
+ item->next = NULL;
+ item->prev = NULL;
+}
+
+static inline int dl_list_empty(struct dl_list *list)
+{
+ return list->next == list;
+}
+
+static inline unsigned int dl_list_len(struct dl_list *list)
+{
+ struct dl_list *item;
+ int count = 0;
+ for (item = list->next; item != list; item = item->next)
+ count++;
+ return count;
+}
+
+#ifndef offsetof
+#define offsetof(type, member) ((long) &((type *) 0)->member)
+#endif
+
+#define dl_list_entry(item, type, member) \
+ ((type *) ((char *) item - offsetof(type, member)))
+
+#define dl_list_first(list, type, member) \
+ (dl_list_empty((list)) ? NULL : \
+ dl_list_entry((list)->next, type, member))
+
+#define dl_list_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))
+
+#endif /* LIST_H */
diff --git a/src/utils/os.h b/src/utils/os.h
new file mode 100644
index 0000000..f4723d8
--- /dev/null
+++ b/src/utils/os.h
@@ -0,0 +1,508 @@
+/*
+ * OS specific functions
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef OS_H
+#define OS_H
+
+typedef long os_time_t;
+
+/**
+ * os_sleep - Sleep (sec, usec)
+ * @sec: Number of seconds to sleep
+ * @usec: Number of microseconds to sleep
+ */
+void os_sleep(os_time_t sec, os_time_t usec);
+
+struct os_time {
+ os_time_t sec;
+ os_time_t usec;
+};
+
+/**
+ * os_get_time - Get current time (sec, usec)
+ * @t: Pointer to buffer for the time
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_time(struct os_time *t);
+
+
+/* Helper macros for handling struct os_time */
+
+#define os_time_before(a, b) \
+ ((a)->sec < (b)->sec || \
+ ((a)->sec == (b)->sec && (a)->usec < (b)->usec))
+
+#define os_time_sub(a, b, res) do { \
+ (res)->sec = (a)->sec - (b)->sec; \
+ (res)->usec = (a)->usec - (b)->usec; \
+ if ((res)->usec < 0) { \
+ (res)->sec--; \
+ (res)->usec += 1000000; \
+ } \
+} while (0)
+
+/**
+ * os_mktime - Convert broken-down time into seconds since 1970-01-01
+ * @year: Four digit year
+ * @month: Month (1 .. 12)
+ * @day: Day of month (1 .. 31)
+ * @hour: Hour (0 .. 23)
+ * @min: Minute (0 .. 59)
+ * @sec: Second (0 .. 60)
+ * @t: Buffer for returning calendar time representation (seconds since
+ * 1970-01-01 00:00:00)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time
+ * which is used by POSIX mktime().
+ */
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+ os_time_t *t);
+
+
+/**
+ * os_daemonize - Run in the background (detach from the controlling terminal)
+ * @pid_file: File name to write the process ID to or %NULL to skip this
+ * Returns: 0 on success, -1 on failure
+ */
+int os_daemonize(const char *pid_file);
+
+/**
+ * os_daemonize_terminate - Stop running in the background (remove pid file)
+ * @pid_file: File name to write the process ID to or %NULL to skip this
+ */
+void os_daemonize_terminate(const char *pid_file);
+
+/**
+ * os_get_random - Get cryptographically strong pseudo random data
+ * @buf: Buffer for pseudo random data
+ * @len: Length of the buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_random(unsigned char *buf, size_t len);
+
+/**
+ * os_random - Get pseudo random value (not necessarily very strong)
+ * Returns: Pseudo random value
+ */
+unsigned long os_random(void);
+
+/**
+ * os_rel2abs_path - Get an absolute path for a file
+ * @rel_path: Relative path to a file
+ * Returns: Absolute path for the file or %NULL on failure
+ *
+ * This function tries to convert a relative path of a file to an absolute path
+ * in order for the file to be found even if current working directory has
+ * changed. The returned value is allocated and caller is responsible for
+ * freeing it. It is acceptable to just return the same path in an allocated
+ * buffer, e.g., return strdup(rel_path). This function is only used to find
+ * configuration files when os_daemonize() may have changed the current working
+ * directory and relative path would be pointing to a different location.
+ */
+char * os_rel2abs_path(const char *rel_path);
+
+/**
+ * os_program_init - Program initialization (called at start)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when a programs starts. If there are any OS specific
+ * processing that is needed, it can be placed here. It is also acceptable to
+ * just return 0 if not special processing is needed.
+ */
+int os_program_init(void);
+
+/**
+ * os_program_deinit - Program deinitialization (called just before exit)
+ *
+ * This function is called just before a program exists. If there are any OS
+ * specific processing, e.g., freeing resourced allocated in os_program_init(),
+ * it should be done here. It is also acceptable for this function to do
+ * nothing.
+ */
+void os_program_deinit(void);
+
+/**
+ * os_setenv - Set environment variable
+ * @name: Name of the variable
+ * @value: Value to set to the variable
+ * @overwrite: Whether existing variable should be overwritten
+ * Returns: 0 on success, -1 on error
+ *
+ * This function is only used for wpa_cli action scripts. OS wrapper does not
+ * need to implement this if such functionality is not needed.
+ */
+int os_setenv(const char *name, const char *value, int overwrite);
+
+/**
+ * os_unsetenv - Delete environent variable
+ * @name: Name of the variable
+ * Returns: 0 on success, -1 on error
+ *
+ * This function is only used for wpa_cli action scripts. OS wrapper does not
+ * need to implement this if such functionality is not needed.
+ */
+int os_unsetenv(const char *name);
+
+/**
+ * os_readfile - Read a file to an allocated memory buffer
+ * @name: Name of the file to read
+ * @len: For returning the length of the allocated buffer
+ * Returns: Pointer to the allocated buffer or %NULL on failure
+ *
+ * This function allocates memory and reads the given file to this buffer. Both
+ * binary and text files can be read with this function. The caller is
+ * responsible for freeing the returned buffer with os_free().
+ */
+char * os_readfile(const char *name, size_t *len);
+
+/**
+ * os_zalloc - Allocate and zero memory
+ * @size: Number of bytes to allocate
+ * Returns: Pointer to allocated and zeroed memory or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+void * os_zalloc(size_t size);
+
+
+/*
+ * The following functions are wrapper for standard ANSI C or POSIX functions.
+ * By default, they are just defined to use the standard function name and no
+ * os_*.c implementation is needed for them. This avoids extra function calls
+ * by allowing the C pre-processor take care of the function name mapping.
+ *
+ * If the target system uses a C library that does not provide these functions,
+ * build_config.h can be used to define the wrappers to use a different
+ * function name. This can be done on function-by-function basis since the
+ * defines here are only used if build_config.h does not define the os_* name.
+ * If needed, os_*.c file can be used to implement the functions that are not
+ * included in the C library on the target system. Alternatively,
+ * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case
+ * these functions need to be implemented in os_*.c file for the target system.
+ */
+
+#ifdef OS_NO_C_LIB_DEFINES
+
+/**
+ * os_malloc - Allocate dynamic memory
+ * @size: Size of the buffer to allocate
+ * Returns: Allocated buffer or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+void * os_malloc(size_t size);
+
+/**
+ * os_realloc - Re-allocate dynamic memory
+ * @ptr: Old buffer from os_malloc() or os_realloc()
+ * @size: Size of the new buffer
+ * Returns: Allocated buffer or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ * If re-allocation fails, %NULL is returned and the original buffer (ptr) is
+ * not freed and caller is still responsible for freeing it.
+ */
+void * os_realloc(void *ptr, size_t size);
+
+/**
+ * os_free - Free dynamic memory
+ * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL
+ */
+void os_free(void *ptr);
+
+/**
+ * os_memcpy - Copy memory area
+ * @dest: Destination
+ * @src: Source
+ * @n: Number of bytes to copy
+ * Returns: dest
+ *
+ * The memory areas src and dst must not overlap. os_memmove() can be used with
+ * overlapping memory.
+ */
+void * os_memcpy(void *dest, const void *src, size_t n);
+
+/**
+ * os_memmove - Copy memory area
+ * @dest: Destination
+ * @src: Source
+ * @n: Number of bytes to copy
+ * Returns: dest
+ *
+ * The memory areas src and dst may overlap.
+ */
+void * os_memmove(void *dest, const void *src, size_t n);
+
+/**
+ * os_memset - Fill memory with a constant byte
+ * @s: Memory area to be filled
+ * @c: Constant byte
+ * @n: Number of bytes started from s to fill with c
+ * Returns: s
+ */
+void * os_memset(void *s, int c, size_t n);
+
+/**
+ * os_memcmp - Compare memory areas
+ * @s1: First buffer
+ * @s2: Second buffer
+ * @n: Maximum numbers of octets to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_memcmp(const void *s1, const void *s2, size_t n);
+
+/**
+ * os_strdup - Duplicate a string
+ * @s: Source string
+ * Returns: Allocated buffer with the string copied into it or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+char * os_strdup(const char *s);
+
+/**
+ * os_strlen - Calculate the length of a string
+ * @s: '\0' terminated string
+ * Returns: Number of characters in s (not counting the '\0' terminator)
+ */
+size_t os_strlen(const char *s);
+
+/**
+ * os_strcasecmp - Compare two strings ignoring case
+ * @s1: First string
+ * @s2: Second string
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greatred than s2
+ */
+int os_strcasecmp(const char *s1, const char *s2);
+
+/**
+ * os_strncasecmp - Compare two strings ignoring case
+ * @s1: First string
+ * @s2: Second string
+ * @n: Maximum numbers of characters to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_strncasecmp(const char *s1, const char *s2, size_t n);
+
+/**
+ * os_strchr - Locate the first occurrence of a character in string
+ * @s: String
+ * @c: Character to search for
+ * Returns: Pointer to the matched character or %NULL if not found
+ */
+char * os_strchr(const char *s, int c);
+
+/**
+ * os_strrchr - Locate the last occurrence of a character in string
+ * @s: String
+ * @c: Character to search for
+ * Returns: Pointer to the matched character or %NULL if not found
+ */
+char * os_strrchr(const char *s, int c);
+
+/**
+ * os_strcmp - Compare two strings
+ * @s1: First string
+ * @s2: Second string
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greatred than s2
+ */
+int os_strcmp(const char *s1, const char *s2);
+
+/**
+ * os_strncmp - Compare two strings
+ * @s1: First string
+ * @s2: Second string
+ * @n: Maximum numbers of characters to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_strncmp(const char *s1, const char *s2, size_t n);
+
+/**
+ * os_strncpy - Copy a string
+ * @dest: Destination
+ * @src: Source
+ * @n: Maximum number of characters to copy
+ * Returns: dest
+ */
+char * os_strncpy(char *dest, const char *src, size_t n);
+
+/**
+ * os_strstr - Locate a substring
+ * @haystack: String (haystack) to search from
+ * @needle: Needle to search from haystack
+ * Returns: Pointer to the beginning of the substring or %NULL if not found
+ */
+char * os_strstr(const char *haystack, const char *needle);
+
+/**
+ * os_snprintf - Print to a memory buffer
+ * @str: Memory buffer to print into
+ * @size: Maximum length of the str buffer
+ * @format: printf format
+ * Returns: Number of characters printed (not including trailing '\0').
+ *
+ * If the output buffer is truncated, number of characters which would have
+ * been written is returned. Since some C libraries return -1 in such a case,
+ * the caller must be prepared on that value, too, to indicate truncation.
+ *
+ * Note: Some C library implementations of snprintf() may not guarantee null
+ * termination in case the output is truncated. The OS wrapper function of
+ * os_snprintf() should provide this guarantee, i.e., to null terminate the
+ * output buffer if a C library version of the function is used and if that
+ * function does not guarantee null termination.
+ *
+ * If the target system does not include snprintf(), see, e.g.,
+ * http://www.ijs.si/software/snprintf/ for an example of a portable
+ * implementation of snprintf.
+ */
+int os_snprintf(char *str, size_t size, const char *format, ...);
+
+#else /* OS_NO_C_LIB_DEFINES */
+
+#ifdef WPA_TRACE
+void * os_malloc(size_t size);
+void * os_realloc(void *ptr, size_t size);
+void os_free(void *ptr);
+char * os_strdup(const char *s);
+#else /* WPA_TRACE */
+#ifndef os_malloc
+#define os_malloc(s) malloc((s))
+#endif
+#ifndef os_realloc
+#define os_realloc(p, s) realloc((p), (s))
+#endif
+#ifndef os_free
+#define os_free(p) free((p))
+#endif
+#ifndef os_strdup
+#ifdef _MSC_VER
+#define os_strdup(s) _strdup(s)
+#else
+#define os_strdup(s) strdup(s)
+#endif
+#endif
+#endif /* WPA_TRACE */
+
+#ifndef os_memcpy
+#define os_memcpy(d, s, n) memcpy((d), (s), (n))
+#endif
+#ifndef os_memmove
+#define os_memmove(d, s, n) memmove((d), (s), (n))
+#endif
+#ifndef os_memset
+#define os_memset(s, c, n) memset(s, c, n)
+#endif
+#ifndef os_memcmp
+#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n))
+#endif
+
+#ifndef os_strlen
+#define os_strlen(s) strlen(s)
+#endif
+#ifndef os_strcasecmp
+#ifdef _MSC_VER
+#define os_strcasecmp(s1, s2) _stricmp((s1), (s2))
+#else
+#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2))
+#endif
+#endif
+#ifndef os_strncasecmp
+#ifdef _MSC_VER
+#define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n))
+#else
+#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n))
+#endif
+#endif
+#ifndef os_strchr
+#define os_strchr(s, c) strchr((s), (c))
+#endif
+#ifndef os_strcmp
+#define os_strcmp(s1, s2) strcmp((s1), (s2))
+#endif
+#ifndef os_strncmp
+#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))
+#endif
+#ifndef os_strncpy
+#define os_strncpy(d, s, n) strncpy((d), (s), (n))
+#endif
+#ifndef os_strrchr
+#define os_strrchr(s, c) strrchr((s), (c))
+#endif
+#ifndef os_strstr
+#define os_strstr(h, n) strstr((h), (n))
+#endif
+
+#ifndef os_snprintf
+#ifdef _MSC_VER
+#define os_snprintf _snprintf
+#else
+#define os_snprintf snprintf
+#endif
+#endif
+
+#endif /* OS_NO_C_LIB_DEFINES */
+
+
+/**
+ * os_strlcpy - Copy a string with size bound and NUL-termination
+ * @dest: Destination
+ * @src: Source
+ * @siz: Size of the target buffer
+ * Returns: Total length of the target string (length of src) (not including
+ * NUL-termination)
+ *
+ * This function matches in behavior with the strlcpy(3) function in OpenBSD.
+ */
+size_t os_strlcpy(char *dest, const char *src, size_t siz);
+
+
+#ifdef OS_REJECT_C_LIB_FUNCTIONS
+#define malloc OS_DO_NOT_USE_malloc
+#define realloc OS_DO_NOT_USE_realloc
+#define free OS_DO_NOT_USE_free
+#define memcpy OS_DO_NOT_USE_memcpy
+#define memmove OS_DO_NOT_USE_memmove
+#define memset OS_DO_NOT_USE_memset
+#define memcmp OS_DO_NOT_USE_memcmp
+#undef strdup
+#define strdup OS_DO_NOT_USE_strdup
+#define strlen OS_DO_NOT_USE_strlen
+#define strcasecmp OS_DO_NOT_USE_strcasecmp
+#define strncasecmp OS_DO_NOT_USE_strncasecmp
+#undef strchr
+#define strchr OS_DO_NOT_USE_strchr
+#undef strcmp
+#define strcmp OS_DO_NOT_USE_strcmp
+#undef strncmp
+#define strncmp OS_DO_NOT_USE_strncmp
+#undef strncpy
+#define strncpy OS_DO_NOT_USE_strncpy
+#define strrchr OS_DO_NOT_USE_strrchr
+#define strstr OS_DO_NOT_USE_strstr
+#undef snprintf
+#define snprintf OS_DO_NOT_USE_snprintf
+
+#define strcpy OS_DO_NOT_USE_strcpy
+#endif /* OS_REJECT_C_LIB_FUNCTIONS */
+
+#endif /* OS_H */
diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c
new file mode 100644
index 0000000..5260e23
--- /dev/null
+++ b/src/utils/os_internal.c
@@ -0,0 +1,471 @@
+/*
+ * wpa_supplicant/hostapd / Internal implementation of OS specific functions
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file is an example of operating system specific wrapper functions.
+ * This version implements many of the functions internally, so it can be used
+ * to fill in missing functions from the target system C libraries.
+ *
+ * Some of the functions are using standard C library calls in order to keep
+ * this file in working condition to allow the functions to be tested on a
+ * Linux target. Please note that OS_NO_C_LIB_DEFINES needs to be defined for
+ * this file to work correctly. Note that these implementations are only
+ * examples and are not optimized for speed.
+ */
+
+#include "includes.h"
+
+#undef OS_REJECT_C_LIB_FUNCTIONS
+#include "os.h"
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+ if (sec)
+ sleep(sec);
+ if (usec)
+ usleep(usec);
+}
+
+
+int os_get_time(struct os_time *t)
+{
+ int res;
+ struct timeval tv;
+ res = gettimeofday(&tv, NULL);
+ t->sec = tv.tv_sec;
+ t->usec = tv.tv_usec;
+ return res;
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+ os_time_t *t)
+{
+ struct tm tm;
+
+ if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+ hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+ sec > 60)
+ return -1;
+
+ os_memset(&tm, 0, sizeof(tm));
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = min;
+ tm.tm_sec = sec;
+
+ *t = (os_time_t) mktime(&tm);
+ return 0;
+}
+
+
+int os_daemonize(const char *pid_file)
+{
+ if (daemon(0, 0)) {
+ perror("daemon");
+ return -1;
+ }
+
+ if (pid_file) {
+ FILE *f = fopen(pid_file, "w");
+ if (f) {
+ fprintf(f, "%u\n", getpid());
+ fclose(f);
+ }
+ }
+
+ return -0;
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+ if (pid_file)
+ unlink(pid_file);
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+ FILE *f;
+ size_t rc;
+
+ f = fopen("/dev/urandom", "rb");
+ if (f == NULL) {
+ printf("Could not open /dev/urandom.\n");
+ return -1;
+ }
+
+ rc = fread(buf, 1, len, f);
+ fclose(f);
+
+ return rc != len ? -1 : 0;
+}
+
+
+unsigned long os_random(void)
+{
+ return random();
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+ char *buf = NULL, *cwd, *ret;
+ size_t len = 128, cwd_len, rel_len, ret_len;
+
+ if (rel_path[0] == '/')
+ return os_strdup(rel_path);
+
+ for (;;) {
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return NULL;
+ cwd = getcwd(buf, len);
+ if (cwd == NULL) {
+ os_free(buf);
+ if (errno != ERANGE) {
+ return NULL;
+ }
+ len *= 2;
+ } else {
+ break;
+ }
+ }
+
+ cwd_len = strlen(cwd);
+ rel_len = strlen(rel_path);
+ ret_len = cwd_len + 1 + rel_len + 1;
+ ret = os_malloc(ret_len);
+ if (ret) {
+ os_memcpy(ret, cwd, cwd_len);
+ ret[cwd_len] = '/';
+ os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
+ ret[ret_len - 1] = '\0';
+ }
+ os_free(buf);
+ return ret;
+}
+
+
+int os_program_init(void)
+{
+ return 0;
+}
+
+
+void os_program_deinit(void)
+{
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+ return setenv(name, value, overwrite);
+}
+
+
+int os_unsetenv(const char *name)
+{
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+ unsetenv(name);
+ return 0;
+#else
+ return unsetenv(name);
+#endif
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+ FILE *f;
+ char *buf;
+
+ f = fopen(name, "rb");
+ if (f == NULL)
+ return NULL;
+
+ fseek(f, 0, SEEK_END);
+ *len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ buf = os_malloc(*len);
+ if (buf == NULL) {
+ fclose(f);
+ return NULL;
+ }
+
+ if (fread(buf, 1, *len, f) != *len) {
+ fclose(f);
+ os_free(buf);
+ return NULL;
+ }
+
+ fclose(f);
+
+ return buf;
+}
+
+
+void * os_zalloc(size_t size)
+{
+ void *n = os_malloc(size);
+ if (n)
+ os_memset(n, 0, size);
+ return n;
+}
+
+
+void * os_malloc(size_t size)
+{
+ return malloc(size);
+}
+
+
+void * os_realloc(void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+
+
+void os_free(void *ptr)
+{
+ free(ptr);
+}
+
+
+void * os_memcpy(void *dest, const void *src, size_t n)
+{
+ char *d = dest;
+ const char *s = src;
+ while (n--)
+ *d++ = *s++;
+ return dest;
+}
+
+
+void * os_memmove(void *dest, const void *src, size_t n)
+{
+ if (dest < src)
+ os_memcpy(dest, src, n);
+ else {
+ /* overlapping areas */
+ char *d = (char *) dest + n;
+ const char *s = (const char *) src + n;
+ while (n--)
+ *--d = *--s;
+ }
+ return dest;
+}
+
+
+void * os_memset(void *s, int c, size_t n)
+{
+ char *p = s;
+ while (n--)
+ *p++ = c;
+ return s;
+}
+
+
+int os_memcmp(const void *s1, const void *s2, size_t n)
+{
+ const unsigned char *p1 = s1, *p2 = s2;
+
+ if (n == 0)
+ return 0;
+
+ while (*p1 == *p2) {
+ p1++;
+ p2++;
+ n--;
+ if (n == 0)
+ return 0;
+ }
+
+ return *p1 - *p2;
+}
+
+
+char * os_strdup(const char *s)
+{
+ char *res;
+ size_t len;
+ if (s == NULL)
+ return NULL;
+ len = os_strlen(s);
+ res = os_malloc(len + 1);
+ if (res)
+ os_memcpy(res, s, len + 1);
+ return res;
+}
+
+
+size_t os_strlen(const char *s)
+{
+ const char *p = s;
+ while (*p)
+ p++;
+ return p - s;
+}
+
+
+int os_strcasecmp(const char *s1, const char *s2)
+{
+ /*
+ * Ignoring case is not required for main functionality, so just use
+ * the case sensitive version of the function.
+ */
+ return os_strcmp(s1, s2);
+}
+
+
+int os_strncasecmp(const char *s1, const char *s2, size_t n)
+{
+ /*
+ * Ignoring case is not required for main functionality, so just use
+ * the case sensitive version of the function.
+ */
+ return os_strncmp(s1, s2, n);
+}
+
+
+char * os_strchr(const char *s, int c)
+{
+ while (*s) {
+ if (*s == c)
+ return (char *) s;
+ s++;
+ }
+ return NULL;
+}
+
+
+char * os_strrchr(const char *s, int c)
+{
+ const char *p = s;
+ while (*p)
+ p++;
+ p--;
+ while (p >= s) {
+ if (*p == c)
+ return (char *) p;
+ p--;
+ }
+ return NULL;
+}
+
+
+int os_strcmp(const char *s1, const char *s2)
+{
+ while (*s1 == *s2) {
+ if (*s1 == '\0')
+ break;
+ s1++;
+ s2++;
+ }
+
+ return *s1 - *s2;
+}
+
+
+int os_strncmp(const char *s1, const char *s2, size_t n)
+{
+ if (n == 0)
+ return 0;
+
+ while (*s1 == *s2) {
+ if (*s1 == '\0')
+ break;
+ s1++;
+ s2++;
+ n--;
+ if (n == 0)
+ return 0;
+ }
+
+ return *s1 - *s2;
+}
+
+
+char * os_strncpy(char *dest, const char *src, size_t n)
+{
+ char *d = dest;
+
+ while (n--) {
+ *d = *src;
+ if (*src == '\0')
+ break;
+ d++;
+ src++;
+ }
+
+ return dest;
+}
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t siz)
+{
+ const char *s = src;
+ size_t left = siz;
+
+ if (left) {
+ /* Copy string up to the maximum size of the dest buffer */
+ while (--left != 0) {
+ if ((*dest++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ if (left == 0) {
+ /* Not enough room for the string; force NUL-termination */
+ if (siz != 0)
+ *dest = '\0';
+ while (*s++)
+ ; /* determine total src string length */
+ }
+
+ return s - src - 1;
+}
+
+
+char * os_strstr(const char *haystack, const char *needle)
+{
+ size_t len = os_strlen(needle);
+ while (*haystack) {
+ if (os_strncmp(haystack, needle, len) == 0)
+ return (char *) haystack;
+ haystack++;
+ }
+
+ return NULL;
+}
+
+
+int os_snprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+
+ /* See http://www.ijs.si/software/snprintf/ for portable
+ * implementation of snprintf.
+ */
+
+ va_start(ap, format);
+ ret = vsnprintf(str, size, format, ap);
+ va_end(ap);
+ if (size > 0)
+ str[size - 1] = '\0';
+ return ret;
+}
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
new file mode 100644
index 0000000..bab8f17
--- /dev/null
+++ b/src/utils/os_none.c
@@ -0,0 +1,226 @@
+/*
+ * wpa_supplicant/hostapd / Empty OS specific functions
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file can be used as a starting point when adding a new OS target. The
+ * functions here do not really work as-is since they are just empty or only
+ * return an error value. os_internal.c can be used as another starting point
+ * or reference since it has example implementation of many of these functions.
+ */
+
+#include "includes.h"
+
+#include "os.h"
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+}
+
+
+int os_get_time(struct os_time *t)
+{
+ return -1;
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+ os_time_t *t)
+{
+ return -1;
+}
+
+
+int os_daemonize(const char *pid_file)
+{
+ return -1;
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+ return -1;
+}
+
+
+unsigned long os_random(void)
+{
+ return 0;
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+ return NULL; /* strdup(rel_path) can be used here */
+}
+
+
+int os_program_init(void)
+{
+ return 0;
+}
+
+
+void os_program_deinit(void)
+{
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+ return -1;
+}
+
+
+int os_unsetenv(const char *name)
+{
+ return -1;
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+ return NULL;
+}
+
+
+void * os_zalloc(size_t size)
+{
+ return NULL;
+}
+
+
+#ifdef OS_NO_C_LIB_DEFINES
+void * os_malloc(size_t size)
+{
+ return NULL;
+}
+
+
+void * os_realloc(void *ptr, size_t size)
+{
+ return NULL;
+}
+
+
+void os_free(void *ptr)
+{
+}
+
+
+void * os_memcpy(void *dest, const void *src, size_t n)
+{
+ return dest;
+}
+
+
+void * os_memmove(void *dest, const void *src, size_t n)
+{
+ return dest;
+}
+
+
+void * os_memset(void *s, int c, size_t n)
+{
+ return s;
+}
+
+
+int os_memcmp(const void *s1, const void *s2, size_t n)
+{
+ return 0;
+}
+
+
+char * os_strdup(const char *s)
+{
+ return NULL;
+}
+
+
+size_t os_strlen(const char *s)
+{
+ return 0;
+}
+
+
+int os_strcasecmp(const char *s1, const char *s2)
+{
+ /*
+ * Ignoring case is not required for main functionality, so just use
+ * the case sensitive version of the function.
+ */
+ return os_strcmp(s1, s2);
+}
+
+
+int os_strncasecmp(const char *s1, const char *s2, size_t n)
+{
+ /*
+ * Ignoring case is not required for main functionality, so just use
+ * the case sensitive version of the function.
+ */
+ return os_strncmp(s1, s2, n);
+}
+
+
+char * os_strchr(const char *s, int c)
+{
+ return NULL;
+}
+
+
+char * os_strrchr(const char *s, int c)
+{
+ return NULL;
+}
+
+
+int os_strcmp(const char *s1, const char *s2)
+{
+ return 0;
+}
+
+
+int os_strncmp(const char *s1, const char *s2, size_t n)
+{
+ return 0;
+}
+
+
+char * os_strncpy(char *dest, const char *src, size_t n)
+{
+ return dest;
+}
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t size)
+{
+ return 0;
+}
+
+
+char * os_strstr(const char *haystack, const char *needle)
+{
+ return NULL;
+}
+
+
+int os_snprintf(char *str, size_t size, const char *format, ...)
+{
+ return 0;
+}
+#endif /* OS_NO_C_LIB_DEFINES */
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
new file mode 100644
index 0000000..4e11758
--- /dev/null
+++ b/src/utils/os_unix.c
@@ -0,0 +1,474 @@
+/*
+ * OS specific functions for UNIX/POSIX systems
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#ifdef ANDROID
+#include <linux/capability.h>
+#include <linux/prctl.h>
+#include <private/android_filesystem_config.h>
+#endif /* ANDROID */
+
+#include "os.h"
+
+#ifdef WPA_TRACE
+
+#include "common.h"
+#include "list.h"
+#include "wpa_debug.h"
+#include "trace.h"
+
+static struct dl_list alloc_list;
+
+#define ALLOC_MAGIC 0xa84ef1b2
+#define FREED_MAGIC 0x67fd487a
+
+struct os_alloc_trace {
+ unsigned int magic;
+ struct dl_list list;
+ size_t len;
+ WPA_TRACE_INFO
+};
+
+#endif /* WPA_TRACE */
+
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+ if (sec)
+ sleep(sec);
+ if (usec)
+ usleep(usec);
+}
+
+
+int os_get_time(struct os_time *t)
+{
+ int res;
+ struct timeval tv;
+ res = gettimeofday(&tv, NULL);
+ t->sec = tv.tv_sec;
+ t->usec = tv.tv_usec;
+ return res;
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+ os_time_t *t)
+{
+ struct tm tm, *tm1;
+ time_t t_local, t1, t2;
+ os_time_t tz_offset;
+
+ if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+ hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+ sec > 60)
+ return -1;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = min;
+ tm.tm_sec = sec;
+
+ t_local = mktime(&tm);
+
+ /* figure out offset to UTC */
+ tm1 = localtime(&t_local);
+ if (tm1) {
+ t1 = mktime(tm1);
+ tm1 = gmtime(&t_local);
+ if (tm1) {
+ t2 = mktime(tm1);
+ tz_offset = t2 - t1;
+ } else
+ tz_offset = 0;
+ } else
+ tz_offset = 0;
+
+ *t = (os_time_t) t_local - tz_offset;
+ return 0;
+}
+
+
+#ifdef __APPLE__
+#include <fcntl.h>
+static int os_daemon(int nochdir, int noclose)
+{
+ int devnull;
+
+ if (chdir("/") < 0)
+ return -1;
+
+ devnull = open("/dev/null", O_RDWR);
+ if (devnull < 0)
+ return -1;
+
+ if (dup2(devnull, STDIN_FILENO) < 0) {
+ close(devnull);
+ return -1;
+ }
+
+ if (dup2(devnull, STDOUT_FILENO) < 0) {
+ close(devnull);
+ return -1;
+ }
+
+ if (dup2(devnull, STDERR_FILENO) < 0) {
+ close(devnull);
+ return -1;
+ }
+
+ return 0;
+}
+#else /* __APPLE__ */
+#define os_daemon daemon
+#endif /* __APPLE__ */
+
+
+int os_daemonize(const char *pid_file)
+{
+#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[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.
+ */
+ gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
+ struct __user_cap_header_struct header;
+ struct __user_cap_data_struct cap;
+
+ setgroups(sizeof(groups)/sizeof(groups[0]), 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/src/utils/os_win32.c b/src/utils/os_win32.c
new file mode 100644
index 0000000..0740964
--- /dev/null
+++ b/src/utils/os_win32.c
@@ -0,0 +1,222 @@
+/*
+ * wpa_supplicant/hostapd / OS specific functions for Win32 systems
+ * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <winsock2.h>
+#include <wincrypt.h>
+
+#include "os.h"
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+ if (sec)
+ Sleep(sec * 1000);
+ if (usec)
+ Sleep(usec / 1000);
+}
+
+
+int os_get_time(struct os_time *t)
+{
+#define EPOCHFILETIME (116444736000000000ULL)
+ FILETIME ft;
+ LARGE_INTEGER li;
+ ULONGLONG tt;
+
+#ifdef _WIN32_WCE
+ SYSTEMTIME st;
+
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, &ft);
+#else /* _WIN32_WCE */
+ GetSystemTimeAsFileTime(&ft);
+#endif /* _WIN32_WCE */
+ li.LowPart = ft.dwLowDateTime;
+ li.HighPart = ft.dwHighDateTime;
+ tt = (li.QuadPart - EPOCHFILETIME) / 10;
+ t->sec = (os_time_t) (tt / 1000000);
+ t->usec = (os_time_t) (tt % 1000000);
+
+ return 0;
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+ os_time_t *t)
+{
+ struct tm tm, *tm1;
+ time_t t_local, t1, t2;
+ os_time_t tz_offset;
+
+ if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+ hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+ sec > 60)
+ return -1;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = min;
+ tm.tm_sec = sec;
+
+ t_local = mktime(&tm);
+
+ /* figure out offset to UTC */
+ tm1 = localtime(&t_local);
+ if (tm1) {
+ t1 = mktime(tm1);
+ tm1 = gmtime(&t_local);
+ if (tm1) {
+ t2 = mktime(tm1);
+ tz_offset = t2 - t1;
+ } else
+ tz_offset = 0;
+ } else
+ tz_offset = 0;
+
+ *t = (os_time_t) t_local - tz_offset;
+ return 0;
+}
+
+
+int os_daemonize(const char *pid_file)
+{
+ /* TODO */
+ return -1;
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+ HCRYPTPROV prov;
+ BOOL ret;
+
+ if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT))
+ return -1;
+
+ ret = CryptGenRandom(prov, len, buf);
+ CryptReleaseContext(prov, 0);
+
+ return ret ? 0 : -1;
+}
+
+
+unsigned long os_random(void)
+{
+ return rand();
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+ return _strdup(rel_path);
+}
+
+
+int os_program_init(void)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+ WSADATA wsaData;
+ if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
+ printf("Could not find a usable WinSock.dll\n");
+ return -1;
+ }
+#endif /* CONFIG_NATIVE_WINDOWS */
+ return 0;
+}
+
+
+void os_program_deinit(void)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+ WSACleanup();
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+ return -1;
+}
+
+
+int os_unsetenv(const char *name)
+{
+ return -1;
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+ FILE *f;
+ char *buf;
+
+ f = fopen(name, "rb");
+ if (f == NULL)
+ return NULL;
+
+ fseek(f, 0, SEEK_END);
+ *len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ buf = malloc(*len);
+ if (buf == NULL) {
+ fclose(f);
+ return NULL;
+ }
+
+ fread(buf, 1, *len, f);
+ fclose(f);
+
+ return buf;
+}
+
+
+void * os_zalloc(size_t size)
+{
+ return calloc(1, size);
+}
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t siz)
+{
+ const char *s = src;
+ size_t left = siz;
+
+ if (left) {
+ /* Copy string up to the maximum size of the dest buffer */
+ while (--left != 0) {
+ if ((*dest++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ if (left == 0) {
+ /* Not enough room for the string; force NUL-termination */
+ if (siz != 0)
+ *dest = '\0';
+ while (*s++)
+ ; /* determine total src string length */
+ }
+
+ return s - src - 1;
+}
diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c
new file mode 100644
index 0000000..bf9f04a
--- /dev/null
+++ b/src/utils/pcsc_funcs.c
@@ -0,0 +1,1238 @@
+/*
+ * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file implements wrapper functions for accessing GSM SIM and 3GPP USIM
+ * cards through PC/SC smartcard library. These functions are used to implement
+ * authentication routines for EAP-SIM and EAP-AKA.
+ */
+
+#include "includes.h"
+#include <winscard.h>
+
+#include "common.h"
+#include "pcsc_funcs.h"
+
+
+/* See ETSI GSM 11.11 and ETSI TS 102 221 for details.
+ * SIM commands:
+ * Command APDU: CLA INS P1 P2 P3 Data
+ * CLA (class of instruction): A0 for GSM, 00 for USIM
+ * INS (instruction)
+ * P1 P2 P3 (parameters, P3 = length of Data)
+ * Response APDU: Data SW1 SW2
+ * SW1 SW2 (Status words)
+ * Commands (INS P1 P2 P3):
+ * SELECT: A4 00 00 02 <file_id, 2 bytes>
+ * GET RESPONSE: C0 00 00 <len>
+ * RUN GSM ALG: 88 00 00 00 <RAND len = 10>
+ * RUN UMTS ALG: 88 00 81 <len=0x22> data: 0x10 | RAND | 0x10 | AUTN
+ * P1 = ID of alg in card
+ * P2 = ID of secret key
+ * READ BINARY: B0 <offset high> <offset low> <len>
+ * READ RECORD: B2 <record number> <mode> <len>
+ * P2 (mode) = '02' (next record), '03' (previous record),
+ * '04' (absolute mode)
+ * VERIFY CHV: 20 00 <CHV number> 08
+ * CHANGE CHV: 24 00 <CHV number> 10
+ * DISABLE CHV: 26 00 01 08
+ * ENABLE CHV: 28 00 01 08
+ * UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10
+ * SLEEP: FA 00 00 00
+ */
+
+/* GSM SIM commands */
+#define SIM_CMD_SELECT 0xa0, 0xa4, 0x00, 0x00, 0x02
+#define SIM_CMD_RUN_GSM_ALG 0xa0, 0x88, 0x00, 0x00, 0x10
+#define SIM_CMD_GET_RESPONSE 0xa0, 0xc0, 0x00, 0x00
+#define SIM_CMD_READ_BIN 0xa0, 0xb0, 0x00, 0x00
+#define SIM_CMD_READ_RECORD 0xa0, 0xb2, 0x00, 0x00
+#define SIM_CMD_VERIFY_CHV1 0xa0, 0x20, 0x00, 0x01, 0x08
+
+/* USIM commands */
+#define USIM_CLA 0x00
+#define USIM_CMD_RUN_UMTS_ALG 0x00, 0x88, 0x00, 0x81, 0x22
+#define USIM_CMD_GET_RESPONSE 0x00, 0xc0, 0x00, 0x00
+
+#define SIM_RECORD_MODE_ABSOLUTE 0x04
+
+#define USIM_FSP_TEMPL_TAG 0x62
+
+#define USIM_TLV_FILE_DESC 0x82
+#define USIM_TLV_FILE_ID 0x83
+#define USIM_TLV_DF_NAME 0x84
+#define USIM_TLV_PROPR_INFO 0xA5
+#define USIM_TLV_LIFE_CYCLE_STATUS 0x8A
+#define USIM_TLV_FILE_SIZE 0x80
+#define USIM_TLV_TOTAL_FILE_SIZE 0x81
+#define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6
+#define USIM_TLV_SHORT_FILE_ID 0x88
+
+#define USIM_PS_DO_TAG 0x90
+
+#define AKA_RAND_LEN 16
+#define AKA_AUTN_LEN 16
+#define AKA_AUTS_LEN 14
+#define RES_MAX_LEN 16
+#define IK_LEN 16
+#define CK_LEN 16
+
+
+typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types;
+
+struct scard_data {
+ SCARDCONTEXT ctx;
+ SCARDHANDLE card;
+ DWORD protocol;
+ sim_types sim_type;
+ int pin1_required;
+};
+
+#ifdef __MINGW32_VERSION
+/* MinGW does not yet support WinScard, so load the needed functions
+ * dynamically from winscard.dll for now. */
+
+static HINSTANCE dll = NULL; /* winscard.dll */
+
+static const SCARD_IO_REQUEST *dll_g_rgSCardT0Pci, *dll_g_rgSCardT1Pci;
+#undef SCARD_PCI_T0
+#define SCARD_PCI_T0 (dll_g_rgSCardT0Pci)
+#undef SCARD_PCI_T1
+#define SCARD_PCI_T1 (dll_g_rgSCardT1Pci)
+
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardEstablishContext)(IN DWORD dwScope,
+ IN LPCVOID pvReserved1,
+ IN LPCVOID pvReserved2,
+ OUT LPSCARDCONTEXT phContext);
+#define SCardEstablishContext dll_SCardEstablishContext
+
+static long (*dll_SCardReleaseContext)(long hContext);
+#define SCardReleaseContext dll_SCardReleaseContext
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardListReadersA)(IN SCARDCONTEXT hContext,
+ IN LPCSTR mszGroups,
+ OUT LPSTR mszReaders,
+ IN OUT LPDWORD pcchReaders);
+#undef SCardListReaders
+#define SCardListReaders dll_SCardListReadersA
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardConnectA)(IN SCARDCONTEXT hContext,
+ IN LPCSTR szReader,
+ IN DWORD dwShareMode,
+ IN DWORD dwPreferredProtocols,
+ OUT LPSCARDHANDLE phCard,
+ OUT LPDWORD pdwActiveProtocol);
+#undef SCardConnect
+#define SCardConnect dll_SCardConnectA
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardDisconnect)(IN SCARDHANDLE hCard,
+ IN DWORD dwDisposition);
+#define SCardDisconnect dll_SCardDisconnect
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardTransmit)(IN SCARDHANDLE hCard,
+ IN LPCSCARD_IO_REQUEST pioSendPci,
+ IN LPCBYTE pbSendBuffer,
+ IN DWORD cbSendLength,
+ IN OUT LPSCARD_IO_REQUEST pioRecvPci,
+ OUT LPBYTE pbRecvBuffer,
+ IN OUT LPDWORD pcbRecvLength);
+#define SCardTransmit dll_SCardTransmit
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardBeginTransaction)(IN SCARDHANDLE hCard);
+#define SCardBeginTransaction dll_SCardBeginTransaction
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardEndTransaction)(IN SCARDHANDLE hCard, IN DWORD dwDisposition);
+#define SCardEndTransaction dll_SCardEndTransaction
+
+
+static int mingw_load_symbols(void)
+{
+ char *sym;
+
+ if (dll)
+ return 0;
+
+ dll = LoadLibrary("winscard");
+ if (dll == NULL) {
+ wpa_printf(MSG_DEBUG, "WinSCard: Could not load winscard.dll "
+ "library");
+ return -1;
+ }
+
+#define LOADSYM(s) \
+ sym = #s; \
+ dll_ ## s = (void *) GetProcAddress(dll, sym); \
+ if (dll_ ## s == NULL) \
+ goto fail;
+
+ LOADSYM(SCardEstablishContext);
+ LOADSYM(SCardReleaseContext);
+ LOADSYM(SCardListReadersA);
+ LOADSYM(SCardConnectA);
+ LOADSYM(SCardDisconnect);
+ LOADSYM(SCardTransmit);
+ LOADSYM(SCardBeginTransaction);
+ LOADSYM(SCardEndTransaction);
+ LOADSYM(g_rgSCardT0Pci);
+ LOADSYM(g_rgSCardT1Pci);
+
+#undef LOADSYM
+
+ return 0;
+
+fail:
+ wpa_printf(MSG_DEBUG, "WinSCard: Could not get address for %s from "
+ "winscard.dll", sym);
+ FreeLibrary(dll);
+ dll = NULL;
+ return -1;
+}
+
+
+static void mingw_unload_symbols(void)
+{
+ if (dll == NULL)
+ return;
+
+ FreeLibrary(dll);
+ dll = NULL;
+}
+
+#else /* __MINGW32_VERSION */
+
+#define mingw_load_symbols() 0
+#define mingw_unload_symbols() do { } while (0)
+
+#endif /* __MINGW32_VERSION */
+
+
+static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
+ unsigned char *buf, size_t *buf_len,
+ sim_types sim_type, unsigned char *aid,
+ size_t aidlen);
+static int scard_select_file(struct scard_data *scard, unsigned short file_id,
+ unsigned char *buf, size_t *buf_len);
+static int scard_verify_pin(struct scard_data *scard, const char *pin);
+static int scard_get_record_len(struct scard_data *scard,
+ unsigned char recnum, unsigned char mode);
+static int scard_read_record(struct scard_data *scard,
+ unsigned char *data, size_t len,
+ unsigned char recnum, unsigned char mode);
+
+
+static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len,
+ int *ps_do, int *file_len)
+{
+ unsigned char *pos, *end;
+
+ if (ps_do)
+ *ps_do = -1;
+ if (file_len)
+ *file_len = -1;
+
+ pos = buf;
+ end = pos + buf_len;
+ if (*pos != USIM_FSP_TEMPL_TAG) {
+ wpa_printf(MSG_DEBUG, "SCARD: file header did not "
+ "start with FSP template tag");
+ return -1;
+ }
+ pos++;
+ if (pos >= end)
+ return -1;
+ if ((pos + pos[0]) < end)
+ end = pos + 1 + pos[0];
+ pos++;
+ wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
+ pos, end - pos);
+
+ while (pos + 1 < end) {
+ wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV "
+ "0x%02x len=%d", pos[0], pos[1]);
+ if (pos + 2 + pos[1] > end)
+ break;
+
+ if (pos[0] == USIM_TLV_FILE_SIZE &&
+ (pos[1] == 1 || pos[1] == 2) && file_len) {
+ if (pos[1] == 1)
+ *file_len = (int) pos[2];
+ else
+ *file_len = ((int) pos[2] << 8) |
+ (int) pos[3];
+ wpa_printf(MSG_DEBUG, "SCARD: file_size=%d",
+ *file_len);
+ }
+
+ if (pos[0] == USIM_TLV_PIN_STATUS_TEMPLATE &&
+ pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG &&
+ pos[3] >= 1 && ps_do) {
+ wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x",
+ pos[4]);
+ *ps_do = (int) pos[4];
+ }
+
+ pos += 2 + pos[1];
+
+ if (pos == end)
+ return 0;
+ }
+ return -1;
+}
+
+
+static int scard_pin_needed(struct scard_data *scard,
+ unsigned char *hdr, size_t hlen)
+{
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ if (hlen > SCARD_CHV1_OFFSET &&
+ !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG))
+ return 1;
+ return 0;
+ }
+
+ if (scard->sim_type == SCARD_USIM) {
+ int ps_do;
+ if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL))
+ return -1;
+ /* TODO: there could be more than one PS_DO entry because of
+ * multiple PINs in key reference.. */
+ if (ps_do > 0 && (ps_do & 0x80))
+ return 1;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int scard_get_aid(struct scard_data *scard, unsigned char *aid,
+ size_t maxlen)
+{
+ int rlen, rec;
+ struct efdir {
+ unsigned char appl_template_tag; /* 0x61 */
+ unsigned char appl_template_len;
+ unsigned char appl_id_tag; /* 0x4f */
+ unsigned char aid_len;
+ unsigned char rid[5];
+ unsigned char appl_code[2]; /* 0x1002 for 3G USIM */
+ } *efdir;
+ unsigned char buf[100];
+ size_t blen;
+
+ efdir = (struct efdir *) buf;
+ blen = sizeof(buf);
+ if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) {
+ wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR select", buf, blen);
+
+ for (rec = 1; rec < 10; rec++) {
+ rlen = scard_get_record_len(scard, rec,
+ SIM_RECORD_MODE_ABSOLUTE);
+ if (rlen < 0) {
+ wpa_printf(MSG_DEBUG, "SCARD: Failed to get EF_DIR "
+ "record length");
+ return -1;
+ }
+ blen = sizeof(buf);
+ if (rlen > (int) blen) {
+ wpa_printf(MSG_DEBUG, "SCARD: Too long EF_DIR record");
+ return -1;
+ }
+ if (scard_read_record(scard, buf, rlen, rec,
+ SIM_RECORD_MODE_ABSOLUTE) < 0) {
+ wpa_printf(MSG_DEBUG, "SCARD: Failed to read "
+ "EF_DIR record %d", rec);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR record", buf, rlen);
+
+ if (efdir->appl_template_tag != 0x61) {
+ wpa_printf(MSG_DEBUG, "SCARD: Unexpected application "
+ "template tag 0x%x",
+ efdir->appl_template_tag);
+ continue;
+ }
+
+ if (efdir->appl_template_len > rlen - 2) {
+ wpa_printf(MSG_DEBUG, "SCARD: Too long application "
+ "template (len=%d rlen=%d)",
+ efdir->appl_template_len, rlen);
+ continue;
+ }
+
+ if (efdir->appl_id_tag != 0x4f) {
+ wpa_printf(MSG_DEBUG, "SCARD: Unexpected application "
+ "identifier tag 0x%x", efdir->appl_id_tag);
+ continue;
+ }
+
+ if (efdir->aid_len < 1 || efdir->aid_len > 16) {
+ wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %d",
+ efdir->aid_len);
+ continue;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record",
+ efdir->rid, efdir->aid_len);
+
+ if (efdir->appl_code[0] == 0x10 &&
+ efdir->appl_code[1] == 0x02) {
+ wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app found from "
+ "EF_DIR record %d", rec);
+ break;
+ }
+ }
+
+ if (rec >= 10) {
+ wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app not found "
+ "from EF_DIR records");
+ return -1;
+ }
+
+ if (efdir->aid_len > maxlen) {
+ wpa_printf(MSG_DEBUG, "SCARD: Too long AID");
+ return -1;
+ }
+
+ os_memcpy(aid, efdir->rid, efdir->aid_len);
+
+ return efdir->aid_len;
+}
+
+
+/**
+ * scard_init - Initialize SIM/USIM connection using PC/SC
+ * @sim_type: Allowed SIM types (SIM, USIM, or both)
+ * Returns: Pointer to private data structure, or %NULL on failure
+ *
+ * This function is used to initialize SIM/USIM connection. PC/SC is used to
+ * open connection to the SIM/USIM card and the card is verified to support the
+ * selected sim_type. In addition, local flag is set if a PIN is needed to
+ * access some of the card functions. Once the connection is not needed
+ * anymore, scard_deinit() can be used to close it.
+ */
+struct scard_data * scard_init(scard_sim_type sim_type)
+{
+ long ret;
+ unsigned long len;
+ struct scard_data *scard;
+#ifdef CONFIG_NATIVE_WINDOWS
+ TCHAR *readers = NULL;
+#else /* CONFIG_NATIVE_WINDOWS */
+ char *readers = NULL;
+#endif /* CONFIG_NATIVE_WINDOWS */
+ unsigned char buf[100];
+ size_t blen;
+ int transaction = 0;
+ int pin_needed;
+
+ wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface");
+ if (mingw_load_symbols())
+ return NULL;
+ scard = os_zalloc(sizeof(*scard));
+ if (scard == NULL)
+ return NULL;
+
+ ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
+ &scard->ctx);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card "
+ "context (err=%ld)", ret);
+ goto failed;
+ }
+
+ ret = SCardListReaders(scard->ctx, NULL, NULL, &len);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed "
+ "(err=%ld)", ret);
+ goto failed;
+ }
+
+#ifdef UNICODE
+ len *= 2;
+#endif /* UNICODE */
+ readers = os_malloc(len);
+ if (readers == NULL) {
+ wpa_printf(MSG_INFO, "SCARD: malloc failed\n");
+ goto failed;
+ }
+
+ ret = SCardListReaders(scard->ctx, NULL, readers, &len);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) "
+ "(err=%ld)", ret);
+ goto failed;
+ }
+ if (len < 3) {
+ wpa_printf(MSG_WARNING, "SCARD: No smart card readers "
+ "available.");
+ goto failed;
+ }
+ /* readers is a list of available reader. Last entry is terminated with
+ * double NUL.
+ * TODO: add support for selecting the reader; now just use the first
+ * one.. */
+#ifdef UNICODE
+ wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", readers);
+#else /* UNICODE */
+ wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers);
+#endif /* UNICODE */
+
+ ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED,
+ SCARD_PROTOCOL_T0, &scard->card, &scard->protocol);
+ if (ret != SCARD_S_SUCCESS) {
+ if (ret == (long) SCARD_E_NO_SMARTCARD)
+ wpa_printf(MSG_INFO, "No smart card inserted.");
+ else
+ wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret);
+ goto failed;
+ }
+
+ os_free(readers);
+ readers = NULL;
+
+ wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)",
+ (unsigned int) scard->card, scard->protocol,
+ scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1");
+
+ ret = SCardBeginTransaction(scard->card);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "SCARD: Could not begin transaction: "
+ "0x%x", (unsigned int) ret);
+ goto failed;
+ }
+ transaction = 1;
+
+ blen = sizeof(buf);
+
+ scard->sim_type = SCARD_GSM_SIM;
+ if (sim_type == SCARD_USIM_ONLY || sim_type == SCARD_TRY_BOTH) {
+ wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support");
+ if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen,
+ SCARD_USIM, NULL, 0)) {
+ wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported");
+ if (sim_type == SCARD_USIM_ONLY)
+ goto failed;
+ wpa_printf(MSG_DEBUG, "SCARD: Trying to use GSM SIM");
+ scard->sim_type = SCARD_GSM_SIM;
+ } else {
+ wpa_printf(MSG_DEBUG, "SCARD: USIM is supported");
+ scard->sim_type = SCARD_USIM;
+ }
+ }
+
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ blen = sizeof(buf);
+ if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) {
+ wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF");
+ goto failed;
+ }
+
+ blen = sizeof(buf);
+ if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) {
+ wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF");
+ goto failed;
+ }
+ } else {
+ unsigned char aid[32];
+ int aid_len;
+
+ aid_len = scard_get_aid(scard, aid, sizeof(aid));
+ if (aid_len < 0) {
+ wpa_printf(MSG_DEBUG, "SCARD: Failed to find AID for "
+ "3G USIM app - try to use standard 3G RID");
+ os_memcpy(aid, "\xa0\x00\x00\x00\x87", 5);
+ aid_len = 5;
+ }
+ wpa_hexdump(MSG_DEBUG, "SCARD: 3G USIM AID", aid, aid_len);
+
+ /* Select based on AID = 3G RID from EF_DIR. This is usually
+ * starting with A0 00 00 00 87. */
+ blen = sizeof(buf);
+ if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type,
+ aid, aid_len)) {
+ wpa_printf(MSG_INFO, "SCARD: Failed to read 3G USIM "
+ "app");
+ wpa_hexdump(MSG_INFO, "SCARD: 3G USIM AID",
+ aid, aid_len);
+ goto failed;
+ }
+ }
+
+ /* Verify whether CHV1 (PIN1) is needed to access the card. */
+ pin_needed = scard_pin_needed(scard, buf, blen);
+ if (pin_needed < 0) {
+ wpa_printf(MSG_DEBUG, "SCARD: Failed to determine whether PIN "
+ "is needed");
+ goto failed;
+ }
+ if (pin_needed) {
+ scard->pin1_required = 1;
+ wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access");
+ }
+
+ ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "SCARD: Could not end transaction: "
+ "0x%x", (unsigned int) ret);
+ }
+
+ return scard;
+
+failed:
+ if (transaction)
+ SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
+ os_free(readers);
+ scard_deinit(scard);
+ return NULL;
+}
+
+
+/**
+ * scard_set_pin - Set PIN (CHV1/PIN1) code for accessing SIM/USIM commands
+ * @scard: Pointer to private data from scard_init()
+ * @pin: PIN code as an ASCII string (e.g., "1234")
+ * Returns: 0 on success, -1 on failure
+ */
+int scard_set_pin(struct scard_data *scard, const char *pin)
+{
+ if (scard == NULL)
+ return -1;
+
+ /* Verify whether CHV1 (PIN1) is needed to access the card. */
+ if (scard->pin1_required) {
+ if (pin == NULL) {
+ wpa_printf(MSG_DEBUG, "No PIN configured for SIM "
+ "access");
+ return -1;
+ }
+ if (scard_verify_pin(scard, pin)) {
+ wpa_printf(MSG_INFO, "PIN verification failed for "
+ "SIM access");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * scard_deinit - Deinitialize SIM/USIM connection
+ * @scard: Pointer to private data from scard_init()
+ *
+ * This function closes the SIM/USIM connect opened with scard_init().
+ */
+void scard_deinit(struct scard_data *scard)
+{
+ long ret;
+
+ if (scard == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface");
+ if (scard->card) {
+ ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect "
+ "smart card (err=%ld)", ret);
+ }
+ }
+
+ if (scard->ctx) {
+ ret = SCardReleaseContext(scard->ctx);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "Failed to release smart card "
+ "context (err=%ld)", ret);
+ }
+ }
+ os_free(scard);
+ mingw_unload_symbols();
+}
+
+
+static long scard_transmit(struct scard_data *scard,
+ unsigned char *_send, size_t send_len,
+ unsigned char *_recv, size_t *recv_len)
+{
+ long ret;
+ unsigned long rlen;
+
+ wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send",
+ _send, send_len);
+ rlen = *recv_len;
+ ret = SCardTransmit(scard->card,
+ scard->protocol == SCARD_PROTOCOL_T1 ?
+ SCARD_PCI_T1 : SCARD_PCI_T0,
+ _send, (unsigned long) send_len,
+ NULL, _recv, &rlen);
+ *recv_len = rlen;
+ if (ret == SCARD_S_SUCCESS) {
+ wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv",
+ _recv, rlen);
+ } else {
+ wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
+ "(err=0x%lx)", ret);
+ }
+ return ret;
+}
+
+
+static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
+ unsigned char *buf, size_t *buf_len,
+ sim_types sim_type, unsigned char *aid,
+ size_t aidlen)
+{
+ long ret;
+ unsigned char resp[3];
+ unsigned char cmd[50] = { SIM_CMD_SELECT };
+ int cmdlen;
+ unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
+ size_t len, rlen;
+
+ if (sim_type == SCARD_USIM) {
+ cmd[0] = USIM_CLA;
+ cmd[3] = 0x04;
+ get_resp[0] = USIM_CLA;
+ }
+
+ wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id);
+ if (aid) {
+ wpa_hexdump(MSG_DEBUG, "SCARD: select file by AID",
+ aid, aidlen);
+ if (5 + aidlen > sizeof(cmd))
+ return -1;
+ cmd[2] = 0x04; /* Select by AID */
+ cmd[4] = aidlen; /* len */
+ os_memcpy(cmd + 5, aid, aidlen);
+ cmdlen = 5 + aidlen;
+ } else {
+ cmd[5] = file_id >> 8;
+ cmd[6] = file_id & 0xff;
+ cmdlen = 7;
+ }
+ len = sizeof(resp);
+ ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
+ "(err=0x%lx)", ret);
+ return -1;
+ }
+
+ if (len != 2) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected resp len "
+ "%d (expected 2)", (int) len);
+ return -1;
+ }
+
+ if (resp[0] == 0x98 && resp[1] == 0x04) {
+ /* Security status not satisfied (PIN_WLAN) */
+ wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied "
+ "(PIN_WLAN)");
+ return -1;
+ }
+
+ if (resp[0] == 0x6e) {
+ wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported");
+ return -1;
+ }
+
+ if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x "
+ "(expected 0x61, 0x6c, or 0x9f)", resp[0]);
+ return -1;
+ }
+ /* Normal ending of command; resp[1] bytes available */
+ get_resp[4] = resp[1];
+ wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)",
+ resp[1]);
+
+ rlen = *buf_len;
+ ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen);
+ if (ret == SCARD_S_SUCCESS) {
+ *buf_len = resp[1] < rlen ? resp[1] : rlen;
+ return 0;
+ }
+
+ wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret);
+ return -1;
+}
+
+
+static int scard_select_file(struct scard_data *scard, unsigned short file_id,
+ unsigned char *buf, size_t *buf_len)
+{
+ return _scard_select_file(scard, file_id, buf, buf_len,
+ scard->sim_type, NULL, 0);
+}
+
+
+static int scard_get_record_len(struct scard_data *scard, unsigned char recnum,
+ unsigned char mode)
+{
+ unsigned char buf[255];
+ unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ };
+ size_t blen;
+ long ret;
+
+ if (scard->sim_type == SCARD_USIM)
+ cmd[0] = USIM_CLA;
+ cmd[2] = recnum;
+ cmd[3] = mode;
+ cmd[4] = sizeof(buf);
+
+ blen = sizeof(buf);
+ ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+ if (ret != SCARD_S_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "SCARD: failed to determine file "
+ "length for record %d", recnum);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response",
+ buf, blen);
+
+ if (blen < 2 || buf[0] != 0x6c) {
+ wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file "
+ "length determination");
+ return -1;
+ }
+
+ return buf[1];
+}
+
+
+static int scard_read_record(struct scard_data *scard,
+ unsigned char *data, size_t len,
+ unsigned char recnum, unsigned char mode)
+{
+ unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ };
+ size_t blen = len + 3;
+ unsigned char *buf;
+ long ret;
+
+ if (scard->sim_type == SCARD_USIM)
+ cmd[0] = USIM_CLA;
+ cmd[2] = recnum;
+ cmd[3] = mode;
+ cmd[4] = len;
+
+ buf = os_malloc(blen);
+ if (buf == NULL)
+ return -1;
+
+ ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+ if (ret != SCARD_S_SUCCESS) {
+ os_free(buf);
+ return -2;
+ }
+ if (blen != len + 2) {
+ wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected "
+ "length %ld (expected %ld)",
+ (long) blen, (long) len + 2);
+ os_free(buf);
+ return -3;
+ }
+
+ if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
+ wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected "
+ "status %02x %02x (expected 90 00)",
+ buf[len], buf[len + 1]);
+ os_free(buf);
+ return -4;
+ }
+
+ os_memcpy(data, buf, len);
+ os_free(buf);
+
+ return 0;
+}
+
+
+static int scard_read_file(struct scard_data *scard,
+ unsigned char *data, size_t len)
+{
+ unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ };
+ size_t blen = len + 3;
+ unsigned char *buf;
+ long ret;
+
+ cmd[4] = len;
+
+ buf = os_malloc(blen);
+ if (buf == NULL)
+ return -1;
+
+ if (scard->sim_type == SCARD_USIM)
+ cmd[0] = USIM_CLA;
+ ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+ if (ret != SCARD_S_SUCCESS) {
+ os_free(buf);
+ return -2;
+ }
+ if (blen != len + 2) {
+ wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
+ "length %ld (expected %ld)",
+ (long) blen, (long) len + 2);
+ os_free(buf);
+ return -3;
+ }
+
+ if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
+ wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
+ "status %02x %02x (expected 90 00)",
+ buf[len], buf[len + 1]);
+ os_free(buf);
+ return -4;
+ }
+
+ os_memcpy(data, buf, len);
+ os_free(buf);
+
+ return 0;
+}
+
+
+static int scard_verify_pin(struct scard_data *scard, const char *pin)
+{
+ long ret;
+ unsigned char resp[3];
+ unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 };
+ size_t len;
+
+ wpa_printf(MSG_DEBUG, "SCARD: verifying PIN");
+
+ if (pin == NULL || os_strlen(pin) > 8)
+ return -1;
+
+ if (scard->sim_type == SCARD_USIM)
+ cmd[0] = USIM_CLA;
+ os_memcpy(cmd + 5, pin, os_strlen(pin));
+ os_memset(cmd + 5 + os_strlen(pin), 0xff, 8 - os_strlen(pin));
+
+ len = sizeof(resp);
+ ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+ if (ret != SCARD_S_SUCCESS)
+ return -2;
+
+ if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) {
+ wpa_printf(MSG_WARNING, "SCARD: PIN verification failed");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully");
+ return 0;
+}
+
+
+/**
+ * scard_get_imsi - Read IMSI from SIM/USIM card
+ * @scard: Pointer to private data from scard_init()
+ * @imsi: Buffer for IMSI
+ * @len: Length of imsi buffer; set to IMSI length on success
+ * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file
+ * selection returns invalid result code, -3 if parsing FSP template file fails
+ * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set
+ * to needed length), -5 if reading IMSI file fails.
+ *
+ * This function can be used to read IMSI from the SIM/USIM card. If the IMSI
+ * file is PIN protected, scard_set_pin() must have been used to set the
+ * correct PIN code before calling scard_get_imsi().
+ */
+int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len)
+{
+ unsigned char buf[100];
+ size_t blen, imsilen, i;
+ char *pos;
+
+ wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI");
+ blen = sizeof(buf);
+ if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen))
+ return -1;
+ if (blen < 4) {
+ wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI "
+ "header (len=%ld)", (long) blen);
+ return -2;
+ }
+
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ blen = (buf[2] << 8) | buf[3];
+ } else {
+ int file_size;
+ if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
+ return -3;
+ blen = file_size;
+ }
+ if (blen < 2 || blen > sizeof(buf)) {
+ wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%ld",
+ (long) blen);
+ return -3;
+ }
+
+ imsilen = (blen - 2) * 2 + 1;
+ wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%ld imsilen=%ld",
+ (long) blen, (long) imsilen);
+ if (blen < 2 || imsilen > *len) {
+ *len = imsilen;
+ return -4;
+ }
+
+ if (scard_read_file(scard, buf, blen))
+ return -5;
+
+ pos = imsi;
+ *pos++ = '0' + (buf[1] >> 4 & 0x0f);
+ for (i = 2; i < blen; i++) {
+ unsigned char digit;
+
+ digit = buf[i] & 0x0f;
+ if (digit < 10)
+ *pos++ = '0' + digit;
+ else
+ imsilen--;
+
+ digit = buf[i] >> 4 & 0x0f;
+ if (digit < 10)
+ *pos++ = '0' + digit;
+ else
+ imsilen--;
+ }
+ *len = imsilen;
+
+ return 0;
+}
+
+
+/**
+ * scard_gsm_auth - Run GSM authentication command on SIM card
+ * @scard: Pointer to private data from scard_init()
+ * @_rand: 16-byte RAND value from HLR/AuC
+ * @sres: 4-byte buffer for SRES
+ * @kc: 8-byte buffer for Kc
+ * Returns: 0 on success, -1 if SIM/USIM connection has not been initialized,
+ * -2 if authentication command execution fails, -3 if unknown response code
+ * for authentication command is received, -4 if reading of response fails,
+ * -5 if if response data is of unexpected length
+ *
+ * This function performs GSM authentication using SIM/USIM card and the
+ * provided RAND value from HLR/AuC. If authentication command can be completed
+ * successfully, SRES and Kc values will be written into sres and kc buffers.
+ */
+int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
+ unsigned char *sres, unsigned char *kc)
+{
+ unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG };
+ int cmdlen;
+ unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
+ unsigned char resp[3], buf[12 + 3 + 2];
+ size_t len;
+ long ret;
+
+ if (scard == NULL)
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", _rand, 16);
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ cmdlen = 5 + 16;
+ os_memcpy(cmd + 5, _rand, 16);
+ } else {
+ cmdlen = 5 + 1 + 16;
+ cmd[0] = USIM_CLA;
+ cmd[3] = 0x80;
+ cmd[4] = 17;
+ cmd[5] = 16;
+ os_memcpy(cmd + 6, _rand, 16);
+ }
+ len = sizeof(resp);
+ ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
+ if (ret != SCARD_S_SUCCESS)
+ return -2;
+
+ if ((scard->sim_type == SCARD_GSM_SIM &&
+ (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) ||
+ (scard->sim_type == SCARD_USIM &&
+ (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM "
+ "auth request (len=%ld resp=%02x %02x)",
+ (long) len, resp[0], resp[1]);
+ return -3;
+ }
+ get_resp[4] = resp[1];
+
+ len = sizeof(buf);
+ ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
+ if (ret != SCARD_S_SUCCESS)
+ return -4;
+
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ if (len != 4 + 8 + 2) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected data "
+ "length for GSM auth (len=%ld, expected 14)",
+ (long) len);
+ return -5;
+ }
+ os_memcpy(sres, buf, 4);
+ os_memcpy(kc, buf + 4, 8);
+ } else {
+ if (len != 1 + 4 + 1 + 8 + 2) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected data "
+ "length for USIM auth (len=%ld, "
+ "expected 16)", (long) len);
+ return -5;
+ }
+ if (buf[0] != 4 || buf[5] != 8) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc "
+ "length (%d %d, expected 4 8)",
+ buf[0], buf[5]);
+ }
+ os_memcpy(sres, buf + 1, 4);
+ os_memcpy(kc, buf + 6, 8);
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4);
+ wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8);
+
+ return 0;
+}
+
+
+/**
+ * scard_umts_auth - Run UMTS authentication command on USIM card
+ * @scard: Pointer to private data from scard_init()
+ * @_rand: 16-byte RAND value from HLR/AuC
+ * @autn: 16-byte AUTN value from HLR/AuC
+ * @res: 16-byte buffer for RES
+ * @res_len: Variable that will be set to RES length
+ * @ik: 16-byte buffer for IK
+ * @ck: 16-byte buffer for CK
+ * @auts: 14-byte buffer for AUTS
+ * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization
+ * failure
+ *
+ * This function performs AKA authentication using USIM card and the provided
+ * RAND and AUTN values from HLR/AuC. If authentication command can be
+ * completed successfully, RES, IK, and CK values will be written into provided
+ * buffers and res_len is set to length of received RES value. If USIM reports
+ * synchronization failure, the received AUTS value will be written into auts
+ * buffer. In this case, RES, IK, and CK are not valid.
+ */
+int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
+ const unsigned char *autn,
+ unsigned char *res, size_t *res_len,
+ unsigned char *ik, unsigned char *ck, unsigned char *auts)
+{
+ unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] =
+ { USIM_CMD_RUN_UMTS_ALG };
+ unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE };
+ unsigned char resp[3], buf[64], *pos, *end;
+ size_t len;
+ long ret;
+
+ if (scard == NULL)
+ return -1;
+
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS "
+ "auth");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", _rand, AKA_RAND_LEN);
+ wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN);
+ cmd[5] = AKA_RAND_LEN;
+ os_memcpy(cmd + 6, _rand, AKA_RAND_LEN);
+ cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN;
+ os_memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN);
+
+ len = sizeof(resp);
+ ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+ if (ret != SCARD_S_SUCCESS)
+ return -1;
+
+ if (len <= sizeof(resp))
+ wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len);
+
+ if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) {
+ wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - "
+ "MAC != XMAC");
+ return -1;
+ } else if (len != 2 || resp[0] != 0x61) {
+ wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS "
+ "auth request (len=%ld resp=%02x %02x)",
+ (long) len, resp[0], resp[1]);
+ return -1;
+ }
+ get_resp[4] = resp[1];
+
+ len = sizeof(buf);
+ ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
+ if (ret != SCARD_S_SUCCESS || len > sizeof(buf))
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len);
+ if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc &&
+ buf[1] == AKA_AUTS_LEN) {
+ wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure");
+ os_memcpy(auts, buf + 2, AKA_AUTS_LEN);
+ wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN);
+ return -2;
+ } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) {
+ pos = buf + 1;
+ end = buf + len;
+
+ /* RES */
+ if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "SCARD: Invalid RES");
+ return -1;
+ }
+ *res_len = *pos++;
+ os_memcpy(res, pos, *res_len);
+ pos += *res_len;
+ wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len);
+
+ /* CK */
+ if (pos[0] != CK_LEN || pos + CK_LEN > end) {
+ wpa_printf(MSG_DEBUG, "SCARD: Invalid CK");
+ return -1;
+ }
+ pos++;
+ os_memcpy(ck, pos, CK_LEN);
+ pos += CK_LEN;
+ wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN);
+
+ /* IK */
+ if (pos[0] != IK_LEN || pos + IK_LEN > end) {
+ wpa_printf(MSG_DEBUG, "SCARD: Invalid IK");
+ return -1;
+ }
+ pos++;
+ os_memcpy(ik, pos, IK_LEN);
+ pos += IK_LEN;
+ wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN);
+
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response");
+ return -1;
+}
diff --git a/src/utils/pcsc_funcs.h b/src/utils/pcsc_funcs.h
new file mode 100644
index 0000000..543f7c5
--- /dev/null
+++ b/src/utils/pcsc_funcs.h
@@ -0,0 +1,68 @@
+/*
+ * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PCSC_FUNCS_H
+#define PCSC_FUNCS_H
+
+/* GSM files
+ * File type in first octet:
+ * 3F = Master File
+ * 7F = Dedicated File
+ * 2F = Elementary File under the Master File
+ * 6F = Elementary File under a Dedicated File
+ */
+#define SCARD_FILE_MF 0x3F00
+#define SCARD_FILE_GSM_DF 0x7F20
+#define SCARD_FILE_UMTS_DF 0x7F50
+#define SCARD_FILE_GSM_EF_IMSI 0x6F07
+#define SCARD_FILE_EF_DIR 0x2F00
+#define SCARD_FILE_EF_ICCID 0x2FE2
+#define SCARD_FILE_EF_CK 0x6FE1
+#define SCARD_FILE_EF_IK 0x6FE2
+
+#define SCARD_CHV1_OFFSET 13
+#define SCARD_CHV1_FLAG 0x80
+
+typedef enum {
+ SCARD_GSM_SIM_ONLY,
+ SCARD_USIM_ONLY,
+ SCARD_TRY_BOTH
+} scard_sim_type;
+
+
+#ifdef PCSC_FUNCS
+struct scard_data * scard_init(scard_sim_type sim_type);
+void scard_deinit(struct scard_data *scard);
+
+int scard_set_pin(struct scard_data *scard, const char *pin);
+int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len);
+int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
+ unsigned char *sres, unsigned char *kc);
+int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
+ const unsigned char *autn,
+ unsigned char *res, size_t *res_len,
+ unsigned char *ik, unsigned char *ck, unsigned char *auts);
+
+#else /* PCSC_FUNCS */
+
+#define scard_init(s) NULL
+#define scard_deinit(s) do { } while (0)
+#define scard_set_pin(s, p) -1
+#define scard_get_imsi(s, i, l) -1
+#define scard_gsm_auth(s, r, s2, k) -1
+#define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1
+
+#endif /* PCSC_FUNCS */
+
+#endif /* PCSC_FUNCS_H */
diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c
new file mode 100644
index 0000000..804473f
--- /dev/null
+++ b/src/utils/radiotap.c
@@ -0,0 +1,287 @@
+/*
+ * Radiotap parser
+ *
+ * Copyright 2007 Andy Green <andy@warmcat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ *
+ * Modified for userspace by Johannes Berg <johannes@sipsolutions.net>
+ * I only modified some things on top to ease syncing should bugs be found.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "radiotap_iter.h"
+
+#define le16_to_cpu le_to_host16
+#define le32_to_cpu le_to_host32
+#define __le32 uint32_t
+#define ulong unsigned long
+#define unlikely(cond) (cond)
+#define get_unaligned(p) \
+({ \
+ struct packed_dummy_struct { \
+ typeof(*(p)) __val; \
+ } __attribute__((packed)) *__ptr = (void *) (p); \
+ \
+ __ptr->__val; \
+})
+
+/* function prototypes and related defs are in radiotap_iter.h */
+
+/**
+ * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
+ * @iterator: radiotap_iterator to initialize
+ * @radiotap_header: radiotap header to parse
+ * @max_length: total length we can parse into (eg, whole packet length)
+ *
+ * Returns: 0 or a negative error code if there is a problem.
+ *
+ * This function initializes an opaque iterator struct which can then
+ * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap
+ * argument which is present in the header. It knows about extended
+ * present headers and handles them.
+ *
+ * How to use:
+ * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator
+ * struct ieee80211_radiotap_iterator (no need to init the struct beforehand)
+ * checking for a good 0 return code. Then loop calling
+ * __ieee80211_radiotap_iterator_next()... it returns either 0,
+ * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem.
+ * The iterator's @this_arg member points to the start of the argument
+ * associated with the current argument index that is present, which can be
+ * found in the iterator's @this_arg_index member. This arg index corresponds
+ * to the IEEE80211_RADIOTAP_... defines.
+ *
+ * Radiotap header length:
+ * You can find the CPU-endian total radiotap header length in
+ * iterator->max_length after executing ieee80211_radiotap_iterator_init()
+ * successfully.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned. Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ *
+ * Example code:
+ * See Documentation/networking/radiotap-headers.txt
+ */
+
+int ieee80211_radiotap_iterator_init(
+ struct ieee80211_radiotap_iterator *iterator,
+ struct ieee80211_radiotap_header *radiotap_header,
+ int max_length)
+{
+ /* Linux only supports version 0 radiotap format */
+ if (radiotap_header->it_version)
+ return -EINVAL;
+
+ /* sanity check for allowed length and radiotap length field */
+ if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len)))
+ return -EINVAL;
+
+ iterator->rtheader = radiotap_header;
+ iterator->max_length = le16_to_cpu(get_unaligned(
+ &radiotap_header->it_len));
+ iterator->arg_index = 0;
+ iterator->bitmap_shifter = le32_to_cpu(get_unaligned(
+ &radiotap_header->it_present));
+ iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header);
+ iterator->this_arg = NULL;
+
+ /* find payload start allowing for extended bitmap(s) */
+
+ if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) {
+ while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) &
+ (1<<IEEE80211_RADIOTAP_EXT)) {
+ iterator->arg += sizeof(u32);
+
+ /*
+ * check for insanity where the present bitmaps
+ * keep claiming to extend up to or even beyond the
+ * stated radiotap header length
+ */
+
+ if (((ulong)iterator->arg - (ulong)iterator->rtheader)
+ > (ulong)iterator->max_length)
+ return -EINVAL;
+ }
+
+ iterator->arg += sizeof(u32);
+
+ /*
+ * no need to check again for blowing past stated radiotap
+ * header length, because ieee80211_radiotap_iterator_next
+ * checks it before it is dereferenced
+ */
+ }
+
+ /* we are all initialized happily */
+
+ return 0;
+}
+
+
+/**
+ * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
+ * @iterator: radiotap_iterator to move to next arg (if any)
+ *
+ * Returns: 0 if there is an argument to handle,
+ * -ENOENT if there are no more args or -EINVAL
+ * if there is something else wrong.
+ *
+ * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*)
+ * in @this_arg_index and sets @this_arg to point to the
+ * payload for the field. It takes care of alignment handling and extended
+ * present fields. @this_arg can be changed by the caller (eg,
+ * incremented to move inside a compound argument like
+ * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in
+ * little-endian format whatever the endianess of your CPU.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned. Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ */
+
+int ieee80211_radiotap_iterator_next(
+ struct ieee80211_radiotap_iterator *iterator)
+{
+
+ /*
+ * small length lookup table for all radiotap types we heard of
+ * starting from b0 in the bitmap, so we can walk the payload
+ * area of the radiotap header
+ *
+ * There is a requirement to pad args, so that args
+ * of a given length must begin at a boundary of that length
+ * -- but note that compound args are allowed (eg, 2 x u16
+ * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not
+ * a reliable indicator of alignment requirement.
+ *
+ * upper nybble: content alignment for arg
+ * lower nybble: content length for arg
+ */
+
+ static const u8 rt_sizes[] = {
+ [IEEE80211_RADIOTAP_TSFT] = 0x88,
+ [IEEE80211_RADIOTAP_FLAGS] = 0x11,
+ [IEEE80211_RADIOTAP_RATE] = 0x11,
+ [IEEE80211_RADIOTAP_CHANNEL] = 0x24,
+ [IEEE80211_RADIOTAP_FHSS] = 0x22,
+ [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11,
+ [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11,
+ [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22,
+ [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22,
+ [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22,
+ [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11,
+ [IEEE80211_RADIOTAP_ANTENNA] = 0x11,
+ [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11,
+ [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11,
+ [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22,
+ [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22,
+ [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11,
+ [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11,
+ /*
+ * add more here as they are defined in
+ * include/net/ieee80211_radiotap.h
+ */
+ };
+
+ /*
+ * for every radiotap entry we can at
+ * least skip (by knowing the length)...
+ */
+
+ while (iterator->arg_index < (int) sizeof(rt_sizes)) {
+ int hit = 0;
+ int pad;
+
+ if (!(iterator->bitmap_shifter & 1))
+ goto next_entry; /* arg not present */
+
+ /*
+ * arg is present, account for alignment padding
+ * 8-bit args can be at any alignment
+ * 16-bit args must start on 16-bit boundary
+ * 32-bit args must start on 32-bit boundary
+ * 64-bit args must start on 64-bit boundary
+ *
+ * note that total arg size can differ from alignment of
+ * elements inside arg, so we use upper nybble of length
+ * table to base alignment on
+ *
+ * also note: these alignments are ** relative to the
+ * start of the radiotap header **. There is no guarantee
+ * that the radiotap header itself is aligned on any
+ * kind of boundary.
+ *
+ * the above is why get_unaligned() is used to dereference
+ * multibyte elements from the radiotap area
+ */
+
+ pad = (((ulong)iterator->arg) -
+ ((ulong)iterator->rtheader)) &
+ ((rt_sizes[iterator->arg_index] >> 4) - 1);
+
+ if (pad)
+ iterator->arg +=
+ (rt_sizes[iterator->arg_index] >> 4) - pad;
+
+ /*
+ * this is what we will return to user, but we need to
+ * move on first so next call has something fresh to test
+ */
+ iterator->this_arg_index = iterator->arg_index;
+ iterator->this_arg = iterator->arg;
+ hit = 1;
+
+ /* internally move on the size of this arg */
+ iterator->arg += rt_sizes[iterator->arg_index] & 0x0f;
+
+ /*
+ * check for insanity where we are given a bitmap that
+ * claims to have more arg content than the length of the
+ * radiotap section. We will normally end up equalling this
+ * max_length on the last arg, never exceeding it.
+ */
+
+ if (((ulong)iterator->arg - (ulong)iterator->rtheader) >
+ (ulong) iterator->max_length)
+ return -EINVAL;
+
+ next_entry:
+ iterator->arg_index++;
+ if (unlikely((iterator->arg_index & 31) == 0)) {
+ /* completed current u32 bitmap */
+ if (iterator->bitmap_shifter & 1) {
+ /* b31 was set, there is more */
+ /* move to next u32 bitmap */
+ iterator->bitmap_shifter = le32_to_cpu(
+ get_unaligned(iterator->next_bitmap));
+ iterator->next_bitmap++;
+ } else
+ /* no more bitmaps: end */
+ iterator->arg_index = sizeof(rt_sizes);
+ } else /* just try the next bit */
+ iterator->bitmap_shifter >>= 1;
+
+ /* if we found a valid arg earlier, return it now */
+ if (hit)
+ return 0;
+ }
+
+ /* we don't know how to handle any more args, we're done */
+ return -ENOENT;
+}
diff --git a/src/utils/radiotap.h b/src/utils/radiotap.h
new file mode 100644
index 0000000..508264c
--- /dev/null
+++ b/src/utils/radiotap.h
@@ -0,0 +1,242 @@
+/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */
+/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2003, 2004 David Young. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
+ * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+/*
+ * Modifications to fit into the linux IEEE 802.11 stack,
+ * Mike Kershaw (dragorn@kismetwireless.net)
+ */
+
+#ifndef IEEE80211RADIOTAP_H
+#define IEEE80211RADIOTAP_H
+
+#include <stdint.h>
+
+/* Base version of the radiotap packet header data */
+#define PKTHDR_RADIOTAP_VERSION 0
+
+/* A generic radio capture format is desirable. There is one for
+ * Linux, but it is neither rigidly defined (there were not even
+ * units given for some fields) nor easily extensible.
+ *
+ * I suggest the following extensible radio capture format. It is
+ * based on a bitmap indicating which fields are present.
+ *
+ * I am trying to describe precisely what the application programmer
+ * should expect in the following, and for that reason I tell the
+ * units and origin of each measurement (where it applies), or else I
+ * use sufficiently weaselly language ("is a monotonically nondecreasing
+ * function of...") that I cannot set false expectations for lawyerly
+ * readers.
+ */
+
+/* The radio capture header precedes the 802.11 header.
+ * All data in the header is little endian on all platforms.
+ */
+struct ieee80211_radiotap_header {
+ uint8_t it_version; /* Version 0. Only increases
+ * for drastic changes,
+ * introduction of compatible
+ * new fields does not count.
+ */
+ uint8_t it_pad;
+ uint16_t it_len; /* length of the whole
+ * header in bytes, including
+ * it_version, it_pad,
+ * it_len, and data fields.
+ */
+ uint32_t it_present; /* A bitmap telling which
+ * fields are present. Set bit 31
+ * (0x80000000) to extend the
+ * bitmap by another 32 bits.
+ * Additional extensions are made
+ * by setting bit 31.
+ */
+};
+
+/* Name Data type Units
+ * ---- --------- -----
+ *
+ * IEEE80211_RADIOTAP_TSFT __le64 microseconds
+ *
+ * Value in microseconds of the MAC's 64-bit 802.11 Time
+ * Synchronization Function timer when the first bit of the
+ * MPDU arrived at the MAC. For received frames, only.
+ *
+ * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap
+ *
+ * Tx/Rx frequency in MHz, followed by flags (see below).
+ *
+ * IEEE80211_RADIOTAP_FHSS uint16_t see below
+ *
+ * For frequency-hopping radios, the hop set (first byte)
+ * and pattern (second byte).
+ *
+ * IEEE80211_RADIOTAP_RATE u8 500kb/s
+ *
+ * Tx/Rx data rate
+ *
+ * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from
+ * one milliwatt (dBm)
+ *
+ * RF signal power at the antenna, decibel difference from
+ * one milliwatt.
+ *
+ * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from
+ * one milliwatt (dBm)
+ *
+ * RF noise power at the antenna, decibel difference from one
+ * milliwatt.
+ *
+ * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB)
+ *
+ * RF signal power at the antenna, decibel difference from an
+ * arbitrary, fixed reference.
+ *
+ * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB)
+ *
+ * RF noise power at the antenna, decibel difference from an
+ * arbitrary, fixed reference point.
+ *
+ * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless
+ *
+ * Quality of Barker code lock. Unitless. Monotonically
+ * nondecreasing with "better" lock strength. Called "Signal
+ * Quality" in datasheets. (Is there a standard way to measure
+ * this?)
+ *
+ * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless
+ *
+ * Transmit power expressed as unitless distance from max
+ * power set at factory calibration. 0 is max power.
+ * Monotonically nondecreasing with lower power levels.
+ *
+ * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB)
+ *
+ * Transmit power expressed as decibel distance from max power
+ * set at factory calibration. 0 is max power. Monotonically
+ * nondecreasing with lower power levels.
+ *
+ * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from
+ * one milliwatt (dBm)
+ *
+ * Transmit power expressed as dBm (decibels from a 1 milliwatt
+ * reference). This is the absolute power level measured at
+ * the antenna port.
+ *
+ * IEEE80211_RADIOTAP_FLAGS u8 bitmap
+ *
+ * Properties of transmitted and received frames. See flags
+ * defined below.
+ *
+ * IEEE80211_RADIOTAP_ANTENNA u8 antenna index
+ *
+ * Unitless indication of the Rx/Tx antenna for this packet.
+ * The first antenna is antenna 0.
+ *
+ * IEEE80211_RADIOTAP_RX_FLAGS uint16_t bitmap
+ *
+ * Properties of received frames. See flags defined below.
+ *
+ * IEEE80211_RADIOTAP_TX_FLAGS uint16_t bitmap
+ *
+ * Properties of transmitted frames. See flags defined below.
+ *
+ * IEEE80211_RADIOTAP_RTS_RETRIES u8 data
+ *
+ * Number of rts retries a transmitted frame used.
+ *
+ * IEEE80211_RADIOTAP_DATA_RETRIES u8 data
+ *
+ * Number of unicast retries a transmitted frame used.
+ *
+ */
+enum ieee80211_radiotap_type {
+ IEEE80211_RADIOTAP_TSFT = 0,
+ IEEE80211_RADIOTAP_FLAGS = 1,
+ IEEE80211_RADIOTAP_RATE = 2,
+ IEEE80211_RADIOTAP_CHANNEL = 3,
+ IEEE80211_RADIOTAP_FHSS = 4,
+ IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5,
+ IEEE80211_RADIOTAP_DBM_ANTNOISE = 6,
+ IEEE80211_RADIOTAP_LOCK_QUALITY = 7,
+ IEEE80211_RADIOTAP_TX_ATTENUATION = 8,
+ IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9,
+ IEEE80211_RADIOTAP_DBM_TX_POWER = 10,
+ IEEE80211_RADIOTAP_ANTENNA = 11,
+ IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
+ IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
+ IEEE80211_RADIOTAP_RX_FLAGS = 14,
+ IEEE80211_RADIOTAP_TX_FLAGS = 15,
+ IEEE80211_RADIOTAP_RTS_RETRIES = 16,
+ IEEE80211_RADIOTAP_DATA_RETRIES = 17,
+ IEEE80211_RADIOTAP_EXT = 31
+};
+
+/* Channel flags. */
+#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */
+#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */
+#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */
+#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */
+#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */
+#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */
+#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */
+#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */
+
+/* For IEEE80211_RADIOTAP_FLAGS */
+#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received
+ * during CFP
+ */
+#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received
+ * with short
+ * preamble
+ */
+#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received
+ * with WEP encryption
+ */
+#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received
+ * with fragmentation
+ */
+#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */
+#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between
+ * 802.11 header and payload
+ * (to 32-bit boundary)
+ */
+/* For IEEE80211_RADIOTAP_RX_FLAGS */
+#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */
+
+/* For IEEE80211_RADIOTAP_TX_FLAGS */
+#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive
+ * retries */
+#define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */
+#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */
+
+#endif /* IEEE80211_RADIOTAP_H */
diff --git a/src/utils/radiotap_iter.h b/src/utils/radiotap_iter.h
new file mode 100644
index 0000000..92a798a
--- /dev/null
+++ b/src/utils/radiotap_iter.h
@@ -0,0 +1,41 @@
+#ifndef __RADIOTAP_ITER_H
+#define __RADIOTAP_ITER_H
+
+#include "radiotap.h"
+
+/* Radiotap header iteration
+ * implemented in radiotap.c
+ */
+/**
+ * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args
+ * @rtheader: pointer to the radiotap header we are walking through
+ * @max_length: length of radiotap header in cpu byte ordering
+ * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg
+ * @this_arg: pointer to current radiotap arg
+ * @arg_index: internal next argument index
+ * @arg: internal next argument pointer
+ * @next_bitmap: internal pointer to next present u32
+ * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
+ */
+
+struct ieee80211_radiotap_iterator {
+ struct ieee80211_radiotap_header *rtheader;
+ int max_length;
+ int this_arg_index;
+ unsigned char *this_arg;
+
+ int arg_index;
+ unsigned char *arg;
+ uint32_t *next_bitmap;
+ uint32_t bitmap_shifter;
+};
+
+extern int ieee80211_radiotap_iterator_init(
+ struct ieee80211_radiotap_iterator *iterator,
+ struct ieee80211_radiotap_header *radiotap_header,
+ int max_length);
+
+extern int ieee80211_radiotap_iterator_next(
+ struct ieee80211_radiotap_iterator *iterator);
+
+#endif /* __RADIOTAP_ITER_H */
diff --git a/src/utils/state_machine.h b/src/utils/state_machine.h
new file mode 100644
index 0000000..31f6672
--- /dev/null
+++ b/src/utils/state_machine.h
@@ -0,0 +1,144 @@
+/*
+ * wpa_supplicant/hostapd - State machine definitions
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file includes a set of pre-processor macros that can be used to
+ * implement a state machine. In addition to including this header file, each
+ * file implementing a state machine must define STATE_MACHINE_DATA to be the
+ * data structure including state variables (enum machine_state,
+ * Boolean changed), and STATE_MACHINE_DEBUG_PREFIX to be a string that is used
+ * as a prefix for all debug messages. If SM_ENTRY_MA macro is used to define
+ * a group of state machines with shared data structure, STATE_MACHINE_ADDR
+ * needs to be defined to point to the MAC address used in debug output.
+ * SM_ENTRY_M macro can be used to define similar group of state machines
+ * without this additional debug info.
+ */
+
+#ifndef STATE_MACHINE_H
+#define STATE_MACHINE_H
+
+/**
+ * SM_STATE - Declaration of a state machine function
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is used to declare a state machine function. It is used in place
+ * of a C function definition to declare functions to be run when the state is
+ * entered by calling SM_ENTER or SM_ENTER_GLOBAL.
+ */
+#define SM_STATE(machine, state) \
+static void sm_ ## machine ## _ ## state ## _Enter(STATE_MACHINE_DATA *sm, \
+ int global)
+
+/**
+ * SM_ENTRY - State machine function entry point
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is used inside each state machine function declared with
+ * SM_STATE. SM_ENTRY should be in the beginning of the function body, but
+ * after declaration of possible local variables. This macro prints debug
+ * information about state transition and update the state machine state.
+ */
+#define SM_ENTRY(machine, state) \
+if (!global || sm->machine ## _state != machine ## _ ## state) { \
+ sm->changed = TRUE; \
+ wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " #machine \
+ " entering state " #state); \
+} \
+sm->machine ## _state = machine ## _ ## state;
+
+/**
+ * SM_ENTRY_M - State machine function entry point for state machine group
+ * @machine: State machine name
+ * @_state: State machine state
+ * @data: State variable prefix (full variable: prefix_state)
+ *
+ * This macro is like SM_ENTRY, but for state machine groups that use a shared
+ * data structure for more than one state machine. Both machine and prefix
+ * parameters are set to "sub-state machine" name. prefix is used to allow more
+ * than one state variable to be stored in the same data structure.
+ */
+#define SM_ENTRY_M(machine, _state, data) \
+if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \
+ sm->changed = TRUE; \
+ wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " \
+ #machine " entering state " #_state); \
+} \
+sm->data ## _ ## state = machine ## _ ## _state;
+
+/**
+ * SM_ENTRY_MA - State machine function entry point for state machine group
+ * @machine: State machine name
+ * @_state: State machine state
+ * @data: State variable prefix (full variable: prefix_state)
+ *
+ * This macro is like SM_ENTRY_M, but a MAC address is included in debug
+ * output. STATE_MACHINE_ADDR has to be defined to point to the MAC address to
+ * be included in debug.
+ */
+#define SM_ENTRY_MA(machine, _state, data) \
+if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \
+ sm->changed = TRUE; \
+ wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " MACSTR " " \
+ #machine " entering state " #_state, \
+ MAC2STR(STATE_MACHINE_ADDR)); \
+} \
+sm->data ## _ ## state = machine ## _ ## _state;
+
+/**
+ * SM_ENTER - Enter a new state machine state
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro expands to a function call to a state machine function defined
+ * with SM_STATE macro. SM_ENTER is used in a state machine step function to
+ * move the state machine to a new state.
+ */
+#define SM_ENTER(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 0)
+
+/**
+ * SM_ENTER_GLOBAL - Enter a new state machine state based on global rule
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is like SM_ENTER, but this is used when entering a new state
+ * based on a global (not specific to any particular state) rule. A separate
+ * macro is used to avoid unwanted debug message floods when the same global
+ * rule is forcing a state machine to remain in on state.
+ */
+#define SM_ENTER_GLOBAL(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 1)
+
+/**
+ * SM_STEP - Declaration of a state machine step function
+ * @machine: State machine name
+ *
+ * This macro is used to declare a state machine step function. It is used in
+ * place of a C function definition to declare a function that is used to move
+ * state machine to a new state based on state variables. This function uses
+ * SM_ENTER and SM_ENTER_GLOBAL macros to enter new state.
+ */
+#define SM_STEP(machine) \
+static void sm_ ## machine ## _Step(STATE_MACHINE_DATA *sm)
+
+/**
+ * SM_STEP_RUN - Call the state machine step function
+ * @machine: State machine name
+ *
+ * This macro expands to a function call to a state machine step function
+ * defined with SM_STEP macro.
+ */
+#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
+
+#endif /* STATE_MACHINE_H */
diff --git a/src/utils/trace.c b/src/utils/trace.c
new file mode 100644
index 0000000..bb3eb24
--- /dev/null
+++ b/src/utils/trace.c
@@ -0,0 +1,329 @@
+/*
+ * Backtrace debugging
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "trace.h"
+
+#ifdef WPA_TRACE
+
+static struct dl_list active_references =
+{ &active_references, &active_references };
+
+#ifdef WPA_TRACE_BFD
+#include <bfd.h>
+#ifdef __linux__
+#include <demangle.h>
+#else /* __linux__ */
+#include <libiberty/demangle.h>
+#endif /* __linux__ */
+
+static char *prg_fname = NULL;
+static bfd *cached_abfd = NULL;
+static asymbol **syms = NULL;
+
+static void get_prg_fname(void)
+{
+ char exe[50], fname[512];
+ int len;
+ os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
+ len = readlink(exe, fname, sizeof(fname) - 1);
+ if (len < 0 || len >= (int) sizeof(fname)) {
+ perror("readlink");
+ return;
+ }
+ fname[len] = '\0';
+ prg_fname = strdup(fname);
+}
+
+
+static bfd * open_bfd(const char *fname)
+{
+ bfd *abfd;
+ char **matching;
+
+ abfd = bfd_openr(prg_fname, NULL);
+ if (abfd == NULL) {
+ wpa_printf(MSG_INFO, "bfd_openr failed");
+ return NULL;
+ }
+
+ if (bfd_check_format(abfd, bfd_archive)) {
+ wpa_printf(MSG_INFO, "bfd_check_format failed");
+ bfd_close(abfd);
+ return NULL;
+ }
+
+ if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
+ wpa_printf(MSG_INFO, "bfd_check_format_matches failed");
+ free(matching);
+ bfd_close(abfd);
+ return NULL;
+ }
+
+ return abfd;
+}
+
+
+static void read_syms(bfd *abfd)
+{
+ long storage, symcount;
+ bfd_boolean dynamic = FALSE;
+
+ if (syms)
+ return;
+
+ if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) {
+ wpa_printf(MSG_INFO, "No symbols");
+ return;
+ }
+
+ storage = bfd_get_symtab_upper_bound(abfd);
+ if (storage == 0) {
+ storage = bfd_get_dynamic_symtab_upper_bound(abfd);
+ dynamic = TRUE;
+ }
+ if (storage < 0) {
+ wpa_printf(MSG_INFO, "Unknown symtab upper bound");
+ return;
+ }
+
+ syms = malloc(storage);
+ if (syms == NULL) {
+ wpa_printf(MSG_INFO, "Failed to allocate memory for symtab "
+ "(%ld bytes)", storage);
+ return;
+ }
+ if (dynamic)
+ symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
+ else
+ symcount = bfd_canonicalize_symtab(abfd, syms);
+ if (symcount < 0) {
+ wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab",
+ dynamic ? "dynamic " : "");
+ free(syms);
+ syms = NULL;
+ return;
+ }
+}
+
+
+struct bfd_data {
+ bfd_vma pc;
+ bfd_boolean found;
+ const char *filename;
+ const char *function;
+ unsigned int line;
+};
+
+
+static void find_addr_sect(bfd *abfd, asection *section, void *obj)
+{
+ struct bfd_data *data = obj;
+ bfd_vma vma;
+ bfd_size_type size;
+
+ if (data->found)
+ return;
+
+ if (!(bfd_get_section_vma(abfd, section)))
+ return;
+
+ vma = bfd_get_section_vma(abfd, section);
+ if (data->pc < vma)
+ return;
+
+ size = bfd_get_section_size(section);
+ if (data->pc >= vma + size)
+ return;
+
+ data->found = bfd_find_nearest_line(abfd, section, syms,
+ data->pc - vma,
+ &data->filename,
+ &data->function,
+ &data->line);
+}
+
+
+static void wpa_trace_bfd_addr(void *pc)
+{
+ bfd *abfd = cached_abfd;
+ struct bfd_data data;
+ const char *name;
+ char *aname = NULL;
+ const char *filename;
+
+ if (abfd == NULL)
+ return;
+
+ data.pc = (bfd_vma) pc;
+ data.found = FALSE;
+ bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+ if (!data.found)
+ return;
+
+ do {
+ if (data.function)
+ aname = bfd_demangle(abfd, data.function,
+ DMGL_ANSI | DMGL_PARAMS);
+ name = aname ? aname : data.function;
+ filename = data.filename;
+ if (filename) {
+ char *end = os_strrchr(filename, '/');
+ int i = 0;
+ while (*filename && *filename == prg_fname[i] &&
+ filename <= end) {
+ filename++;
+ i++;
+ }
+ }
+ wpa_printf(MSG_INFO, " %s() %s:%u",
+ name, filename, data.line);
+ free(aname);
+
+ data.found = bfd_find_inliner_info(abfd, &data.filename,
+ &data.function, &data.line);
+ } while (data.found);
+}
+
+
+static const char * wpa_trace_bfd_addr2func(void *pc)
+{
+ bfd *abfd = cached_abfd;
+ struct bfd_data data;
+
+ if (abfd == NULL)
+ return NULL;
+
+ data.pc = (bfd_vma) pc;
+ data.found = FALSE;
+ bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+ if (!data.found)
+ return NULL;
+
+ return data.function;
+}
+
+
+static void wpa_trace_bfd_init(void)
+{
+ if (!prg_fname) {
+ get_prg_fname();
+ if (!prg_fname)
+ return;
+ }
+
+ if (!cached_abfd) {
+ cached_abfd = open_bfd(prg_fname);
+ if (!cached_abfd) {
+ wpa_printf(MSG_INFO, "Failed to open bfd");
+ return;
+ }
+ }
+
+ read_syms(cached_abfd);
+ if (!syms) {
+ wpa_printf(MSG_INFO, "Failed to read symbols");
+ return;
+ }
+}
+
+
+void wpa_trace_dump_funcname(const char *title, void *pc)
+{
+ wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
+ wpa_trace_bfd_init();
+ wpa_trace_bfd_addr(pc);
+}
+
+#else /* WPA_TRACE_BFD */
+
+#define wpa_trace_bfd_init() do { } while (0)
+#define wpa_trace_bfd_addr(pc) do { } while (0)
+#define wpa_trace_bfd_addr2func(pc) NULL
+
+#endif /* WPA_TRACE_BFD */
+
+void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
+{
+ char **sym;
+ int i;
+ enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
+
+ wpa_trace_bfd_init();
+ wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
+ sym = backtrace_symbols(btrace, btrace_num);
+ state = TRACE_HEAD;
+ for (i = 0; i < btrace_num; i++) {
+ const char *func = wpa_trace_bfd_addr2func(btrace[i]);
+ if (state == TRACE_HEAD && func &&
+ (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
+ os_strcmp(func, "wpa_trace_check_ref") == 0 ||
+ os_strcmp(func, "wpa_trace_show") == 0))
+ continue;
+ if (state == TRACE_TAIL && sym && sym[i] &&
+ os_strstr(sym[i], "__libc_start_main"))
+ break;
+ if (state == TRACE_HEAD)
+ state = TRACE_RELEVANT;
+ if (sym)
+ wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
+ else
+ wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
+ wpa_trace_bfd_addr(btrace[i]);
+ if (state == TRACE_RELEVANT && func &&
+ os_strcmp(func, "main") == 0)
+ state = TRACE_TAIL;
+ }
+ free(sym);
+ wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
+}
+
+
+void wpa_trace_show(const char *title)
+{
+ struct info {
+ WPA_TRACE_INFO
+ } info;
+ wpa_trace_record(&info);
+ wpa_trace_dump(title, &info);
+}
+
+
+void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
+{
+ if (addr == NULL)
+ return;
+ ref->addr = addr;
+ wpa_trace_record(ref);
+ dl_list_add(&active_references, &ref->list);
+}
+
+
+void wpa_trace_check_ref(const void *addr)
+{
+ struct wpa_trace_ref *ref;
+ dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
+ if (addr != ref->addr)
+ continue;
+ wpa_trace_show("Freeing referenced memory");
+ wpa_trace_dump("Reference registration", ref);
+ abort();
+ }
+}
+
+#endif /* WPA_TRACE */
diff --git a/src/utils/trace.h b/src/utils/trace.h
new file mode 100644
index 0000000..22d3de0
--- /dev/null
+++ b/src/utils/trace.h
@@ -0,0 +1,74 @@
+/*
+ * Backtrace debugging
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#define WPA_TRACE_LEN 16
+
+#ifdef WPA_TRACE
+#include <execinfo.h>
+
+#include "list.h"
+
+#define WPA_TRACE_INFO void *btrace[WPA_TRACE_LEN]; int btrace_num;
+
+struct wpa_trace_ref {
+ struct dl_list list;
+ const void *addr;
+ WPA_TRACE_INFO
+};
+#define WPA_TRACE_REF(name) struct wpa_trace_ref wpa_trace_ref_##name
+
+#define wpa_trace_dump(title, ptr) \
+ wpa_trace_dump_func((title), (ptr)->btrace, (ptr)->btrace_num)
+void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num);
+#define wpa_trace_record(ptr) \
+ (ptr)->btrace_num = backtrace((ptr)->btrace, WPA_TRACE_LEN)
+void wpa_trace_show(const char *title);
+#define wpa_trace_add_ref(ptr, name, addr) \
+ wpa_trace_add_ref_func(&(ptr)->wpa_trace_ref_##name, (addr))
+void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr);
+#define wpa_trace_remove_ref(ptr, name, addr) \
+ do { \
+ if ((addr)) \
+ dl_list_del(&(ptr)->wpa_trace_ref_##name.list); \
+ } while (0)
+void wpa_trace_check_ref(const void *addr);
+
+#else /* WPA_TRACE */
+
+#define WPA_TRACE_INFO
+#define WPA_TRACE_REF(n)
+#define wpa_trace_dump(title, ptr) do { } while (0)
+#define wpa_trace_record(ptr) do { } while (0)
+#define wpa_trace_show(title) do { } while (0)
+#define wpa_trace_add_ref(ptr, name, addr) do { } while (0)
+#define wpa_trace_remove_ref(ptr, name, addr) do { } while (0)
+#define wpa_trace_check_ref(addr) do { } while (0)
+
+#endif /* WPA_TRACE */
+
+
+#ifdef WPA_TRACE_BFD
+
+void wpa_trace_dump_funcname(const char *title, void *pc);
+
+#else /* WPA_TRACE_BFD */
+
+#define wpa_trace_dump_funcname(title, pc) do { } while (0)
+
+#endif /* WPA_TRACE_BFD */
+
+#endif /* TRACE_H */
diff --git a/src/utils/uuid.c b/src/utils/uuid.c
new file mode 100644
index 0000000..d8cc267
--- /dev/null
+++ b/src/utils/uuid.c
@@ -0,0 +1,77 @@
+/*
+ * Universally Unique IDentifier (UUID)
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "uuid.h"
+
+int uuid_str2bin(const char *str, u8 *bin)
+{
+ const char *pos;
+ u8 *opos;
+
+ pos = str;
+ opos = bin;
+
+ if (hexstr2bin(pos, opos, 4))
+ return -1;
+ pos += 8;
+ opos += 4;
+
+ if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+ return -1;
+ pos += 4;
+ opos += 2;
+
+ if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+ return -1;
+ pos += 4;
+ opos += 2;
+
+ if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+ return -1;
+ pos += 4;
+ opos += 2;
+
+ if (*pos++ != '-' || hexstr2bin(pos, opos, 6))
+ return -1;
+
+ return 0;
+}
+
+
+int uuid_bin2str(const u8 *bin, char *str, size_t max_len)
+{
+ int len;
+ len = os_snprintf(str, max_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x-%02x%02x%02x%02x%02x%02x",
+ bin[0], bin[1], bin[2], bin[3],
+ bin[4], bin[5], bin[6], bin[7],
+ bin[8], bin[9], bin[10], bin[11],
+ bin[12], bin[13], bin[14], bin[15]);
+ if (len < 0 || (size_t) len >= max_len)
+ return -1;
+ return 0;
+}
+
+
+int is_nil_uuid(const u8 *uuid)
+{
+ int i;
+ for (i = 0; i < UUID_LEN; i++)
+ if (uuid[i])
+ return 0;
+ return 1;
+}
diff --git a/src/utils/uuid.h b/src/utils/uuid.h
new file mode 100644
index 0000000..0759165
--- /dev/null
+++ b/src/utils/uuid.h
@@ -0,0 +1,24 @@
+/*
+ * Universally Unique IDentifier (UUID)
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef UUID_H
+#define UUID_H
+
+#define UUID_LEN 16
+
+int uuid_str2bin(const char *str, u8 *bin);
+int uuid_bin2str(const u8 *bin, char *str, size_t max_len);
+int is_nil_uuid(const u8 *uuid);
+
+#endif /* UUID_H */
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
new file mode 100644
index 0000000..b8c5e2f
--- /dev/null
+++ b/src/utils/wpa_debug.c
@@ -0,0 +1,484 @@
+/*
+ * wpa_supplicant/hostapd / Debug prints
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+
+#ifdef CONFIG_DEBUG_SYSLOG
+#include <syslog.h>
+
+static int wpa_debug_syslog = 0;
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+
+int wpa_debug_level = MSG_INFO;
+int wpa_debug_show_keys = 0;
+int wpa_debug_timestamp = 0;
+
+
+#ifdef CONFIG_ANDROID_LOG
+
+#include <android/log.h>
+
+void android_printf(int level, char *format, ...)
+{
+ if (level >= wpa_debug_level) {
+ va_list ap;
+ if (level == MSG_ERROR)
+ level = ANDROID_LOG_ERROR;
+ else if (level == MSG_WARNING)
+ level = ANDROID_LOG_WARN;
+ else if (level == MSG_INFO)
+ level = ANDROID_LOG_INFO;
+ else
+ level = ANDROID_LOG_DEBUG;
+ va_start(ap, format);
+ __android_log_vprint(level, "wpa_supplicant", format, ap);
+ va_end(ap);
+ }
+}
+
+#else /* 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)
+{
+ struct os_time tv;
+
+ if (!wpa_debug_timestamp)
+ return;
+
+ os_get_time(&tv);
+#ifdef CONFIG_DEBUG_FILE
+ if (out_file) {
+ fprintf(out_file, "%ld.%06u: ", (long) tv.sec,
+ (unsigned int) tv.usec);
+ } else
+#endif /* CONFIG_DEBUG_FILE */
+ printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec);
+}
+
+
+#ifdef CONFIG_DEBUG_SYSLOG
+#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 */
+
+
+/**
+ * wpa_printf - conditional printf
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_printf(int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (level >= wpa_debug_level) {
+#ifdef CONFIG_DEBUG_SYSLOG
+ if (wpa_debug_syslog) {
+ vsyslog(syslog_priority(level), fmt, ap);
+ } else {
+#endif /* CONFIG_DEBUG_SYSLOG */
+ wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+ if (out_file) {
+ vfprintf(out_file, fmt, ap);
+ fprintf(out_file, "\n");
+ } else {
+#endif /* CONFIG_DEBUG_FILE */
+ vprintf(fmt, ap);
+ printf("\n");
+#ifdef CONFIG_DEBUG_FILE
+ }
+#endif /* CONFIG_DEBUG_FILE */
+#ifdef CONFIG_DEBUG_SYSLOG
+ }
+#endif /* CONFIG_DEBUG_SYSLOG */
+ }
+ va_end(ap);
+}
+
+
+static void _wpa_hexdump(int level, const char *title, const u8 *buf,
+ size_t len, int show)
+{
+ size_t i;
+ if (level < wpa_debug_level)
+ return;
+ wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+ if (out_file) {
+ fprintf(out_file, "%s - hexdump(len=%lu):",
+ title, (unsigned long) len);
+ if (buf == NULL) {
+ fprintf(out_file, " [NULL]");
+ } else if (show) {
+ for (i = 0; i < len; i++)
+ fprintf(out_file, " %02x", buf[i]);
+ } else {
+ fprintf(out_file, " [REMOVED]");
+ }
+ fprintf(out_file, "\n");
+ } else {
+#endif /* CONFIG_DEBUG_FILE */
+ printf("%s - hexdump(len=%lu):", title, (unsigned long) len);
+ if (buf == NULL) {
+ printf(" [NULL]");
+ } else if (show) {
+ for (i = 0; i < len; i++)
+ printf(" %02x", buf[i]);
+ } else {
+ printf(" [REMOVED]");
+ }
+ printf("\n");
+#ifdef CONFIG_DEBUG_FILE
+ }
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len)
+{
+ _wpa_hexdump(level, title, buf, len, 1);
+}
+
+
+void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
+{
+ _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys);
+}
+
+
+static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
+ size_t len, int show)
+{
+ size_t i, llen;
+ const u8 *pos = buf;
+ const size_t line_len = 16;
+
+ if (level < wpa_debug_level)
+ return;
+ wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+ if (out_file) {
+ if (!show) {
+ fprintf(out_file,
+ "%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ if (buf == NULL) {
+ fprintf(out_file,
+ "%s - hexdump_ascii(len=%lu): [NULL]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n",
+ title, (unsigned long) len);
+ while (len) {
+ llen = len > line_len ? line_len : len;
+ fprintf(out_file, " ");
+ for (i = 0; i < llen; i++)
+ fprintf(out_file, " %02x", pos[i]);
+ for (i = llen; i < line_len; i++)
+ fprintf(out_file, " ");
+ fprintf(out_file, " ");
+ for (i = 0; i < llen; i++) {
+ if (isprint(pos[i]))
+ fprintf(out_file, "%c", pos[i]);
+ else
+ fprintf(out_file, "_");
+ }
+ for (i = llen; i < line_len; i++)
+ fprintf(out_file, " ");
+ fprintf(out_file, "\n");
+ pos += llen;
+ len -= llen;
+ }
+ } else {
+#endif /* CONFIG_DEBUG_FILE */
+ if (!show) {
+ printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ if (buf == NULL) {
+ printf("%s - hexdump_ascii(len=%lu): [NULL]\n",
+ title, (unsigned long) len);
+ return;
+ }
+ printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len);
+ while (len) {
+ llen = len > line_len ? line_len : len;
+ printf(" ");
+ for (i = 0; i < llen; i++)
+ printf(" %02x", pos[i]);
+ for (i = llen; i < line_len; i++)
+ printf(" ");
+ printf(" ");
+ for (i = 0; i < llen; i++) {
+ if (isprint(pos[i]))
+ printf("%c", pos[i]);
+ else
+ printf("_");
+ }
+ for (i = llen; i < line_len; i++)
+ printf(" ");
+ printf("\n");
+ pos += llen;
+ len -= llen;
+ }
+#ifdef CONFIG_DEBUG_FILE
+ }
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len)
+{
+ _wpa_hexdump_ascii(level, title, buf, len, 1);
+}
+
+
+void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
+ size_t len)
+{
+ _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
+}
+
+
+#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 */
+
+#endif /* CONFIG_ANDROID_LOG */
+
+#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, buf, len);
+ os_free(buf);
+}
+
+
+void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ const int buflen = 2048;
+ int len;
+
+ if (!wpa_msg_cb)
+ return;
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_msg_ctrl: Failed to allocate "
+ "message buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ wpa_msg_cb(ctx, level, buf, len);
+ os_free(buf);
+}
+#endif /* CONFIG_NO_WPA_MSG */
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+static hostapd_logger_cb_func hostapd_logger_cb = NULL;
+
+void hostapd_logger_register_cb(hostapd_logger_cb_func func)
+{
+ hostapd_logger_cb = func;
+}
+
+
+void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ const int buflen = 2048;
+ int len;
+
+ buf = os_malloc(buflen);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "hostapd_logger: Failed to allocate "
+ "message buffer");
+ return;
+ }
+ va_start(ap, fmt);
+ len = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ if (hostapd_logger_cb)
+ hostapd_logger_cb(ctx, addr, module, level, buf, len);
+ else if (addr)
+ wpa_printf(MSG_DEBUG, "hostapd_logger: STA " MACSTR " - %s",
+ MAC2STR(addr), buf);
+ else
+ wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf);
+ os_free(buf);
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
new file mode 100644
index 0000000..ae36afe
--- /dev/null
+++ b/src/utils/wpa_debug.h
@@ -0,0 +1,307 @@
+/*
+ * wpa_supplicant/hostapd / Debug prints
+ * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_DEBUG_H
+#define WPA_DEBUG_H
+
+#include "wpabuf.h"
+
+/* Debugging function - conditional printf and hex dump. Driver wrappers can
+ * use these for debugging purposes. */
+
+enum {
+ MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
+};
+
+#ifdef CONFIG_ANDROID_LOG
+
+#define wpa_debug_print_timestamp() do {} while (0)
+#define wpa_hexdump(...) do {} while (0)
+#define wpa_hexdump_key(...) do {} while (0)
+#define wpa_hexdump_buf(l,t,b) do {} while (0)
+#define wpa_hexdump_buf_key(l,t,b) do {} while (0)
+#define wpa_hexdump_ascii(...) do {} while (0)
+#define wpa_hexdump_ascii_key(...) do {} while (0)
+#define wpa_debug_open_file(...) do {} while (0)
+#define wpa_debug_close_file() do {} while (0)
+#define wpa_dbg(...) do {} while (0)
+
+static inline int wpa_debug_reopen_file(void)
+{
+ return 0;
+}
+
+
+void android_printf(int level, char *format, ...)
+PRINTF_FORMAT(2, 3);
+
+#define wpa_printf android_printf
+
+#else /* CONFIG_ANDROID_LOG */
+
+#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) : 0,
+ 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 */
+
+#endif /* CONFIG_ANDROID_LOG */
+
+
+#ifdef CONFIG_NO_WPA_MSG
+#define wpa_msg(args...) do { } while (0)
+#define wpa_msg_ctrl(args...) do { } while (0)
+#define wpa_msg_register_cb(f) do { } while (0)
+#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);
+
+typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt,
+ size_t len);
+
+/**
+ * wpa_msg_register_cb - Register callback function for wpa_msg() messages
+ * @func: Callback function (%NULL to unregister)
+ */
+void wpa_msg_register_cb(wpa_msg_cb_func func);
+
+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 EAPOL_TEST
+#define WPA_ASSERT(a) \
+ do { \
+ if (!(a)) { \
+ printf("WPA_ASSERT FAILED '" #a "' " \
+ "%s %s:%d\n", \
+ __FUNCTION__, __FILE__, __LINE__); \
+ exit(1); \
+ } \
+ } while (0)
+#else
+#define WPA_ASSERT(a) do { } while (0)
+#endif
+
+#endif /* WPA_DEBUG_H */
diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c
new file mode 100644
index 0000000..eda779e
--- /dev/null
+++ b/src/utils/wpabuf.c
@@ -0,0 +1,304 @@
+/*
+ * Dynamic data buffer
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "trace.h"
+#include "wpabuf.h"
+
+#ifdef WPA_TRACE
+#define WPABUF_MAGIC 0x51a974e3
+
+struct wpabuf_trace {
+ unsigned int magic;
+};
+
+static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf)
+{
+ return (struct wpabuf_trace *)
+ ((const u8 *) buf - sizeof(struct wpabuf_trace));
+}
+#endif /* WPA_TRACE */
+
+
+static void wpabuf_overflow(const struct wpabuf *buf, size_t len)
+{
+#ifdef WPA_TRACE
+ struct wpabuf_trace *trace = wpabuf_get_trace(buf);
+ if (trace->magic != WPABUF_MAGIC) {
+ wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
+ trace->magic);
+ }
+#endif /* WPA_TRACE */
+ wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu",
+ buf, (unsigned long) buf->size, (unsigned long) buf->used,
+ (unsigned long) len);
+ wpa_trace_show("wpabuf overflow");
+ abort();
+}
+
+
+int wpabuf_resize(struct wpabuf **_buf, size_t add_len)
+{
+ struct wpabuf *buf = *_buf;
+#ifdef WPA_TRACE
+ struct wpabuf_trace *trace;
+#endif /* WPA_TRACE */
+
+ if (buf == NULL) {
+ *_buf = wpabuf_alloc(add_len);
+ return *_buf == NULL ? -1 : 0;
+ }
+
+#ifdef WPA_TRACE
+ trace = wpabuf_get_trace(buf);
+ if (trace->magic != WPABUF_MAGIC) {
+ wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
+ trace->magic);
+ wpa_trace_show("wpabuf_resize invalid magic");
+ abort();
+ }
+#endif /* WPA_TRACE */
+
+ if (buf->used + add_len > buf->size) {
+ unsigned char *nbuf;
+ if (buf->ext_data) {
+ nbuf = os_realloc(buf->ext_data, buf->used + add_len);
+ if (nbuf == NULL)
+ return -1;
+ os_memset(nbuf + buf->used, 0, add_len);
+ buf->ext_data = nbuf;
+ } else {
+#ifdef WPA_TRACE
+ nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) +
+ sizeof(struct wpabuf) +
+ buf->used + add_len);
+ if (nbuf == NULL)
+ return -1;
+ trace = (struct wpabuf_trace *) nbuf;
+ buf = (struct wpabuf *) (trace + 1);
+ os_memset(nbuf + sizeof(struct wpabuf_trace) +
+ sizeof(struct wpabuf) + buf->used, 0,
+ add_len);
+#else /* WPA_TRACE */
+ nbuf = os_realloc(buf, sizeof(struct wpabuf) +
+ buf->used + add_len);
+ if (nbuf == NULL)
+ return -1;
+ buf = (struct wpabuf *) nbuf;
+ os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0,
+ add_len);
+#endif /* WPA_TRACE */
+ *_buf = buf;
+ }
+ buf->size = buf->used + add_len;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpabuf_alloc - Allocate a wpabuf of the given size
+ * @len: Length for the allocated buffer
+ * Returns: Buffer to the allocated wpabuf or %NULL on failure
+ */
+struct wpabuf * wpabuf_alloc(size_t len)
+{
+#ifdef WPA_TRACE
+ struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
+ sizeof(struct wpabuf) + len);
+ struct wpabuf *buf;
+ if (trace == NULL)
+ return NULL;
+ trace->magic = WPABUF_MAGIC;
+ buf = (struct wpabuf *) (trace + 1);
+#else /* WPA_TRACE */
+ struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf) + len);
+ if (buf == NULL)
+ return NULL;
+#endif /* WPA_TRACE */
+
+ buf->size = len;
+ return buf;
+}
+
+
+struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len)
+{
+#ifdef WPA_TRACE
+ struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
+ sizeof(struct wpabuf));
+ struct wpabuf *buf;
+ if (trace == NULL)
+ return NULL;
+ trace->magic = WPABUF_MAGIC;
+ buf = (struct wpabuf *) (trace + 1);
+#else /* WPA_TRACE */
+ struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf));
+ if (buf == NULL)
+ return NULL;
+#endif /* WPA_TRACE */
+
+ buf->size = len;
+ buf->used = len;
+ buf->ext_data = data;
+
+ return buf;
+}
+
+
+struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len)
+{
+ struct wpabuf *buf = wpabuf_alloc(len);
+ if (buf)
+ wpabuf_put_data(buf, data, len);
+ return buf;
+}
+
+
+struct wpabuf * wpabuf_dup(const struct wpabuf *src)
+{
+ struct wpabuf *buf = wpabuf_alloc(wpabuf_len(src));
+ if (buf)
+ wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src));
+ return buf;
+}
+
+
+/**
+ * wpabuf_free - Free a wpabuf
+ * @buf: wpabuf buffer
+ */
+void wpabuf_free(struct wpabuf *buf)
+{
+#ifdef WPA_TRACE
+ struct wpabuf_trace *trace;
+ if (buf == NULL)
+ return;
+ trace = wpabuf_get_trace(buf);
+ if (trace->magic != WPABUF_MAGIC) {
+ wpa_printf(MSG_ERROR, "wpabuf_free: invalid magic %x",
+ trace->magic);
+ wpa_trace_show("wpabuf_free magic mismatch");
+ abort();
+ }
+ os_free(buf->ext_data);
+ os_free(trace);
+#else /* WPA_TRACE */
+ if (buf == NULL)
+ return;
+ os_free(buf->ext_data);
+ os_free(buf);
+#endif /* WPA_TRACE */
+}
+
+
+void * wpabuf_put(struct wpabuf *buf, size_t len)
+{
+ void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
+ buf->used += len;
+ if (buf->used > buf->size) {
+ wpabuf_overflow(buf, len);
+ }
+ return tmp;
+}
+
+
+/**
+ * wpabuf_concat - Concatenate two buffers into a newly allocated one
+ * @a: First buffer
+ * @b: Second buffer
+ * Returns: wpabuf with concatenated a + b data or %NULL on failure
+ *
+ * Both buffers a and b will be freed regardless of the return value. Input
+ * buffers can be %NULL which is interpreted as an empty buffer.
+ */
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b)
+{
+ struct wpabuf *n = NULL;
+ size_t len = 0;
+
+ if (b == NULL)
+ return a;
+
+ if (a)
+ len += wpabuf_len(a);
+ if (b)
+ len += wpabuf_len(b);
+
+ n = wpabuf_alloc(len);
+ if (n) {
+ if (a)
+ wpabuf_put_buf(n, a);
+ if (b)
+ wpabuf_put_buf(n, b);
+ }
+
+ wpabuf_free(a);
+ wpabuf_free(b);
+
+ return n;
+}
+
+
+/**
+ * wpabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length
+ * @buf: Buffer to be padded
+ * @len: Length for the padded buffer
+ * Returns: wpabuf padded to len octets or %NULL on failure
+ *
+ * If buf is longer than len octets or of same size, it will be returned as-is.
+ * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed
+ * by the source data. The source buffer will be freed on error, i.e., caller
+ * will only be responsible on freeing the returned buffer. If buf is %NULL,
+ * %NULL will be returned.
+ */
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len)
+{
+ struct wpabuf *ret;
+ size_t blen;
+
+ if (buf == NULL)
+ return NULL;
+
+ blen = wpabuf_len(buf);
+ if (blen >= len)
+ return buf;
+
+ ret = wpabuf_alloc(len);
+ if (ret) {
+ os_memset(wpabuf_put(ret, len - blen), 0, len - blen);
+ wpabuf_put_buf(ret, buf);
+ }
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...)
+{
+ va_list ap;
+ void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
+ int res;
+
+ va_start(ap, fmt);
+ res = vsnprintf(tmp, buf->size - buf->used, fmt, ap);
+ va_end(ap);
+ if (res < 0 || (size_t) res >= buf->size - buf->used)
+ wpabuf_overflow(buf, res);
+ buf->used += res;
+}
diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h
new file mode 100644
index 0000000..cccfcc8
--- /dev/null
+++ b/src/utils/wpabuf.h
@@ -0,0 +1,168 @@
+/*
+ * Dynamic data buffer
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPABUF_H
+#define WPABUF_H
+
+/*
+ * Internal data structure for wpabuf. Please do not touch this directly from
+ * elsewhere. This is only defined in header file to allow inline functions
+ * from this file to access data.
+ */
+struct wpabuf {
+ size_t size; /* total size of the allocated buffer */
+ size_t used; /* length of data in the buffer */
+ u8 *ext_data; /* pointer to external data; NULL if data follows
+ * struct wpabuf */
+ /* optionally followed by the allocated buffer */
+};
+
+
+int wpabuf_resize(struct wpabuf **buf, size_t add_len);
+struct wpabuf * wpabuf_alloc(size_t len);
+struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len);
+struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
+struct wpabuf * wpabuf_dup(const struct wpabuf *src);
+void wpabuf_free(struct wpabuf *buf);
+void * wpabuf_put(struct wpabuf *buf, size_t len);
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3);
+
+
+/**
+ * wpabuf_size - Get the currently allocated size of a wpabuf buffer
+ * @buf: wpabuf buffer
+ * Returns: Currently allocated size of the buffer
+ */
+static inline size_t wpabuf_size(const struct wpabuf *buf)
+{
+ return buf->size;
+}
+
+/**
+ * wpabuf_len - Get the current length of a wpabuf buffer data
+ * @buf: wpabuf buffer
+ * Returns: Currently used length of the buffer
+ */
+static inline size_t wpabuf_len(const struct wpabuf *buf)
+{
+ return buf->used;
+}
+
+/**
+ * wpabuf_tailroom - Get size of available tail room in the end of the buffer
+ * @buf: wpabuf buffer
+ * Returns: Tail room (in bytes) of available space in the end of the buffer
+ */
+static inline size_t wpabuf_tailroom(const struct wpabuf *buf)
+{
+ return buf->size - buf->used;
+}
+
+/**
+ * wpabuf_head - Get pointer to the head of the buffer data
+ * @buf: wpabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline const void * wpabuf_head(const struct wpabuf *buf)
+{
+ if (buf->ext_data)
+ return buf->ext_data;
+ return buf + 1;
+}
+
+static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf)
+{
+ return wpabuf_head(buf);
+}
+
+/**
+ * wpabuf_mhead - Get modifiable pointer to the head of the buffer data
+ * @buf: wpabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline void * wpabuf_mhead(struct wpabuf *buf)
+{
+ if (buf->ext_data)
+ return buf->ext_data;
+ return buf + 1;
+}
+
+static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf)
+{
+ return wpabuf_mhead(buf);
+}
+
+static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data)
+{
+ u8 *pos = wpabuf_put(buf, 1);
+ *pos = data;
+}
+
+static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data)
+{
+ u8 *pos = wpabuf_put(buf, 2);
+ WPA_PUT_LE16(pos, data);
+}
+
+static inline void wpabuf_put_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->ext_data = (u8 *) data;
+ buf->size = buf->used = len;
+}
+
+static inline void wpabuf_put_str(struct wpabuf *dst, const char *str)
+{
+ wpabuf_put_data(dst, str, os_strlen(str));
+}
+
+#endif /* WPABUF_H */
diff --git a/src/wps/Makefile b/src/wps/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/wps/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/wps/http.h b/src/wps/http.h
new file mode 100644
index 0000000..2fee3a8
--- /dev/null
+++ b/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 <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef HTTP_H
+#define HTTP_H
+
+enum http_reply_code {
+ HTTP_OK = 200,
+ HTTP_BAD_REQUEST = 400,
+ UPNP_INVALID_ACTION = 401,
+ UPNP_INVALID_ARGS = 402,
+ HTTP_NOT_FOUND = 404,
+ HTTP_PRECONDITION_FAILED = 412,
+ HTTP_INTERNAL_SERVER_ERROR = 500,
+ HTTP_UNIMPLEMENTED = 501,
+ UPNP_ACTION_FAILED = 501,
+ UPNP_ARG_VALUE_INVALID = 600,
+ UPNP_ARG_VALUE_OUT_OF_RANGE = 601,
+ UPNP_OUT_OF_MEMORY = 603
+};
+
+#endif /* HTTP_H */
diff --git a/src/wps/http_client.c b/src/wps/http_client.c
new file mode 100644
index 0000000..9b53b80
--- /dev/null
+++ b/src/wps/http_client.c
@@ -0,0 +1,374 @@
+/*
+ * http_client - HTTP client
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <fcntl.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+#include "http_client.h"
+
+
+#define HTTP_CLIENT_TIMEOUT_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(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/src/wps/http_client.h b/src/wps/http_client.h
new file mode 100644
index 0000000..924d6ab
--- /dev/null
+++ b/src/wps/http_client.h
@@ -0,0 +1,46 @@
+/*
+ * http_client - HTTP client
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef HTTP_CLIENT_H
+#define HTTP_CLIENT_H
+
+struct http_client;
+
+enum http_client_event {
+ HTTP_CLIENT_FAILED,
+ HTTP_CLIENT_TIMEOUT,
+ HTTP_CLIENT_OK,
+ HTTP_CLIENT_INVALID_REPLY,
+};
+
+char * http_client_url_parse(const char *url, struct sockaddr_in *dst,
+ char **path);
+struct http_client * http_client_addr(struct sockaddr_in *dst,
+ struct wpabuf *req, size_t max_response,
+ void (*cb)(void *ctx,
+ struct http_client *c,
+ enum http_client_event event),
+ void *cb_ctx);
+struct http_client * http_client_url(const char *url,
+ struct wpabuf *req, size_t max_response,
+ void (*cb)(void *ctx,
+ struct http_client *c,
+ enum http_client_event event),
+ void *cb_ctx);
+void http_client_free(struct http_client *c);
+struct wpabuf * http_client_get_body(struct http_client *c);
+char * http_client_get_hdr_line(struct http_client *c, const char *tag);
+char * http_link_update(char *url, const char *base);
+
+#endif /* HTTP_CLIENT_H */
diff --git a/src/wps/http_server.c b/src/wps/http_server.c
new file mode 100644
index 0000000..356f599
--- /dev/null
+++ b/src/wps/http_server.c
@@ -0,0 +1,312 @@
+/*
+ * http_server - HTTP server
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <fcntl.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+#include "http_server.h"
+
+#define HTTP_SERVER_TIMEOUT 30
+#define HTTP_SERVER_MAX_REQ_LEN 8000
+#define HTTP_SERVER_MAX_CONNECTIONS 10
+
+struct http_request {
+ struct http_request *next;
+ struct http_server *srv;
+ int fd;
+ struct sockaddr_in cli;
+ struct httpread *hread;
+};
+
+struct http_server {
+ void (*cb)(void *ctx, struct http_request *req);
+ void *cb_ctx;
+
+ int fd;
+ int port;
+
+ struct http_request *requests;
+ unsigned int request_count;
+};
+
+
+static void http_request_cb(struct httpread *handle, void *cookie,
+ enum httpread_event en)
+{
+ struct http_request *req = cookie;
+ struct http_server *srv = req->srv;
+
+ if (en == HTTPREAD_EVENT_FILE_READY) {
+ wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d received",
+ inet_ntoa(req->cli.sin_addr),
+ ntohs(req->cli.sin_port));
+ srv->cb(srv->cb_ctx, req);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d could not be received "
+ "completely", inet_ntoa(req->cli.sin_addr),
+ ntohs(req->cli.sin_port));
+ http_request_deinit(req);
+}
+
+
+static struct http_request * http_request_init(struct http_server *srv, int fd,
+ struct sockaddr_in *cli)
+{
+ struct http_request *req;
+
+ if (srv->request_count >= HTTP_SERVER_MAX_CONNECTIONS) {
+ wpa_printf(MSG_DEBUG, "HTTP: Too many concurrent requests");
+ return NULL;
+ }
+
+ req = os_zalloc(sizeof(*req));
+ if (req == NULL)
+ return NULL;
+
+ req->srv = srv;
+ req->fd = fd;
+ req->cli = *cli;
+
+ req->hread = httpread_create(req->fd, http_request_cb, req,
+ HTTP_SERVER_MAX_REQ_LEN,
+ HTTP_SERVER_TIMEOUT);
+ if (req->hread == NULL) {
+ http_request_deinit(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+void http_request_deinit(struct http_request *req)
+{
+ struct http_request *r, *p;
+ struct http_server *srv;
+
+ if (req == NULL)
+ return;
+
+ srv = req->srv;
+ p = NULL;
+ r = srv->requests;
+ while (r) {
+ if (r == req) {
+ if (p)
+ p->next = r->next;
+ else
+ srv->requests = r->next;
+ srv->request_count--;
+ break;
+ }
+ p = r;
+ r = r->next;
+ }
+
+ httpread_destroy(req->hread);
+ close(req->fd);
+ os_free(req);
+}
+
+
+static void http_request_free_all(struct http_request *req)
+{
+ struct http_request *prev;
+ while (req) {
+ prev = req;
+ req = req->next;
+ http_request_deinit(prev);
+ }
+}
+
+
+void http_request_send(struct http_request *req, struct wpabuf *resp)
+{
+ int res;
+
+ wpa_printf(MSG_DEBUG, "HTTP: Send %lu byte response to %s:%d",
+ (unsigned long) wpabuf_len(resp),
+ inet_ntoa(req->cli.sin_addr),
+ ntohs(req->cli.sin_port));
+
+ res = send(req->fd, wpabuf_head(resp), wpabuf_len(resp), 0);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "HTTP: Send failed: %s",
+ strerror(errno));
+ } else if ((size_t) res < wpabuf_len(resp)) {
+ wpa_printf(MSG_DEBUG, "HTTP: Sent only %d of %lu bytes",
+ res, (unsigned long) wpabuf_len(resp));
+ /* TODO: add eloop handler for sending rest of the data */
+ }
+
+ wpabuf_free(resp);
+}
+
+
+void http_request_send_and_deinit(struct http_request *req,
+ struct wpabuf *resp)
+{
+ http_request_send(req, resp);
+ http_request_deinit(req);
+}
+
+
+enum httpread_hdr_type http_request_get_type(struct http_request *req)
+{
+ return httpread_hdr_type_get(req->hread);
+}
+
+
+char * http_request_get_uri(struct http_request *req)
+{
+ return httpread_uri_get(req->hread);
+}
+
+
+char * http_request_get_hdr(struct http_request *req)
+{
+ return httpread_hdr_get(req->hread);
+}
+
+
+char * http_request_get_data(struct http_request *req)
+{
+ return httpread_data_get(req->hread);
+}
+
+
+char * http_request_get_hdr_line(struct http_request *req, const char *tag)
+{
+ return httpread_hdr_line_get(req->hread, tag);
+}
+
+
+struct sockaddr_in * http_request_get_cli_addr(struct http_request *req)
+{
+ return &req->cli;
+}
+
+
+static void http_server_cb(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ struct http_server *srv = eloop_ctx;
+ int conn;
+ struct http_request *req;
+
+ conn = accept(srv->fd, (struct sockaddr *) &addr, &addr_len);
+ if (conn < 0) {
+ wpa_printf(MSG_DEBUG, "HTTP: Failed to accept new connection: "
+ "%s", strerror(errno));
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "HTTP: Connection from %s:%d",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+ req = http_request_init(srv, conn, &addr);
+ if (req == NULL) {
+ close(conn);
+ return;
+ }
+
+ req->next = srv->requests;
+ srv->requests = req;
+ srv->request_count++;
+}
+
+
+struct http_server * http_server_init(struct in_addr *addr, int port,
+ void (*cb)(void *ctx,
+ struct http_request *req),
+ void *cb_ctx)
+{
+ struct sockaddr_in sin;
+ struct http_server *srv;
+
+ srv = os_zalloc(sizeof(*srv));
+ if (srv == NULL)
+ return NULL;
+ srv->cb = cb;
+ srv->cb_ctx = cb_ctx;
+
+ srv->fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (srv->fd < 0)
+ goto fail;
+ if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
+ goto fail;
+ if (port < 0)
+ srv->port = 49152;
+ else
+ srv->port = port;
+
+ os_memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = addr->s_addr;
+
+ for (;;) {
+ sin.sin_port = htons(srv->port);
+ if (bind(srv->fd, (struct sockaddr *) &sin, sizeof(sin)) == 0)
+ break;
+ if (errno == EADDRINUSE) {
+ /* search for unused port */
+ if (++srv->port == 65535 || port >= 0)
+ goto fail;
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "HTTP: Failed to bind server port %d: "
+ "%s", srv->port, strerror(errno));
+ goto fail;
+ }
+ if (listen(srv->fd, 10 /* max backlog */) < 0)
+ goto fail;
+ if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
+ goto fail;
+ if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb,
+ srv, NULL))
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "HTTP: Started server on %s:%d",
+ inet_ntoa(*addr), srv->port);
+
+ return srv;
+
+fail:
+ http_server_deinit(srv);
+ return NULL;
+}
+
+
+void http_server_deinit(struct http_server *srv)
+{
+ if (srv == NULL)
+ return;
+ if (srv->fd >= 0) {
+ eloop_unregister_sock(srv->fd, EVENT_TYPE_READ);
+ close(srv->fd);
+ }
+ http_request_free_all(srv->requests);
+
+ os_free(srv);
+}
+
+
+int http_server_get_port(struct http_server *srv)
+{
+ return srv->port;
+}
diff --git a/src/wps/http_server.h b/src/wps/http_server.h
new file mode 100644
index 0000000..219941c
--- /dev/null
+++ b/src/wps/http_server.h
@@ -0,0 +1,39 @@
+/*
+ * http_server - HTTP server
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef HTTP_SERVER_H
+#define HTTP_SERVER_H
+
+struct http_server;
+struct http_request;
+
+void http_request_deinit(struct http_request *req);
+void http_request_send(struct http_request *req, struct wpabuf *resp);
+void http_request_send_and_deinit(struct http_request *req,
+ struct wpabuf *resp);
+enum httpread_hdr_type http_request_get_type(struct http_request *req);
+char * http_request_get_uri(struct http_request *req);
+char * http_request_get_hdr(struct http_request *req);
+char * http_request_get_data(struct http_request *req);
+char * http_request_get_hdr_line(struct http_request *req, const char *tag);
+struct sockaddr_in * http_request_get_cli_addr(struct http_request *req);
+
+struct http_server * http_server_init(struct in_addr *addr, int port,
+ void (*cb)(void *ctx,
+ struct http_request *req),
+ void *cb_ctx);
+void http_server_deinit(struct http_server *srv);
+int http_server_get_port(struct http_server *srv);
+
+#endif /* HTTP_SERVER_H */
diff --git a/src/wps/httpread.c b/src/wps/httpread.c
new file mode 100644
index 0000000..40422e4
--- /dev/null
+++ b/src/wps/httpread.c
@@ -0,0 +1,861 @@
+/*
+ * httpread - Manage reading file(s) from HTTP/TCP socket
+ * Author: Ted Merrill
+ * Copyright 2008 Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * The files are buffered via internal callbacks from eloop, then presented to
+ * an application callback routine when completely read into memory. May also
+ * be used if no file is expected but just to get the header, including HTTP
+ * replies (e.g. HTTP/1.1 200 OK etc.).
+ *
+ * This does not attempt to be an optimally efficient implementation, but does
+ * attempt to be of reasonably small size and memory consumption; assuming that
+ * only small files are to be read. A maximum file size is provided by
+ * application and enforced.
+ *
+ * It is assumed that the application does not expect any of the following:
+ * -- transfer encoding other than chunked
+ * -- trailer fields
+ * It is assumed that, even if the other side requested that the connection be
+ * kept open, that we will close it (thus HTTP messages sent by application
+ * should have the connection closed field); this is allowed by HTTP/1.1 and
+ * simplifies things for us.
+ *
+ * Other limitations:
+ * -- HTTP header may not exceed a hard-coded size.
+ *
+ * Notes:
+ * This code would be massively simpler without some of the new features of
+ * HTTP/1.1, especially chunked data.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+
+
+/* Tunable parameters */
+#define HTTPREAD_READBUF_SIZE 1024 /* read in chunks of this size */
+#define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */
+#define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */
+
+#if 0
+/* httpread_debug -- set this global variable > 0 e.g. from debugger
+ * to enable debugs (larger numbers for more debugs)
+ * Make this a #define of 0 to eliminate the debugging code.
+ */
+int httpread_debug = 99;
+#else
+#define httpread_debug 0 /* eliminates even the debugging code */
+#endif
+
+
+/* control instance -- actual definition (opaque to application)
+ */
+struct httpread {
+ /* information from creation */
+ int sd; /* descriptor of TCP socket to read from */
+ void (*cb)(struct httpread *handle, void *cookie,
+ enum httpread_event e); /* call on event */
+ void *cookie; /* pass to callback */
+ int max_bytes; /* maximum file size else abort it */
+ int timeout_seconds; /* 0 or total duration timeout period */
+
+ /* dynamically used information follows */
+ int sd_registered; /* nonzero if we need to unregister socket */
+ int to_registered; /* nonzero if we need to unregister timeout */
+
+ int got_hdr; /* nonzero when header is finalized */
+ char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */
+ int hdr_nbytes;
+
+ enum httpread_hdr_type hdr_type;
+ int version; /* 1 if we've seen 1.1 */
+ int reply_code; /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
+ int got_content_length; /* true if we know content length for sure */
+ int content_length; /* body length, iff got_content_length */
+ int chunked; /* nonzero for chunked data */
+ char *uri;
+
+ int got_body; /* nonzero when body is finalized */
+ char *body;
+ int body_nbytes;
+ int body_alloc_nbytes; /* amount allocated */
+
+ int got_file; /* here when we are done */
+
+ /* The following apply if data is chunked: */
+ int in_chunk_data; /* 0=in/at header, 1=in the data or tail*/
+ int chunk_start; /* offset in body of chunk hdr or data */
+ int chunk_size; /* data of chunk (not hdr or ending CRLF)*/
+ int in_trailer; /* in header fields after data (chunked only)*/
+ enum trailer_state {
+ trailer_line_begin = 0,
+ trailer_empty_cr, /* empty line + CR */
+ trailer_nonempty,
+ trailer_nonempty_cr,
+ } trailer_state;
+};
+
+
+/* Check words for equality, where words consist of graphical characters
+ * delimited by whitespace
+ * Returns nonzero if "equal" doing case insensitive comparison.
+ */
+static int word_eq(char *s1, char *s2)
+{
+ int c1;
+ int c2;
+ int end1 = 0;
+ int end2 = 0;
+ for (;;) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (isalpha(c1) && isupper(c1))
+ c1 = tolower(c1);
+ if (isalpha(c2) && isupper(c2))
+ c2 = tolower(c2);
+ end1 = !isgraph(c1);
+ end2 = !isgraph(c2);
+ if (end1 || end2 || c1 != c2)
+ break;
+ }
+ return end1 && end2; /* reached end of both words? */
+}
+
+
+/* convert hex to binary
+ * Requires that c have been previously tested true with isxdigit().
+ */
+static int hex_value(int c)
+{
+ if (isdigit(c))
+ return c - '0';
+ if (islower(c))
+ return 10 + c - 'a';
+ return 10 + c - 'A';
+}
+
+
+static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
+
+/* httpread_destroy -- if h is non-NULL, clean up
+ * This must eventually be called by the application following
+ * call of the application's callback and may be called
+ * earlier if desired.
+ */
+void httpread_destroy(struct httpread *h)
+{
+ if (httpread_debug >= 10)
+ wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
+ if (!h)
+ return;
+
+ if (h->to_registered)
+ eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
+ h->to_registered = 0;
+ if (h->sd_registered)
+ eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
+ h->sd_registered = 0;
+ os_free(h->body);
+ os_free(h->uri);
+ os_memset(h, 0, sizeof(*h)); /* aid debugging */
+ h->sd = -1; /* aid debugging */
+ os_free(h);
+}
+
+
+/* httpread_timeout_handler -- called on excessive total duration
+ */
+static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
+{
+ struct httpread *h = user_ctx;
+ wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
+ h->to_registered = 0; /* is self-cancelling */
+ (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
+}
+
+
+/* Analyze options only so far as is needed to correctly obtain the file.
+ * The application can look at the raw header to find other options.
+ */
+static int httpread_hdr_option_analyze(
+ struct httpread *h,
+ char *hbp /* pointer to current line in header buffer */
+ )
+{
+ if (word_eq(hbp, "CONTENT-LENGTH:")) {
+ while (isgraph(*hbp))
+ hbp++;
+ while (*hbp == ' ' || *hbp == '\t')
+ hbp++;
+ if (!isdigit(*hbp))
+ return -1;
+ h->content_length = atol(hbp);
+ h->got_content_length = 1;
+ return 0;
+ }
+ if (word_eq(hbp, "TRANSFER_ENCODING:") ||
+ word_eq(hbp, "TRANSFER-ENCODING:")) {
+ while (isgraph(*hbp))
+ hbp++;
+ while (*hbp == ' ' || *hbp == '\t')
+ hbp++;
+ /* There should (?) be no encodings of interest
+ * other than chunked...
+ */
+ if (word_eq(hbp, "CHUNKED")) {
+ h->chunked = 1;
+ h->in_chunk_data = 0;
+ /* ignore possible ;<parameters> */
+ }
+ return 0;
+ }
+ /* skip anything we don't know, which is a lot */
+ return 0;
+}
+
+
+static int httpread_hdr_analyze(struct httpread *h)
+{
+ char *hbp = h->hdr; /* pointer into h->hdr */
+ int standard_first_line = 1;
+
+ /* First line is special */
+ h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
+ if (!isgraph(*hbp))
+ goto bad;
+ if (os_strncmp(hbp, "HTTP/", 5) == 0) {
+ h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
+ standard_first_line = 0;
+ hbp += 5;
+ if (hbp[0] == '1' && hbp[1] == '.' &&
+ isdigit(hbp[2]) && hbp[2] != '0')
+ h->version = 1;
+ while (isgraph(*hbp))
+ hbp++;
+ while (*hbp == ' ' || *hbp == '\t')
+ hbp++;
+ if (!isdigit(*hbp))
+ goto bad;
+ h->reply_code = atol(hbp);
+ } else if (word_eq(hbp, "GET"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_GET;
+ else if (word_eq(hbp, "HEAD"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
+ else if (word_eq(hbp, "POST"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_POST;
+ else if (word_eq(hbp, "PUT"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
+ else if (word_eq(hbp, "DELETE"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
+ else if (word_eq(hbp, "TRACE"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
+ else if (word_eq(hbp, "CONNECT"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
+ else if (word_eq(hbp, "NOTIFY"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
+ else if (word_eq(hbp, "M-SEARCH"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
+ else if (word_eq(hbp, "M-POST"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
+ else if (word_eq(hbp, "SUBSCRIBE"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
+ else if (word_eq(hbp, "UNSUBSCRIBE"))
+ h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
+ else {
+ }
+
+ if (standard_first_line) {
+ char *rawuri;
+ char *uri;
+ /* skip type */
+ while (isgraph(*hbp))
+ hbp++;
+ while (*hbp == ' ' || *hbp == '\t')
+ hbp++;
+ /* parse uri.
+ * Find length, allocate memory for translated
+ * copy, then translate by changing %<hex><hex>
+ * into represented value.
+ */
+ rawuri = hbp;
+ while (isgraph(*hbp))
+ hbp++;
+ h->uri = os_malloc((hbp - rawuri) + 1);
+ if (h->uri == NULL)
+ goto bad;
+ uri = h->uri;
+ while (rawuri < hbp) {
+ int c = *rawuri;
+ if (c == '%' &&
+ isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
+ *uri++ = (hex_value(rawuri[1]) << 4) |
+ hex_value(rawuri[2]);
+ rawuri += 3;
+ } else {
+ *uri++ = c;
+ rawuri++;
+ }
+ }
+ *uri = 0; /* null terminate */
+ while (isgraph(*hbp))
+ hbp++;
+ while (*hbp == ' ' || *hbp == '\t')
+ hbp++;
+ /* get version */
+ if (0 == strncmp(hbp, "HTTP/", 5)) {
+ hbp += 5;
+ if (hbp[0] == '1' && hbp[1] == '.' &&
+ isdigit(hbp[2]) && hbp[2] != '0')
+ h->version = 1;
+ }
+ }
+ /* skip rest of line */
+ while (*hbp)
+ if (*hbp++ == '\n')
+ break;
+
+ /* Remainder of lines are options, in any order;
+ * or empty line to terminate
+ */
+ for (;;) {
+ /* Empty line to terminate */
+ if (hbp[0] == '\n' ||
+ (hbp[0] == '\r' && hbp[1] == '\n'))
+ break;
+ if (!isgraph(*hbp))
+ goto bad;
+ if (httpread_hdr_option_analyze(h, hbp))
+ goto bad;
+ /* skip line */
+ while (*hbp)
+ if (*hbp++ == '\n')
+ break;
+ }
+
+ /* chunked overrides content-length always */
+ if (h->chunked)
+ h->got_content_length = 0;
+
+ /* For some types, we should not try to read a body
+ * This is in addition to the application determining
+ * that we should not read a body.
+ */
+ switch (h->hdr_type) {
+ case HTTPREAD_HDR_TYPE_REPLY:
+ /* Some codes can have a body and some not.
+ * For now, just assume that any other than 200
+ * do not...
+ */
+ if (h->reply_code != 200)
+ h->max_bytes = 0;
+ break;
+ case HTTPREAD_HDR_TYPE_GET:
+ case HTTPREAD_HDR_TYPE_HEAD:
+ /* in practice it appears that it is assumed
+ * that GETs have a body length of 0... ?
+ */
+ if (h->chunked == 0 && h->got_content_length == 0)
+ h->max_bytes = 0;
+ break;
+ case HTTPREAD_HDR_TYPE_POST:
+ case HTTPREAD_HDR_TYPE_PUT:
+ case HTTPREAD_HDR_TYPE_DELETE:
+ case HTTPREAD_HDR_TYPE_TRACE:
+ case HTTPREAD_HDR_TYPE_CONNECT:
+ case HTTPREAD_HDR_TYPE_NOTIFY:
+ case HTTPREAD_HDR_TYPE_M_SEARCH:
+ case HTTPREAD_HDR_TYPE_M_POST:
+ case HTTPREAD_HDR_TYPE_SUBSCRIBE:
+ case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
+ default:
+ break;
+ }
+
+ return 0;
+
+bad:
+ /* Error */
+ return -1;
+}
+
+
+/* httpread_read_handler -- called when socket ready to read
+ *
+ * Note: any extra data we read past end of transmitted file is ignored;
+ * if we were to support keeping connections open for multiple files then
+ * this would have to be addressed.
+ */
+static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct httpread *h = sock_ctx;
+ int nread;
+ char *rbp; /* pointer into read buffer */
+ char *hbp; /* pointer into header buffer */
+ char *bbp; /* pointer into body buffer */
+ char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */
+
+ if (httpread_debug >= 20)
+ wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
+
+ /* read some at a time, then search for the interal
+ * boundaries between header and data and etc.
+ */
+ nread = read(h->sd, readbuf, sizeof(readbuf));
+ if (nread < 0)
+ goto bad;
+ if (nread == 0) {
+ /* end of transmission... this may be normal
+ * or may be an error... in some cases we can't
+ * tell which so we must assume it is normal then.
+ */
+ if (!h->got_hdr) {
+ /* Must at least have completed header */
+ wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
+ goto bad;
+ }
+ if (h->chunked || h->got_content_length) {
+ /* Premature EOF; e.g. dropped connection */
+ wpa_printf(MSG_DEBUG,
+ "httpread premature eof(%p) %d/%d",
+ h, h->body_nbytes,
+ h->content_length);
+ goto bad;
+ }
+ /* No explicit length, hopefully we have all the data
+ * although dropped connections can cause false
+ * end
+ */
+ if (httpread_debug >= 10)
+ wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
+ h->got_body = 1;
+ goto got_file;
+ }
+ rbp = readbuf;
+
+ /* Header consists of text lines (terminated by both CR and LF)
+ * and an empty line (CR LF only).
+ */
+ if (!h->got_hdr) {
+ hbp = h->hdr + h->hdr_nbytes;
+ /* add to headers until:
+ * -- we run out of data in read buffer
+ * -- or, we run out of header buffer room
+ * -- or, we get double CRLF in headers
+ */
+ for (;;) {
+ if (nread == 0)
+ goto get_more;
+ if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
+ goto bad;
+ }
+ *hbp++ = *rbp++;
+ nread--;
+ h->hdr_nbytes++;
+ if (h->hdr_nbytes >= 4 &&
+ hbp[-1] == '\n' &&
+ hbp[-2] == '\r' &&
+ hbp[-3] == '\n' &&
+ hbp[-4] == '\r' ) {
+ h->got_hdr = 1;
+ *hbp = 0; /* null terminate */
+ break;
+ }
+ }
+ /* here we've just finished reading the header */
+ if (httpread_hdr_analyze(h)) {
+ wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
+ goto bad;
+ }
+ if (h->max_bytes == 0) {
+ if (httpread_debug >= 10)
+ wpa_printf(MSG_DEBUG,
+ "httpread no body hdr end(%p)", h);
+ goto got_file;
+ }
+ if (h->got_content_length && h->content_length == 0) {
+ if (httpread_debug >= 10)
+ wpa_printf(MSG_DEBUG,
+ "httpread zero content length(%p)",
+ h);
+ goto got_file;
+ }
+ }
+
+ /* Certain types of requests never have data and so
+ * must be specially recognized.
+ */
+ if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
+ !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
+ !os_strncasecmp(h->hdr, "HEAD", 4) ||
+ !os_strncasecmp(h->hdr, "GET", 3)) {
+ if (!h->got_body) {
+ if (httpread_debug >= 10)
+ wpa_printf(MSG_DEBUG,
+ "httpread NO BODY for sp. type");
+ }
+ h->got_body = 1;
+ goto got_file;
+ }
+
+ /* Data can be just plain binary data, or if "chunked"
+ * consists of chunks each with a header, ending with
+ * an ending header.
+ */
+ if (nread == 0)
+ goto get_more;
+ if (!h->got_body) {
+ /* Here to get (more of) body */
+ /* ensure we have enough room for worst case for body
+ * plus a null termination character
+ */
+ if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
+ char *new_body;
+ int new_alloc_nbytes;
+
+ if (h->body_nbytes >= h->max_bytes)
+ goto bad;
+ new_alloc_nbytes = h->body_alloc_nbytes +
+ HTTPREAD_BODYBUF_DELTA;
+ /* For content-length case, the first time
+ * through we allocate the whole amount
+ * we need.
+ */
+ if (h->got_content_length &&
+ new_alloc_nbytes < (h->content_length + 1))
+ new_alloc_nbytes = h->content_length + 1;
+ if ((new_body = os_realloc(h->body, new_alloc_nbytes))
+ == NULL)
+ goto bad;
+
+ h->body = new_body;
+ h->body_alloc_nbytes = new_alloc_nbytes;
+ }
+ /* add bytes */
+ bbp = h->body + h->body_nbytes;
+ for (;;) {
+ int ncopy;
+ /* See if we need to stop */
+ if (h->chunked && h->in_chunk_data == 0) {
+ /* in chunk header */
+ char *cbp = h->body + h->chunk_start;
+ if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
+ bbp[-1] == '\n') {
+ /* end of chunk hdr line */
+ /* hdr line consists solely
+ * of a hex numeral and CFLF
+ */
+ if (!isxdigit(*cbp))
+ goto bad;
+ h->chunk_size = strtoul(cbp, NULL, 16);
+ /* throw away chunk header
+ * so we have only real data
+ */
+ h->body_nbytes = h->chunk_start;
+ bbp = cbp;
+ if (h->chunk_size == 0) {
+ /* end of chunking */
+ /* trailer follows */
+ h->in_trailer = 1;
+ if (httpread_debug >= 20)
+ wpa_printf(
+ MSG_DEBUG,
+ "httpread end chunks(%p)", h);
+ break;
+ }
+ h->in_chunk_data = 1;
+ /* leave chunk_start alone */
+ }
+ } else if (h->chunked) {
+ /* in chunk data */
+ if ((h->body_nbytes - h->chunk_start) ==
+ (h->chunk_size + 2)) {
+ /* end of chunk reached,
+ * new chunk starts
+ */
+ /* check chunk ended w/ CRLF
+ * which we'll throw away
+ */
+ if (bbp[-1] == '\n' &&
+ bbp[-2] == '\r') {
+ } else
+ goto bad;
+ h->body_nbytes -= 2;
+ bbp -= 2;
+ h->chunk_start = h->body_nbytes;
+ h->in_chunk_data = 0;
+ h->chunk_size = 0; /* just in case */
+ }
+ } else if (h->got_content_length &&
+ h->body_nbytes >= h->content_length) {
+ h->got_body = 1;
+ if (httpread_debug >= 10)
+ wpa_printf(
+ MSG_DEBUG,
+ "httpread got content(%p)", h);
+ goto got_file;
+ }
+ if (nread <= 0)
+ break;
+ /* Now transfer. Optimize using memcpy where we can. */
+ if (h->chunked && h->in_chunk_data) {
+ /* copy up to remainder of chunk data
+ * plus the required CR+LF at end
+ */
+ ncopy = (h->chunk_start + h->chunk_size + 2) -
+ h->body_nbytes;
+ } else if (h->chunked) {
+ /*in chunk header -- don't optimize */
+ *bbp++ = *rbp++;
+ nread--;
+ h->body_nbytes++;
+ continue;
+ } else if (h->got_content_length) {
+ ncopy = h->content_length - h->body_nbytes;
+ } else {
+ ncopy = nread;
+ }
+ /* Note: should never be 0 */
+ if (ncopy > nread)
+ ncopy = nread;
+ os_memcpy(bbp, rbp, ncopy);
+ bbp += ncopy;
+ h->body_nbytes += ncopy;
+ rbp += ncopy;
+ nread -= ncopy;
+ } /* body copy loop */
+ } /* !got_body */
+ if (h->chunked && h->in_trailer) {
+ /* If "chunked" then there is always a trailer,
+ * consisting of zero or more non-empty lines
+ * ending with CR LF and then an empty line w/ CR LF.
+ * We do NOT support trailers except to skip them --
+ * this is supported (generally) by the http spec.
+ */
+ bbp = h->body + h->body_nbytes;
+ for (;;) {
+ int c;
+ if (nread <= 0)
+ break;
+ c = *rbp++;
+ nread--;
+ switch (h->trailer_state) {
+ case trailer_line_begin:
+ if (c == '\r')
+ h->trailer_state = trailer_empty_cr;
+ else
+ h->trailer_state = trailer_nonempty;
+ break;
+ case trailer_empty_cr:
+ /* end empty line */
+ if (c == '\n') {
+ h->trailer_state = trailer_line_begin;
+ h->in_trailer = 0;
+ if (httpread_debug >= 10)
+ wpa_printf(
+ MSG_DEBUG,
+ "httpread got content(%p)", h);
+ h->got_body = 1;
+ goto got_file;
+ }
+ h->trailer_state = trailer_nonempty;
+ break;
+ case trailer_nonempty:
+ if (c == '\r')
+ h->trailer_state = trailer_nonempty_cr;
+ break;
+ case trailer_nonempty_cr:
+ if (c == '\n')
+ h->trailer_state = trailer_line_begin;
+ else
+ h->trailer_state = trailer_nonempty;
+ break;
+ }
+ }
+ }
+ goto get_more;
+
+bad:
+ /* Error */
+ wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
+ (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
+ return;
+
+get_more:
+ return;
+
+got_file:
+ if (httpread_debug >= 10)
+ wpa_printf(MSG_DEBUG,
+ "httpread got file %d bytes type %d",
+ h->body_nbytes, h->hdr_type);
+ /* Null terminate for convenience of some applications */
+ if (h->body)
+ h->body[h->body_nbytes] = 0; /* null terminate */
+ h->got_file = 1;
+ /* Assume that we do NOT support keeping connection alive,
+ * and just in case somehow we don't get destroyed right away,
+ * unregister now.
+ */
+ if (h->sd_registered)
+ eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
+ h->sd_registered = 0;
+ /* The application can destroy us whenever they feel like...
+ * cancel timeout.
+ */
+ if (h->to_registered)
+ eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
+ h->to_registered = 0;
+ (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
+}
+
+
+/* httpread_create -- start a new reading session making use of eloop.
+ * The new instance will use the socket descriptor for reading (until
+ * it gets a file and not after) but will not close the socket, even
+ * when the instance is destroyed (the application must do that).
+ * Return NULL on error.
+ *
+ * Provided that httpread_create successfully returns a handle,
+ * the callback fnc is called to handle httpread_event events.
+ * The caller should do destroy on any errors or unknown events.
+ *
+ * Pass max_bytes == 0 to not read body at all (required for e.g.
+ * reply to HEAD request).
+ */
+struct httpread * httpread_create(
+ int sd, /* descriptor of TCP socket to read from */
+ void (*cb)(struct httpread *handle, void *cookie,
+ enum httpread_event e), /* call on event */
+ void *cookie, /* pass to callback */
+ int max_bytes, /* maximum body size else abort it */
+ int timeout_seconds /* 0; or total duration timeout period */
+ )
+{
+ struct httpread *h = NULL;
+
+ h = os_zalloc(sizeof(*h));
+ if (h == NULL)
+ goto fail;
+ h->sd = sd;
+ h->cb = cb;
+ h->cookie = cookie;
+ h->max_bytes = max_bytes;
+ h->timeout_seconds = timeout_seconds;
+
+ if (timeout_seconds > 0) {
+ if (eloop_register_timeout(timeout_seconds, 0,
+ httpread_timeout_handler,
+ NULL, h)) {
+ /* No way to recover (from malloc failure) */
+ goto fail;
+ }
+ h->to_registered = 1;
+ }
+ if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
+ NULL, h)) {
+ /* No way to recover (from malloc failure) */
+ goto fail;
+ }
+ h->sd_registered = 1;
+ return h;
+
+fail:
+
+ /* Error */
+ httpread_destroy(h);
+ return NULL;
+}
+
+
+/* httpread_hdr_type_get -- When file is ready, returns header type. */
+enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
+{
+ return h->hdr_type;
+}
+
+
+/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
+ * or possibly NULL (which would be an error).
+ */
+char * httpread_uri_get(struct httpread *h)
+{
+ return h->uri;
+}
+
+
+/* httpread_reply_code_get -- When reply is ready, returns reply code */
+int httpread_reply_code_get(struct httpread *h)
+{
+ return h->reply_code;
+}
+
+
+/* httpread_length_get -- When file is ready, returns file length. */
+int httpread_length_get(struct httpread *h)
+{
+ return h->body_nbytes;
+}
+
+
+/* httpread_data_get -- When file is ready, returns file content
+ * with null byte appened.
+ * Might return NULL in some error condition.
+ */
+void * httpread_data_get(struct httpread *h)
+{
+ return h->body ? h->body : "";
+}
+
+
+/* httpread_hdr_get -- When file is ready, returns header content
+ * with null byte appended.
+ * Might return NULL in some error condition.
+ */
+char * httpread_hdr_get(struct httpread *h)
+{
+ return h->hdr;
+}
+
+
+/* httpread_hdr_line_get -- When file is ready, returns pointer
+ * to line within header content matching the given tag
+ * (after the tag itself and any spaces/tabs).
+ *
+ * The tag should end with a colon for reliable matching.
+ *
+ * If not found, returns NULL;
+ */
+char * httpread_hdr_line_get(struct httpread *h, const char *tag)
+{
+ int tag_len = os_strlen(tag);
+ char *hdr = h->hdr;
+ hdr = os_strchr(hdr, '\n');
+ if (hdr == NULL)
+ return NULL;
+ hdr++;
+ for (;;) {
+ if (!os_strncasecmp(hdr, tag, tag_len)) {
+ hdr += tag_len;
+ while (*hdr == ' ' || *hdr == '\t')
+ hdr++;
+ return hdr;
+ }
+ hdr = os_strchr(hdr, '\n');
+ if (hdr == NULL)
+ return NULL;
+ hdr++;
+ }
+}
diff --git a/src/wps/httpread.h b/src/wps/httpread.h
new file mode 100644
index 0000000..51aa214
--- /dev/null
+++ b/src/wps/httpread.h
@@ -0,0 +1,123 @@
+/*
+ * httpread - Manage reading file(s) from HTTP/TCP socket
+ * Author: Ted Merrill
+ * Copyright 2008 Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef HTTPREAD_H
+#define HTTPREAD_H
+
+/* event types (passed to callback) */
+enum httpread_event {
+ HTTPREAD_EVENT_FILE_READY = 1, /* including reply ready */
+ HTTPREAD_EVENT_TIMEOUT = 2,
+ HTTPREAD_EVENT_ERROR = 3 /* misc. error, esp malloc error */
+};
+
+
+/* header type detected
+ * available to callback via call to httpread_reply_code_get()
+ */
+enum httpread_hdr_type {
+ HTTPREAD_HDR_TYPE_UNKNOWN = 0, /* none of the following */
+ HTTPREAD_HDR_TYPE_REPLY = 1, /* hdr begins w/ HTTP/ */
+ HTTPREAD_HDR_TYPE_GET = 2, /* hdr begins with GET<sp> */
+ HTTPREAD_HDR_TYPE_HEAD = 3, /* hdr begins with HEAD<sp> */
+ HTTPREAD_HDR_TYPE_POST = 4, /* hdr begins with POST<sp> */
+ HTTPREAD_HDR_TYPE_PUT = 5, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_DELETE = 6, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_TRACE = 7, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_CONNECT = 8, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_NOTIFY = 9, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_M_SEARCH = 10, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_M_POST = 11, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_SUBSCRIBE = 12, /* hdr begins with ... */
+ HTTPREAD_HDR_TYPE_UNSUBSCRIBE = 13, /* hdr begins with ... */
+
+ HTTPREAD_N_HDR_TYPES /* keep last */
+};
+
+
+/* control instance -- opaque struct declaration
+ */
+struct httpread;
+
+
+/* httpread_destroy -- if h is non-NULL, clean up
+ * This must eventually be called by the application following
+ * call of the application's callback and may be called
+ * earlier if desired.
+ */
+void httpread_destroy(struct httpread *h);
+
+/* httpread_create -- start a new reading session making use of eloop.
+ * The new instance will use the socket descriptor for reading (until
+ * it gets a file and not after) but will not close the socket, even
+ * when the instance is destroyed (the application must do that).
+ * Return NULL on error.
+ *
+ * Provided that httpread_create successfully returns a handle,
+ * the callback fnc is called to handle httpread_event events.
+ * The caller should do destroy on any errors or unknown events.
+ *
+ * Pass max_bytes == 0 to not read body at all (required for e.g.
+ * reply to HEAD request).
+ */
+struct httpread * httpread_create(
+ int sd, /* descriptor of TCP socket to read from */
+ void (*cb)(struct httpread *handle, void *cookie,
+ enum httpread_event e), /* call on event */
+ void *cookie, /* pass to callback */
+ int max_bytes, /* maximum file size else abort it */
+ int timeout_seconds /* 0; or total duration timeout period */
+ );
+
+/* httpread_hdr_type_get -- When file is ready, returns header type.
+ */
+enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h);
+
+
+/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
+ * or possibly NULL (which would be an error).
+ */
+char *httpread_uri_get(struct httpread *h);
+
+/* httpread_reply_code_get -- When reply is ready, returns reply code */
+int httpread_reply_code_get(struct httpread *h);
+
+
+/* httpread_length_get -- When file is ready, returns file length. */
+int httpread_length_get(struct httpread *h);
+
+/* httpread_data_get -- When file is ready, returns file content
+ * with null byte appened.
+ * Might return NULL in some error condition.
+ */
+void * httpread_data_get(struct httpread *h);
+
+/* httpread_hdr_get -- When file is ready, returns header content
+ * with null byte appended.
+ * Might return NULL in some error condition.
+ */
+char * httpread_hdr_get(struct httpread *h);
+
+/* httpread_hdr_line_get -- When file is ready, returns pointer
+ * to line within header content matching the given tag
+ * (after the tag itself and any spaces/tabs).
+ *
+ * The tag should end with a colon for reliable matching.
+ *
+ * If not found, returns NULL;
+ */
+char * httpread_hdr_line_get(struct httpread *h, const char *tag);
+
+#endif /* HTTPREAD_H */
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
new file mode 100644
index 0000000..9baec7f
--- /dev/null
+++ b/src/wps/ndef.c
@@ -0,0 +1,175 @@
+/*
+ * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup
+ * Reference is "NFCForum-TS-NDEF_1.0 2006-07-24".
+ * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include "common.h"
+#include "wps/wps.h"
+#include "wps/wps_i.h"
+
+#define FLAG_MESSAGE_BEGIN (1 << 7)
+#define FLAG_MESSAGE_END (1 << 6)
+#define FLAG_CHUNK (1 << 5)
+#define FLAG_SHORT_RECORD (1 << 4)
+#define FLAG_ID_LENGTH_PRESENT (1 << 3)
+#define FLAG_TNF_RFC2046 (0x02)
+
+struct ndef_record {
+ u8 *type;
+ u8 *id;
+ u8 *payload;
+ u8 type_length;
+ u8 id_length;
+ u32 payload_length;
+ u32 total_length;
+};
+
+static char wifi_handover_type[] = "application/vnd.wfa.wsc";
+
+static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record)
+{
+ u8 *pos = data + 1;
+
+ if (size < 2)
+ return -1;
+ record->type_length = *pos++;
+ if (data[0] & FLAG_SHORT_RECORD) {
+ if (size < 3)
+ return -1;
+ record->payload_length = *pos++;
+ } else {
+ if (size < 6)
+ return -1;
+ record->payload_length = ntohl(*(u32 *)pos);
+ pos += sizeof(u32);
+ }
+
+ if (data[0] & FLAG_ID_LENGTH_PRESENT) {
+ if ((int) size < pos - data + 1)
+ return -1;
+ record->id_length = *pos++;
+ } else
+ record->id_length = 0;
+
+ record->type = record->type_length == 0 ? NULL : pos;
+ pos += record->type_length;
+
+ record->id = record->id_length == 0 ? NULL : pos;
+ pos += record->id_length;
+
+ record->payload = record->payload_length == 0 ? NULL : pos;
+ pos += record->payload_length;
+
+ record->total_length = pos - data;
+ if (record->total_length > size)
+ return -1;
+ return 0;
+}
+
+
+static struct wpabuf * ndef_parse_records(struct wpabuf *buf,
+ int (*filter)(struct ndef_record *))
+{
+ struct ndef_record record;
+ int len = wpabuf_len(buf);
+ u8 *data = wpabuf_mhead(buf);
+
+ while (len > 0) {
+ if (ndef_parse_record(data, len, &record) < 0) {
+ wpa_printf(MSG_ERROR, "NDEF : Failed to parse");
+ return NULL;
+ }
+ if (filter == NULL || filter(&record))
+ return wpabuf_alloc_copy(record.payload,
+ record.payload_length);
+ data += record.total_length;
+ len -= record.total_length;
+ }
+ wpa_printf(MSG_ERROR, "NDEF : Record not found");
+ return NULL;
+}
+
+
+static struct wpabuf * ndef_build_record(u8 flags, void *type,
+ u8 type_length, void *id,
+ u8 id_length, void *payload,
+ u32 payload_length)
+{
+ struct wpabuf *record;
+ size_t total_len;
+ int short_record;
+ u8 local_flag;
+
+ short_record = payload_length < 256 ? 1 : 0;
+
+ total_len = 2; /* flag + type length */
+ /* payload length */
+ total_len += short_record ? sizeof(u8) : sizeof(u32);
+ if (id_length > 0)
+ total_len += 1;
+ total_len += type_length + id_length + payload_length;
+ record = wpabuf_alloc(total_len);
+ if (record == NULL) {
+ wpa_printf(MSG_ERROR, "NDEF : Failed to allocate "
+ "record for build");
+ return NULL;
+ }
+
+ local_flag = flags;
+ if (id_length > 0)
+ local_flag |= FLAG_ID_LENGTH_PRESENT;
+ if (short_record)
+ local_flag |= FLAG_SHORT_RECORD;
+ wpabuf_put_u8(record, local_flag);
+
+ wpabuf_put_u8(record, type_length);
+
+ if (short_record)
+ wpabuf_put_u8(record, payload_length);
+ else
+ wpabuf_put_be32(record, payload_length);
+
+ if (id_length > 0)
+ wpabuf_put_u8(record, id_length);
+ wpabuf_put_data(record, type, type_length);
+ wpabuf_put_data(record, id, id_length);
+ wpabuf_put_data(record, payload, payload_length);
+ return record;
+}
+
+
+static int wifi_filter(struct ndef_record *record)
+{
+ if (record->type_length != os_strlen(wifi_handover_type))
+ return 0;
+ if (os_memcmp(record->type, wifi_handover_type,
+ os_strlen(wifi_handover_type)) != 0)
+ return 0;
+ return 1;
+}
+
+
+struct wpabuf * ndef_parse_wifi(struct wpabuf *buf)
+{
+ return ndef_parse_records(buf, wifi_filter);
+}
+
+
+struct wpabuf * ndef_build_wifi(struct wpabuf *buf)
+{
+ return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
+ FLAG_TNF_RFC2046, wifi_handover_type,
+ os_strlen(wifi_handover_type), NULL, 0,
+ wpabuf_mhead(buf), wpabuf_len(buf));
+}
diff --git a/src/wps/upnp_xml.c b/src/wps/upnp_xml.c
new file mode 100644
index 0000000..b1b1e2b
--- /dev/null
+++ b/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 <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "http.h"
+#include "upnp_xml.h"
+
+
+/*
+ * XML parsing and formatting
+ *
+ * XML is a markup language based on unicode; usually (and in our case,
+ * always!) based on utf-8. utf-8 uses a variable number of bytes per
+ * character. utf-8 has the advantage that all non-ASCII unicode characters are
+ * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII
+ * characters are single ascii bytes, thus we can use typical text processing.
+ *
+ * (One other interesting thing about utf-8 is that it is possible to look at
+ * any random byte and determine if it is the first byte of a character as
+ * versus a continuation byte).
+ *
+ * The base syntax of XML uses a few ASCII punctionation characters; any
+ * characters that would appear in the payload data are rewritten using
+ * sequences, e.g., &amp; for ampersand(&) and &lt for left angle bracket (<).
+ * Five such escapes total (more can be defined but that does not apply to our
+ * case). Thus we can safely parse for angle brackets etc.
+ *
+ * XML describes tree structures of tagged data, with each element beginning
+ * with an opening tag <label> and ending with a closing tag </label> with
+ * matching label. (There is also a self-closing tag <label/> which is supposed
+ * to be equivalent to <label></label>, i.e., no payload, but we are unlikely
+ * to see it for our purpose).
+ *
+ * Actually the opening tags are a little more complicated because they can
+ * contain "attributes" after the label (delimited by ascii space or tab chars)
+ * of the form attribute_label="value" or attribute_label='value'; as it turns
+ * out we do not have to read any of these attributes, just ignore them.
+ *
+ * Labels are any sequence of chars other than space, tab, right angle bracket
+ * (and ?), but may have an inner structure of <namespace><colon><plain_label>.
+ * As it turns out, we can ignore the namespaces, in fact we can ignore the
+ * entire tree hierarchy, because the plain labels we are looking for will be
+ * unique (not in general, but for this application). We do however have to be
+ * careful to skip over the namespaces.
+ *
+ * In generating XML we have to be more careful, but that is easy because
+ * everything we do is pretty canned. The only real care to take is to escape
+ * any special chars in our payload.
+ */
+
+/**
+ * xml_next_tag - Advance to next tag
+ * @in: Input
+ * @out: OUT: start of tag just after '<'
+ * @out_tagname: OUT: start of name of tag, skipping namespace
+ * @end: OUT: one after tag
+ * Returns: 0 on success, 1 on failure
+ *
+ * A tag has form:
+ * <left angle bracket><...><right angle bracket>
+ * Within the angle brackets, there is an optional leading forward slash (which
+ * makes the tag an ending tag), then an optional leading label (followed by
+ * colon) and then the tag name itself.
+ *
+ * Note that angle brackets present in the original data must have been encoded
+ * as &lt; and &gt; so they will not trouble us.
+ */
+static int xml_next_tag(const char *in, const char **out,
+ const char **out_tagname, const char **end)
+{
+ while (*in && *in != '<')
+ in++;
+ if (*in != '<')
+ return 1;
+ *out = ++in;
+ if (*in == '/')
+ in++;
+ *out_tagname = in; /* maybe */
+ while (isalnum(*in) || *in == '-')
+ in++;
+ if (*in == ':')
+ *out_tagname = ++in;
+ while (*in && *in != '>')
+ in++;
+ if (*in != '>')
+ return 1;
+ *end = ++in;
+ return 0;
+}
+
+
+/* xml_data_encode -- format data for xml file, escaping special characters.
+ *
+ * Note that we assume we are using utf8 both as input and as output!
+ * In utf8, characters may be classed as follows:
+ * 0xxxxxxx(2) -- 1 byte ascii char
+ * 11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80
+ * 110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here)
+ * 1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here)
+ * 11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here)
+ * 10xxxxxx(2) -- extension byte (6 payload bits per byte)
+ * Some values implied by the above are however illegal because they
+ * do not represent unicode chars or are not the shortest encoding.
+ * Actually, we can almost entirely ignore the above and just do
+ * text processing same as for ascii text.
+ *
+ * XML is written with arbitrary unicode characters, except that five
+ * characters have special meaning and so must be escaped where they
+ * appear in payload data... which we do here.
+ */
+void xml_data_encode(struct wpabuf *buf, const char *data, int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ u8 c = ((u8 *) data)[i];
+ if (c == '<') {
+ wpabuf_put_str(buf, "&lt;");
+ continue;
+ }
+ if (c == '>') {
+ wpabuf_put_str(buf, "&gt;");
+ continue;
+ }
+ if (c == '&') {
+ wpabuf_put_str(buf, "&amp;");
+ continue;
+ }
+ if (c == '\'') {
+ wpabuf_put_str(buf, "&apos;");
+ continue;
+ }
+ if (c == '"') {
+ wpabuf_put_str(buf, "&quot;");
+ continue;
+ }
+ /*
+ * We could try to represent control characters using the
+ * sequence: &#x; where x is replaced by a hex numeral, but not
+ * clear why we would do this.
+ */
+ wpabuf_put_u8(buf, c);
+ }
+}
+
+
+/* xml_add_tagged_data -- format tagged data as a new xml line.
+ *
+ * tag must not have any special chars.
+ * data may have special chars, which are escaped.
+ */
+void xml_add_tagged_data(struct wpabuf *buf, const char *tag, const char *data)
+{
+ wpabuf_printf(buf, "<%s>", tag);
+ xml_data_encode(buf, data, os_strlen(data));
+ wpabuf_printf(buf, "</%s>\n", tag);
+}
+
+
+/* A POST body looks something like (per upnp spec):
+ * <?xml version="1.0"?>
+ * <s:Envelope
+ * xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
+ * s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ * <s:Body>
+ * <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v">
+ * <argumentName>in arg value</argumentName>
+ * other in args and their values go here, if any
+ * </u:actionName>
+ * </s:Body>
+ * </s:Envelope>
+ *
+ * where :
+ * s: might be some other namespace name followed by colon
+ * u: might be some other namespace name followed by colon
+ * actionName will be replaced according to action requested
+ * schema following actionName will be WFA scheme instead
+ * argumentName will be actual argument name
+ * (in arg value) will be actual argument value
+ */
+char * xml_get_first_item(const char *doc, const char *item)
+{
+ const char *match = item;
+ int match_len = os_strlen(item);
+ const char *tag, *tagname, *end;
+ char *value;
+
+ /*
+ * This is crude: ignore any possible tag name conflicts and go right
+ * to the first tag of this name. This should be ok for the limited
+ * domain of UPnP messages.
+ */
+ for (;;) {
+ if (xml_next_tag(doc, &tag, &tagname, &end))
+ return NULL;
+ doc = end;
+ if (!os_strncasecmp(tagname, match, match_len) &&
+ *tag != '/' &&
+ (tagname[match_len] == '>' ||
+ !isgraph(tagname[match_len]))) {
+ break;
+ }
+ }
+ end = doc;
+ while (*end && *end != '<')
+ end++;
+ value = os_zalloc(1 + (end - doc));
+ if (value == NULL)
+ return NULL;
+ os_memcpy(value, doc, end - doc);
+ return value;
+}
+
+
+struct wpabuf * xml_get_base64_item(const char *data, const char *name,
+ enum http_reply_code *ret)
+{
+ char *msg;
+ struct wpabuf *buf;
+ unsigned char *decoded;
+ size_t len;
+
+ msg = xml_get_first_item(data, name);
+ if (msg == NULL) {
+ *ret = UPNP_ARG_VALUE_INVALID;
+ return NULL;
+ }
+
+ decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len);
+ os_free(msg);
+ if (decoded == NULL) {
+ *ret = UPNP_OUT_OF_MEMORY;
+ return NULL;
+ }
+
+ buf = wpabuf_alloc_ext_data(decoded, len);
+ if (buf == NULL) {
+ os_free(decoded);
+ *ret = UPNP_OUT_OF_MEMORY;
+ return NULL;
+ }
+ return buf;
+}
diff --git a/src/wps/upnp_xml.h b/src/wps/upnp_xml.h
new file mode 100644
index 0000000..62dbe60
--- /dev/null
+++ b/src/wps/upnp_xml.h
@@ -0,0 +1,23 @@
+/*
+ * UPnP XML helper routines
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef UPNP_XML_H
+#define UPNP_XML_H
+
+#include "http.h"
+
+void xml_data_encode(struct wpabuf *buf, const char *data, int len);
+void xml_add_tagged_data(struct wpabuf *buf, const char *tag,
+ const char *data);
+char * xml_get_first_item(const char *doc, const char *item);
+struct wpabuf * xml_get_base64_item(const char *data, const char *name,
+ enum http_reply_code *ret);
+
+#endif /* UPNP_XML_H */
diff --git a/src/wps/wps.c b/src/wps/wps.c
new file mode 100644
index 0000000..7564d60
--- /dev/null
+++ b/src/wps/wps.c
@@ -0,0 +1,627 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/dh_group5.h"
+#include "common/ieee802_11_defs.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+#ifdef CONFIG_WPS_TESTING
+int wps_version_number = 0x20;
+int wps_testing_dummy_cred = 0;
+#endif /* CONFIG_WPS_TESTING */
+
+
+/**
+ * wps_init - Initialize WPS Registration protocol data
+ * @cfg: WPS configuration
+ * Returns: Pointer to allocated data or %NULL on failure
+ *
+ * This function is used to initialize WPS data for a registration protocol
+ * instance (i.e., each run of registration protocol as a Registrar of
+ * Enrollee. The caller is responsible for freeing this data after the
+ * registration run has been completed by calling wps_deinit().
+ */
+struct wps_data * wps_init(const struct wps_config *cfg)
+{
+ struct wps_data *data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->wps = cfg->wps;
+ data->registrar = cfg->registrar;
+ if (cfg->registrar) {
+ os_memcpy(data->uuid_r, cfg->wps->uuid, WPS_UUID_LEN);
+ } else {
+ os_memcpy(data->mac_addr_e, cfg->wps->dev.mac_addr, ETH_ALEN);
+ os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN);
+ }
+ if (cfg->pin) {
+ data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ?
+ cfg->dev_pw_id : data->wps->oob_dev_pw_id;
+ data->dev_password = os_malloc(cfg->pin_len);
+ if (data->dev_password == NULL) {
+ os_free(data);
+ return NULL;
+ }
+ os_memcpy(data->dev_password, cfg->pin, cfg->pin_len);
+ data->dev_password_len = cfg->pin_len;
+ }
+
+ data->pbc = cfg->pbc;
+ if (cfg->pbc) {
+ /* Use special PIN '00000000' for PBC */
+ data->dev_pw_id = DEV_PW_PUSHBUTTON;
+ os_free(data->dev_password);
+ data->dev_password = os_malloc(8);
+ if (data->dev_password == NULL) {
+ os_free(data);
+ return NULL;
+ }
+ os_memset(data->dev_password, '0', 8);
+ data->dev_password_len = 8;
+ }
+
+ data->state = data->registrar ? RECV_M1 : SEND_M1;
+
+ if (cfg->assoc_wps_ie) {
+ struct wps_parse_attr attr;
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: WPS IE from (Re)AssocReq",
+ cfg->assoc_wps_ie);
+ if (wps_parse_msg(cfg->assoc_wps_ie, &attr) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to parse WPS IE "
+ "from (Re)AssocReq");
+ } else if (attr.request_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Request Type attribute "
+ "in (Re)AssocReq WPS IE");
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS: Request Type (from WPS IE "
+ "in (Re)AssocReq WPS IE): %d",
+ *attr.request_type);
+ data->request_type = *attr.request_type;
+ }
+ }
+
+ if (cfg->new_ap_settings) {
+ data->new_ap_settings =
+ os_malloc(sizeof(*data->new_ap_settings));
+ if (data->new_ap_settings == NULL) {
+ os_free(data);
+ return NULL;
+ }
+ os_memcpy(data->new_ap_settings, cfg->new_ap_settings,
+ sizeof(*data->new_ap_settings));
+ }
+
+ if (cfg->peer_addr)
+ os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN);
+ if (cfg->p2p_dev_addr)
+ os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN);
+
+ data->use_psk_key = cfg->use_psk_key;
+
+ return data;
+}
+
+
+/**
+ * wps_deinit - Deinitialize WPS Registration protocol data
+ * @data: WPS Registration protocol data from wps_init()
+ */
+void wps_deinit(struct wps_data *data)
+{
+ if (data->wps_pin_revealed) {
+ wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and "
+ "negotiation failed");
+ if (data->registrar)
+ wps_registrar_invalidate_pin(data->wps->registrar,
+ data->uuid_e);
+ } else if (data->registrar)
+ wps_registrar_unlock_pin(data->wps->registrar, data->uuid_e);
+
+ wpabuf_free(data->dh_privkey);
+ wpabuf_free(data->dh_pubkey_e);
+ wpabuf_free(data->dh_pubkey_r);
+ wpabuf_free(data->last_msg);
+ os_free(data->dev_password);
+ os_free(data->new_psk);
+ wps_device_data_free(&data->peer_dev);
+ os_free(data->new_ap_settings);
+ dh5_free(data->dh_ctx);
+ os_free(data);
+}
+
+
+/**
+ * wps_process_msg - Process a WPS message
+ * @wps: WPS Registration protocol data from wps_init()
+ * @op_code: Message OP Code
+ * @msg: Message data
+ * Returns: Processing result
+ *
+ * This function is used to process WPS messages with OP Codes WSC_ACK,
+ * WSC_NACK, WSC_MSG, and WSC_Done. The caller (e.g., EAP server/peer) is
+ * responsible for reassembling the messages before calling this function.
+ * Response to this message is built by calling wps_get_msg().
+ */
+enum wps_process_res wps_process_msg(struct wps_data *wps,
+ enum wsc_op_code op_code,
+ const struct wpabuf *msg)
+{
+ if (wps->registrar)
+ return wps_registrar_process_msg(wps, op_code, msg);
+ else
+ return wps_enrollee_process_msg(wps, op_code, msg);
+}
+
+
+/**
+ * wps_get_msg - Build a WPS message
+ * @wps: WPS Registration protocol data from wps_init()
+ * @op_code: Buffer for returning message OP Code
+ * Returns: The generated WPS message or %NULL on failure
+ *
+ * This function is used to build a response to a message processed by calling
+ * wps_process_msg(). The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code)
+{
+ if (wps->registrar)
+ return wps_registrar_get_msg(wps, op_code);
+ else
+ return wps_enrollee_get_msg(wps, op_code);
+}
+
+
+/**
+ * wps_is_selected_pbc_registrar - Check whether WPS IE indicates active PBC
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if PBC Registrar is active, 0 if not
+ */
+int wps_is_selected_pbc_registrar(const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ /*
+ * In theory, this could also verify that attr.sel_reg_config_methods
+ * includes WPS_CONFIG_PUSHBUTTON, but some deployed AP implementations
+ * do not set Selected Registrar Config Methods attribute properly, so
+ * it is safer to just use Device Password ID here.
+ */
+
+ if (wps_parse_msg(msg, &attr) < 0 ||
+ !attr.selected_registrar || *attr.selected_registrar == 0 ||
+ !attr.dev_password_id ||
+ WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
+ return 0;
+
+#ifdef CONFIG_WPS_STRICT
+ if (!attr.sel_reg_config_methods ||
+ !(WPA_GET_BE16(attr.sel_reg_config_methods) &
+ WPS_CONFIG_PUSHBUTTON))
+ return 0;
+#endif /* CONFIG_WPS_STRICT */
+
+ return 1;
+}
+
+
+static int is_selected_pin_registrar(struct wps_parse_attr *attr)
+{
+ /*
+ * In theory, this could also verify that attr.sel_reg_config_methods
+ * includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD,
+ * but some deployed AP implementations do not set Selected Registrar
+ * Config Methods attribute properly, so it is safer to just use
+ * Device Password ID here.
+ */
+
+ if (!attr->selected_registrar || *attr->selected_registrar == 0)
+ return 0;
+
+ if (attr->dev_password_id != NULL &&
+ WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON)
+ return 0;
+
+#ifdef CONFIG_WPS_STRICT
+ if (!attr->sel_reg_config_methods ||
+ !(WPA_GET_BE16(attr->sel_reg_config_methods) &
+ (WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD)))
+ return 0;
+#endif /* CONFIG_WPS_STRICT */
+
+ return 1;
+}
+
+
+/**
+ * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if PIN Registrar is active, 0 if not
+ */
+int wps_is_selected_pin_registrar(const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return 0;
+
+ return is_selected_pin_registrar(&attr);
+}
+
+
+/**
+ * wps_is_addr_authorized - Check whether WPS IE authorizes MAC address
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * @addr: MAC address to search for
+ * @ver1_compat: Whether to use version 1 compatibility mode
+ * Returns: 1 if address is authorized, 0 if not
+ */
+int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
+ int ver1_compat)
+{
+ struct wps_parse_attr attr;
+ unsigned int i;
+ const u8 *pos;
+ const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return 0;
+
+ if (!attr.version2 && ver1_compat) {
+ /*
+ * Version 1.0 AP - AuthorizedMACs not used, so revert back to
+ * old mechanism of using SelectedRegistrar.
+ */
+ return is_selected_pin_registrar(&attr);
+ }
+
+ if (!attr.authorized_macs)
+ return 0;
+
+ pos = attr.authorized_macs;
+ for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) {
+ if (os_memcmp(pos, addr, ETH_ALEN) == 0 ||
+ os_memcmp(pos, bcast, ETH_ALEN) == 0)
+ return 1;
+ pos += ETH_ALEN;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wps_ap_priority_compar - Prioritize WPS IE from two APs
+ * @wps_a: WPS IE contents from Beacon or Probe Response frame
+ * @wps_b: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if wps_b is considered more likely selection for WPS
+ * provisioning, -1 if wps_a is considered more like, or 0 if no preference
+ */
+int wps_ap_priority_compar(const struct wpabuf *wps_a,
+ const struct wpabuf *wps_b)
+{
+ struct wps_parse_attr attr_a, attr_b;
+ int sel_a, sel_b;
+
+ if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0)
+ return 1;
+ if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0)
+ return -1;
+
+ sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0;
+ sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0;
+
+ if (sel_a && !sel_b)
+ return -1;
+ if (!sel_a && sel_b)
+ return 1;
+
+ return 0;
+}
+
+
+/**
+ * wps_get_uuid_e - Get UUID-E from WPS IE
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: Pointer to UUID-E or %NULL if not included
+ *
+ * The returned pointer is to the msg contents and it remains valid only as
+ * long as the msg buffer is valid.
+ */
+const u8 * wps_get_uuid_e(const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return NULL;
+ return attr.uuid_e;
+}
+
+
+/**
+ * wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request
+ * @req_type: Value for Request Type attribute
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type)
+{
+ struct wpabuf *ie;
+ u8 *len;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
+ "Request");
+ ie = wpabuf_alloc(100);
+ if (ie == NULL)
+ return NULL;
+
+ wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(ie, 1);
+ wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+ if (wps_build_version(ie) ||
+ wps_build_req_type(ie, req_type) ||
+ wps_build_wfa_ext(ie, 0, NULL, 0)) {
+ wpabuf_free(ie);
+ return NULL;
+ }
+
+ *len = wpabuf_len(ie) - 2;
+
+ return ie;
+}
+
+
+/**
+ * wps_build_assoc_resp_ie - Build WPS IE for (Re)Association Response
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_assoc_resp_ie(void)
+{
+ struct wpabuf *ie;
+ u8 *len;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
+ "Response");
+ ie = wpabuf_alloc(100);
+ if (ie == NULL)
+ return NULL;
+
+ wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(ie, 1);
+ wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+ if (wps_build_version(ie) ||
+ wps_build_resp_type(ie, WPS_RESP_AP) ||
+ wps_build_wfa_ext(ie, 0, NULL, 0)) {
+ wpabuf_free(ie);
+ return NULL;
+ }
+
+ *len = wpabuf_len(ie) - 2;
+
+ return ie;
+}
+
+
+/**
+ * wps_build_probe_req_ie - Build WPS IE for Probe Request
+ * @pbc: Whether searching for PBC mode APs
+ * @dev: Device attributes
+ * @uuid: Own UUID
+ * @req_type: Value for Request Type attribute
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Requested device types (8 * num_req_dev_types octets) or
+ * %NULL if none
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
+ const u8 *uuid,
+ enum wps_request_type req_type,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types)
+{
+ struct wpabuf *ie;
+ u16 methods = 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request");
+
+ ie = wpabuf_alloc(500);
+ if (ie == NULL)
+ return NULL;
+
+ methods |= WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ /*
+ * TODO: Should figure out whether this device has a physical or
+ * virtual pushbutton.
+ */
+ methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+#endif /* CONFIG_WPS2 */
+
+ /*
+ * TODO: Should figure out whether this Probe Request was triggered
+ * using physical or virtual display. Also, if the device has a PIN on
+ * a label, that should be indicated here.
+ */
+ methods |= WPS_CONFIG_DISPLAY |
+#ifdef CONFIG_WPS2
+ WPS_CONFIG_VIRT_DISPLAY |
+#endif /* CONFIG_WPS2 */
+ WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS_UFD
+ methods |= WPS_CONFIG_USBA;
+#endif /* CONFIG_WPS_UFD */
+#ifdef CONFIG_WPS_NFC
+ methods |= WPS_CONFIG_NFC_INTERFACE;
+#endif /* CONFIG_WPS_NFC */
+
+ if (wps_build_version(ie) ||
+ wps_build_req_type(ie, req_type) ||
+ wps_build_config_methods(ie, methods) ||
+ wps_build_uuid_e(ie, uuid) ||
+ wps_build_primary_dev_type(dev, ie) ||
+ wps_build_rf_bands(dev, ie) ||
+ wps_build_assoc_state(NULL, ie) ||
+ wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
+ wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON :
+ DEV_PW_DEFAULT) ||
+#ifdef CONFIG_WPS2
+ wps_build_manufacturer(dev, ie) ||
+ wps_build_model_name(dev, ie) ||
+ wps_build_model_number(dev, ie) ||
+ wps_build_dev_name(dev, ie) ||
+ wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
+#endif /* CONFIG_WPS2 */
+ wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
+ ||
+ wps_build_secondary_dev_type(dev, ie)
+ ) {
+ wpabuf_free(ie);
+ return NULL;
+ }
+
+#ifndef CONFIG_WPS2
+ if (dev->p2p && wps_build_dev_name(dev, ie)) {
+ wpabuf_free(ie);
+ return NULL;
+ }
+#endif /* CONFIG_WPS2 */
+
+ return wps_ie_encapsulate(ie);
+}
+
+
+void wps_free_pending_msgs(struct upnp_pending_message *msgs)
+{
+ struct upnp_pending_message *p, *prev;
+ p = msgs;
+ while (p) {
+ prev = p;
+ p = p->next;
+ wpabuf_free(prev->msg);
+ os_free(prev);
+ }
+}
+
+
+int wps_attr_text(struct wpabuf *data, char *buf, char *end)
+{
+ struct wps_parse_attr attr;
+ char *pos = buf;
+ int ret;
+
+ if (wps_parse_msg(data, &attr) < 0)
+ return -1;
+
+ if (attr.wps_state) {
+ if (*attr.wps_state == WPS_STATE_NOT_CONFIGURED)
+ ret = os_snprintf(pos, end - pos,
+ "wps_state=unconfigured\n");
+ else if (*attr.wps_state == WPS_STATE_CONFIGURED)
+ ret = os_snprintf(pos, end - pos,
+ "wps_state=configured\n");
+ else
+ ret = 0;
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.ap_setup_locked && *attr.ap_setup_locked) {
+ ret = os_snprintf(pos, end - pos,
+ "wps_ap_setup_locked=1\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.selected_registrar && *attr.selected_registrar) {
+ ret = os_snprintf(pos, end - pos,
+ "wps_selected_registrar=1\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.dev_password_id) {
+ ret = os_snprintf(pos, end - pos,
+ "wps_device_password_id=%u\n",
+ WPA_GET_BE16(attr.dev_password_id));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.sel_reg_config_methods) {
+ ret = os_snprintf(pos, end - pos,
+ "wps_selected_registrar_config_methods="
+ "0x%04x\n",
+ WPA_GET_BE16(attr.sel_reg_config_methods));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.primary_dev_type) {
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ ret = os_snprintf(pos, end - pos,
+ "wps_primary_device_type=%s\n",
+ wps_dev_type_bin2str(attr.primary_dev_type,
+ devtype,
+ sizeof(devtype)));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.dev_name) {
+ char *str = os_malloc(attr.dev_name_len + 1);
+ size_t i;
+ if (str == NULL)
+ return pos - buf;
+ for (i = 0; i < attr.dev_name_len; i++) {
+ if (attr.dev_name[i] < 32)
+ str[i] = '_';
+ else
+ str[i] = attr.dev_name[i];
+ }
+ str[i] = '\0';
+ ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str);
+ os_free(str);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (attr.config_methods) {
+ ret = os_snprintf(pos, end - pos,
+ "wps_config_methods=0x%04x\n",
+ WPA_GET_BE16(attr.config_methods));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
diff --git a/src/wps/wps.h b/src/wps/wps.h
new file mode 100644
index 0000000..918273d
--- /dev/null
+++ b/src/wps/wps.h
@@ -0,0 +1,964 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPS_H
+#define WPS_H
+
+#include "wps_defs.h"
+
+/**
+ * enum wsc_op_code - EAP-WSC OP-Code values
+ */
+enum wsc_op_code {
+ WSC_UPnP = 0 /* No OP Code in UPnP transport */,
+ WSC_Start = 0x01,
+ WSC_ACK = 0x02,
+ WSC_NACK = 0x03,
+ WSC_MSG = 0x04,
+ WSC_Done = 0x05,
+ WSC_FRAG_ACK = 0x06
+};
+
+struct wps_registrar;
+struct upnp_wps_device_sm;
+struct wps_er;
+
+/**
+ * struct wps_credential - WPS Credential
+ * @ssid: SSID
+ * @ssid_len: Length of SSID
+ * @auth_type: Authentication Type (WPS_AUTH_OPEN, .. flags)
+ * @encr_type: Encryption Type (WPS_ENCR_NONE, .. flags)
+ * @key_idx: Key index
+ * @key: Key
+ * @key_len: Key length in octets
+ * @mac_addr: MAC address of the Credential receiver
+ * @cred_attr: Unparsed Credential attribute data (used only in cred_cb());
+ * this may be %NULL, if not used
+ * @cred_attr_len: Length of cred_attr in octets
+ */
+struct wps_credential {
+ u8 ssid[32];
+ size_t ssid_len;
+ u16 auth_type;
+ u16 encr_type;
+ u8 key_idx;
+ u8 key[64];
+ size_t key_len;
+ u8 mac_addr[ETH_ALEN];
+ const u8 *cred_attr;
+ size_t cred_attr_len;
+};
+
+#define WPS_DEV_TYPE_LEN 8
+#define WPS_DEV_TYPE_BUFSIZE 21
+#define WPS_SEC_DEV_TYPE_MAX_LEN 128
+/* maximum number of advertised WPS vendor extension attributes */
+#define MAX_WPS_VENDOR_EXTENSIONS 10
+/* maximum size of WPS Vendor extension attribute */
+#define WPS_MAX_VENDOR_EXT_LEN 1024
+/* maximum number of parsed WPS vendor extension attributes */
+#define MAX_WPS_PARSE_VENDOR_EXT 10
+
+/**
+ * struct wps_device_data - WPS Device Data
+ * @mac_addr: Device MAC address
+ * @device_name: Device Name (0..32 octets encoded in UTF-8)
+ * @manufacturer: Manufacturer (0..64 octets encoded in UTF-8)
+ * @model_name: Model Name (0..32 octets encoded in UTF-8)
+ * @model_number: Model Number (0..32 octets encoded in UTF-8)
+ * @serial_number: Serial Number (0..32 octets encoded in UTF-8)
+ * @pri_dev_type: Primary Device Type
+ * @sec_dev_type: Array of secondary device types
+ * @num_sec_dev_type: Number of secondary device types
+ * @os_version: OS Version
+ * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags)
+ * @p2p: Whether the device is a P2P device
+ */
+struct wps_device_data {
+ u8 mac_addr[ETH_ALEN];
+ char *device_name;
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+ u8 pri_dev_type[WPS_DEV_TYPE_LEN];
+#define WPS_SEC_DEVICE_TYPES 5
+ u8 sec_dev_type[WPS_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
+ u8 num_sec_dev_types;
+ u32 os_version;
+ u8 rf_bands;
+ struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+
+ int p2p;
+};
+
+struct oob_conf_data {
+ enum {
+ OOB_METHOD_UNKNOWN = 0,
+ OOB_METHOD_DEV_PWD_E,
+ OOB_METHOD_DEV_PWD_R,
+ OOB_METHOD_CRED,
+ } oob_method;
+ struct wpabuf *dev_password;
+ struct wpabuf *pubkey_hash;
+};
+
+/**
+ * struct wps_config - WPS configuration for a single registration protocol run
+ */
+struct wps_config {
+ /**
+ * wps - Pointer to long term WPS context
+ */
+ struct wps_context *wps;
+
+ /**
+ * registrar - Whether this end is a Registrar
+ */
+ int registrar;
+
+ /**
+ * pin - Enrollee Device Password (%NULL for Registrar or PBC)
+ */
+ const u8 *pin;
+
+ /**
+ * pin_len - Length on pin in octets
+ */
+ size_t pin_len;
+
+ /**
+ * pbc - Whether this is protocol run uses PBC
+ */
+ int pbc;
+
+ /**
+ * assoc_wps_ie: (Re)AssocReq WPS IE (in AP; %NULL if not AP)
+ */
+ const struct wpabuf *assoc_wps_ie;
+
+ /**
+ * new_ap_settings - New AP settings (%NULL if not used)
+ *
+ * This parameter provides new AP settings when using a wireless
+ * stations as a Registrar to configure the AP. %NULL means that AP
+ * will not be reconfigured, i.e., the station will only learn the
+ * current AP settings by using AP PIN.
+ */
+ const struct wps_credential *new_ap_settings;
+
+ /**
+ * peer_addr: MAC address of the peer in AP; %NULL if not AP
+ */
+ const u8 *peer_addr;
+
+ /**
+ * use_psk_key - Use PSK format key in Credential
+ *
+ * Force PSK format to be used instead of ASCII passphrase when
+ * building Credential for an Enrollee. The PSK value is set in
+ * struct wpa_context::psk.
+ */
+ int use_psk_key;
+
+ /**
+ * dev_pw_id - Device Password ID for Enrollee when PIN is used
+ */
+ u16 dev_pw_id;
+
+ /**
+ * p2p_dev_addr - P2P Device Address from (Re)Association Request
+ *
+ * On AP/GO, this is set to the P2P Device Address of the associating
+ * P2P client if a P2P IE is included in the (Re)Association Request
+ * frame and the P2P Device Address is included. Otherwise, this is set
+ * to %NULL to indicate the station does not have a P2P Device Address.
+ */
+ const u8 *p2p_dev_addr;
+};
+
+struct wps_data * wps_init(const struct wps_config *cfg);
+
+void wps_deinit(struct wps_data *data);
+
+/**
+ * enum wps_process_res - WPS message processing result
+ */
+enum wps_process_res {
+ /**
+ * WPS_DONE - Processing done
+ */
+ WPS_DONE,
+
+ /**
+ * WPS_CONTINUE - Processing continues
+ */
+ WPS_CONTINUE,
+
+ /**
+ * WPS_FAILURE - Processing failed
+ */
+ WPS_FAILURE,
+
+ /**
+ * WPS_PENDING - Processing continues, but waiting for an external
+ * event (e.g., UPnP message from an external Registrar)
+ */
+ WPS_PENDING
+};
+enum wps_process_res wps_process_msg(struct wps_data *wps,
+ enum wsc_op_code op_code,
+ const struct wpabuf *msg);
+
+struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code);
+
+int wps_is_selected_pbc_registrar(const struct wpabuf *msg);
+int wps_is_selected_pin_registrar(const struct wpabuf *msg);
+int wps_ap_priority_compar(const struct wpabuf *wps_a,
+ const struct wpabuf *wps_b);
+int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
+ int ver1_compat);
+const u8 * wps_get_uuid_e(const struct wpabuf *msg);
+
+struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type);
+struct wpabuf * wps_build_assoc_resp_ie(void);
+struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
+ const u8 *uuid,
+ enum wps_request_type req_type,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types);
+
+
+/**
+ * struct wps_registrar_config - WPS Registrar configuration
+ */
+struct wps_registrar_config {
+ /**
+ * new_psk_cb - Callback for new PSK
+ * @ctx: Higher layer context data (cb_ctx)
+ * @mac_addr: MAC address of the Enrollee
+ * @psk: The new PSK
+ * @psk_len: The length of psk in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback is called when a new per-device PSK is provisioned.
+ */
+ int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
+ size_t psk_len);
+
+ /**
+ * set_ie_cb - Callback for WPS IE changes
+ * @ctx: Higher layer context data (cb_ctx)
+ * @beacon_ie: WPS IE for Beacon
+ * @probe_resp_ie: WPS IE for Probe Response
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback is called whenever the WPS IE in Beacon or Probe
+ * Response frames needs to be changed (AP only). Callee is responsible
+ * for freeing the buffers.
+ */
+ int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
+ struct wpabuf *probe_resp_ie);
+
+ /**
+ * pin_needed_cb - Callback for requesting a PIN
+ * @ctx: Higher layer context data (cb_ctx)
+ * @uuid_e: UUID-E of the unknown Enrollee
+ * @dev: Device Data from the unknown Enrollee
+ *
+ * This callback is called whenever an unknown Enrollee requests to use
+ * PIN method and a matching PIN (Device Password) is not found in
+ * Registrar data.
+ */
+ void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+ const struct wps_device_data *dev);
+
+ /**
+ * reg_success_cb - Callback for reporting successful registration
+ * @ctx: Higher layer context data (cb_ctx)
+ * @mac_addr: MAC address of the Enrollee
+ * @uuid_e: UUID-E of the Enrollee
+ *
+ * This callback is called whenever an Enrollee completes registration
+ * successfully.
+ */
+ void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
+ const u8 *uuid_e);
+
+ /**
+ * set_sel_reg_cb - Callback for reporting selected registrar changes
+ * @ctx: Higher layer context data (cb_ctx)
+ * @sel_reg: Whether the Registrar is selected
+ * @dev_passwd_id: Device Password ID to indicate with method or
+ * specific password the Registrar intends to use
+ * @sel_reg_config_methods: Bit field of active config methods
+ *
+ * This callback is called whenever the Selected Registrar state
+ * changes (e.g., a new PIN becomes available or PBC is invoked). This
+ * callback is only used by External Registrar implementation;
+ * set_ie_cb() is used by AP implementation in similar caes, but it
+ * provides the full WPS IE data instead of just the minimal Registrar
+ * state information.
+ */
+ void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods);
+
+ /**
+ * enrollee_seen_cb - Callback for reporting Enrollee based on ProbeReq
+ * @ctx: Higher layer context data (cb_ctx)
+ * @addr: MAC address of the Enrollee
+ * @uuid_e: UUID of the Enrollee
+ * @pri_dev_type: Primary device type
+ * @config_methods: Config Methods
+ * @dev_password_id: Device Password ID
+ * @request_type: Request Type
+ * @dev_name: Device Name (if available)
+ */
+ void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
+ const u8 *pri_dev_type, u16 config_methods,
+ u16 dev_password_id, u8 request_type,
+ const char *dev_name);
+
+ /**
+ * cb_ctx: Higher layer context data for Registrar callbacks
+ */
+ void *cb_ctx;
+
+ /**
+ * skip_cred_build: Do not build credential
+ *
+ * This option can be used to disable internal code that builds
+ * Credential attribute into M8 based on the current network
+ * configuration and Enrollee capabilities. The extra_cred data will
+ * then be used as the Credential(s).
+ */
+ int skip_cred_build;
+
+ /**
+ * extra_cred: Additional Credential attribute(s)
+ *
+ * This optional data (set to %NULL to disable) can be used to add
+ * Credential attribute(s) for other networks into M8. If
+ * skip_cred_build is set, this will also override the automatically
+ * generated Credential attribute.
+ */
+ const u8 *extra_cred;
+
+ /**
+ * extra_cred_len: Length of extra_cred in octets
+ */
+ size_t extra_cred_len;
+
+ /**
+ * disable_auto_conf - Disable auto-configuration on first registration
+ *
+ * By default, the AP that is started in not configured state will
+ * generate a random PSK and move to configured state when the first
+ * registration protocol run is completed successfully. This option can
+ * be used to disable this functionality and leave it up to an external
+ * program to take care of configuration. This requires the extra_cred
+ * to be set with a suitable Credential and skip_cred_build being used.
+ */
+ int disable_auto_conf;
+
+ /**
+ * static_wep_only - Whether the BSS supports only static WEP
+ */
+ int static_wep_only;
+
+ /**
+ * dualband - Whether this is a concurrent dualband AP
+ */
+ int dualband;
+};
+
+
+/**
+ * enum wps_event - WPS event types
+ */
+enum wps_event {
+ /**
+ * WPS_EV_M2D - M2D received (Registrar did not know us)
+ */
+ WPS_EV_M2D,
+
+ /**
+ * WPS_EV_FAIL - Registration failed
+ */
+ WPS_EV_FAIL,
+
+ /**
+ * WPS_EV_SUCCESS - Registration succeeded
+ */
+ WPS_EV_SUCCESS,
+
+ /**
+ * WPS_EV_PWD_AUTH_FAIL - Password authentication failed
+ */
+ WPS_EV_PWD_AUTH_FAIL,
+
+ /**
+ * WPS_EV_PBC_OVERLAP - PBC session overlap detected
+ */
+ WPS_EV_PBC_OVERLAP,
+
+ /**
+ * WPS_EV_PBC_TIMEOUT - PBC walktime expired before protocol run start
+ */
+ WPS_EV_PBC_TIMEOUT,
+
+ /**
+ * WPS_EV_ER_AP_ADD - ER: AP added
+ */
+ WPS_EV_ER_AP_ADD,
+
+ /**
+ * WPS_EV_ER_AP_REMOVE - ER: AP removed
+ */
+ WPS_EV_ER_AP_REMOVE,
+
+ /**
+ * WPS_EV_ER_ENROLLEE_ADD - ER: Enrollee added
+ */
+ WPS_EV_ER_ENROLLEE_ADD,
+
+ /**
+ * WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed
+ */
+ WPS_EV_ER_ENROLLEE_REMOVE,
+
+ /**
+ * WPS_EV_ER_AP_SETTINGS - ER: AP Settings learned
+ */
+ WPS_EV_ER_AP_SETTINGS,
+
+ /**
+ * WPS_EV_ER_SET_SELECTED_REGISTRAR - ER: SetSelectedRegistrar event
+ */
+ WPS_EV_ER_SET_SELECTED_REGISTRAR
+};
+
+/**
+ * union wps_event_data - WPS event data
+ */
+union wps_event_data {
+ /**
+ * struct wps_event_m2d - M2D event data
+ */
+ struct wps_event_m2d {
+ u16 config_methods;
+ const u8 *manufacturer;
+ size_t manufacturer_len;
+ const u8 *model_name;
+ size_t model_name_len;
+ const u8 *model_number;
+ size_t model_number_len;
+ const u8 *serial_number;
+ size_t serial_number_len;
+ const u8 *dev_name;
+ size_t dev_name_len;
+ const u8 *primary_dev_type; /* 8 octets */
+ u16 config_error;
+ u16 dev_password_id;
+ } m2d;
+
+ /**
+ * struct wps_event_fail - Registration failure information
+ * @msg: enum wps_msg_type
+ */
+ struct wps_event_fail {
+ int msg;
+ u16 config_error;
+ u16 error_indication;
+ } fail;
+
+ struct wps_event_pwd_auth_fail {
+ int enrollee;
+ int part;
+ } pwd_auth_fail;
+
+ struct wps_event_er_ap {
+ const u8 *uuid;
+ const u8 *mac_addr;
+ const char *friendly_name;
+ const char *manufacturer;
+ const char *manufacturer_url;
+ const char *model_description;
+ const char *model_name;
+ const char *model_number;
+ const char *model_url;
+ const char *serial_number;
+ const char *upc;
+ const u8 *pri_dev_type;
+ u8 wps_state;
+ } ap;
+
+ struct wps_event_er_enrollee {
+ const u8 *uuid;
+ const u8 *mac_addr;
+ int m1_received;
+ u16 config_methods;
+ u16 dev_passwd_id;
+ const u8 *pri_dev_type;
+ const char *dev_name;
+ const char *manufacturer;
+ const char *model_name;
+ const char *model_number;
+ const char *serial_number;
+ } enrollee;
+
+ struct wps_event_er_ap_settings {
+ const u8 *uuid;
+ const struct wps_credential *cred;
+ } ap_settings;
+
+ struct wps_event_er_set_selected_registrar {
+ const u8 *uuid;
+ int sel_reg;
+ u16 dev_passwd_id;
+ u16 sel_reg_config_methods;
+ enum {
+ WPS_ER_SET_SEL_REG_START,
+ WPS_ER_SET_SEL_REG_DONE,
+ WPS_ER_SET_SEL_REG_FAILED
+ } state;
+ } set_sel_reg;
+};
+
+/**
+ * struct upnp_pending_message - Pending PutWLANResponse messages
+ * @next: Pointer to next pending message or %NULL
+ * @addr: NewWLANEventMAC
+ * @msg: NewMessage
+ * @type: Message Type
+ */
+struct upnp_pending_message {
+ struct upnp_pending_message *next;
+ u8 addr[ETH_ALEN];
+ struct wpabuf *msg;
+ enum wps_msg_type type;
+};
+
+/**
+ * struct wps_context - Long term WPS context data
+ *
+ * This data is stored at the higher layer Authenticator or Supplicant data
+ * structures and it is maintained over multiple registration protocol runs.
+ */
+struct wps_context {
+ /**
+ * ap - Whether the local end is an access point
+ */
+ int ap;
+
+ /**
+ * registrar - Pointer to WPS registrar data from wps_registrar_init()
+ */
+ struct wps_registrar *registrar;
+
+ /**
+ * wps_state - Current WPS state
+ */
+ enum wps_state wps_state;
+
+ /**
+ * ap_setup_locked - Whether AP setup is locked (only used at AP)
+ */
+ int ap_setup_locked;
+
+ /**
+ * uuid - Own UUID
+ */
+ u8 uuid[16];
+
+ /**
+ * ssid - SSID
+ *
+ * This SSID is used by the Registrar to fill in information for
+ * Credentials. In addition, AP uses it when acting as an Enrollee to
+ * notify Registrar of the current configuration.
+ */
+ u8 ssid[32];
+
+ /**
+ * ssid_len - Length of ssid in octets
+ */
+ size_t ssid_len;
+
+ /**
+ * dev - Own WPS device data
+ */
+ struct wps_device_data dev;
+
+ /**
+ * oob_conf - OOB Config data
+ */
+ struct oob_conf_data oob_conf;
+
+ /**
+ * oob_dev_pw_id - OOB Device password id
+ */
+ u16 oob_dev_pw_id;
+
+ /**
+ * dh_ctx - Context data for Diffie-Hellman operation
+ */
+ void *dh_ctx;
+
+ /**
+ * dh_privkey - Diffie-Hellman private key
+ */
+ struct wpabuf *dh_privkey;
+
+ /**
+ * dh_pubkey_oob - Diffie-Hellman public key
+ */
+ struct wpabuf *dh_pubkey;
+
+ /**
+ * config_methods - Enabled configuration methods
+ *
+ * Bit field of WPS_CONFIG_*
+ */
+ u16 config_methods;
+
+ /**
+ * encr_types - Enabled encryption types (bit field of WPS_ENCR_*)
+ */
+ u16 encr_types;
+
+ /**
+ * auth_types - Authentication types (bit field of WPS_AUTH_*)
+ */
+ u16 auth_types;
+
+ /**
+ * network_key - The current Network Key (PSK) or %NULL to generate new
+ *
+ * If %NULL, Registrar will generate per-device PSK. In addition, AP
+ * uses this when acting as an Enrollee to notify Registrar of the
+ * current configuration.
+ *
+ * When using WPA/WPA2-Person, this key can be either the ASCII
+ * passphrase (8..63 characters) or the 32-octet PSK (64 hex
+ * characters). When this is set to the ASCII passphrase, the PSK can
+ * be provided in the psk buffer and used per-Enrollee to control which
+ * key type is included in the Credential (e.g., to reduce calculation
+ * need on low-powered devices by provisioning PSK while still allowing
+ * other devices to get the passphrase).
+ */
+ u8 *network_key;
+
+ /**
+ * network_key_len - Length of network_key in octets
+ */
+ size_t network_key_len;
+
+ /**
+ * psk - The current network PSK
+ *
+ * This optional value can be used to provide the current PSK if
+ * network_key is set to the ASCII passphrase.
+ */
+ u8 psk[32];
+
+ /**
+ * psk_set - Whether psk value is set
+ */
+ int psk_set;
+
+ /**
+ * ap_settings - AP Settings override for M7 (only used at AP)
+ *
+ * If %NULL, AP Settings attributes will be generated based on the
+ * current network configuration.
+ */
+ u8 *ap_settings;
+
+ /**
+ * ap_settings_len - Length of ap_settings in octets
+ */
+ size_t ap_settings_len;
+
+ /**
+ * friendly_name - Friendly Name (required for UPnP)
+ */
+ char *friendly_name;
+
+ /**
+ * manufacturer_url - Manufacturer URL (optional for UPnP)
+ */
+ char *manufacturer_url;
+
+ /**
+ * model_description - Model Description (recommended for UPnP)
+ */
+ char *model_description;
+
+ /**
+ * model_url - Model URL (optional for UPnP)
+ */
+ char *model_url;
+
+ /**
+ * upc - Universal Product Code (optional for UPnP)
+ */
+ char *upc;
+
+ /**
+ * cred_cb - Callback to notify that new Credentials were received
+ * @ctx: Higher layer context data (cb_ctx)
+ * @cred: The received Credential
+ * Return: 0 on success, -1 on failure
+ */
+ int (*cred_cb)(void *ctx, const struct wps_credential *cred);
+
+ /**
+ * event_cb - Event callback (state information about progress)
+ * @ctx: Higher layer context data (cb_ctx)
+ * @event: Event type
+ * @data: Event data
+ */
+ void (*event_cb)(void *ctx, enum wps_event event,
+ union wps_event_data *data);
+
+ /**
+ * cb_ctx: Higher layer context data for callbacks
+ */
+ void *cb_ctx;
+
+ struct upnp_wps_device_sm *wps_upnp;
+
+ /* Pending messages from UPnP PutWLANResponse */
+ struct upnp_pending_message *upnp_msgs;
+};
+
+struct oob_device_data {
+ char *device_name;
+ char *device_path;
+ void * (*init_func)(struct wps_context *, struct oob_device_data *,
+ int);
+ struct wpabuf * (*read_func)(void *);
+ int (*write_func)(void *, struct wpabuf *);
+ void (*deinit_func)(void *);
+};
+
+struct oob_nfc_device_data {
+ int (*init_func)(char *);
+ void * (*read_func)(size_t *);
+ int (*write_func)(void *, size_t);
+ void (*deinit_func)(void);
+};
+
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+ const struct wps_registrar_config *cfg);
+void wps_registrar_deinit(struct wps_registrar *reg);
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
+ const u8 *uuid, const u8 *pin, size_t pin_len,
+ int timeout);
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_wps_cancel(struct wps_registrar *reg);
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+ const u8 *p2p_dev_addr);
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+ const struct wpabuf *wps_data,
+ int p2p_wildcard);
+int wps_registrar_update_ie(struct wps_registrar *reg);
+int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
+ char *buf, size_t buflen);
+int wps_registrar_config_ap(struct wps_registrar *reg,
+ struct wps_credential *cred);
+
+unsigned int wps_pin_checksum(unsigned int pin);
+unsigned int wps_pin_valid(unsigned int pin);
+unsigned int wps_generate_pin(void);
+void wps_free_pending_msgs(struct upnp_pending_message *msgs);
+
+struct oob_device_data * wps_get_oob_device(char *device_type);
+struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name);
+int wps_get_oob_method(char *method);
+int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
+ int registrar);
+int wps_attr_text(struct wpabuf *data, char *buf, char *end);
+
+struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname,
+ const char *filter);
+void wps_er_refresh(struct wps_er *er);
+void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx);
+void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods);
+int wps_er_pbc(struct wps_er *er, const u8 *uuid);
+int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
+ size_t pin_len);
+int wps_er_set_config(struct wps_er *er, const u8 *uuid,
+ const struct wps_credential *cred);
+int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
+ size_t pin_len, const struct wps_credential *cred);
+
+int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
+char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
+ size_t buf_len);
+void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
+u16 wps_config_methods_str2bin(const char *str);
+
+#ifdef CONFIG_WPS_STRICT
+int wps_validate_beacon(const struct wpabuf *wps_ie);
+int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
+ const u8 *addr);
+int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr);
+int wps_validate_assoc_req(const struct wpabuf *wps_ie);
+int wps_validate_assoc_resp(const struct wpabuf *wps_ie);
+int wps_validate_m1(const struct wpabuf *tlvs);
+int wps_validate_m2(const struct wpabuf *tlvs);
+int wps_validate_m2d(const struct wpabuf *tlvs);
+int wps_validate_m3(const struct wpabuf *tlvs);
+int wps_validate_m4(const struct wpabuf *tlvs);
+int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m5(const struct wpabuf *tlvs);
+int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m6(const struct wpabuf *tlvs);
+int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m7(const struct wpabuf *tlvs);
+int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2);
+int wps_validate_m8(const struct wpabuf *tlvs);
+int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2);
+int wps_validate_wsc_ack(const struct wpabuf *tlvs);
+int wps_validate_wsc_nack(const struct wpabuf *tlvs);
+int wps_validate_wsc_done(const struct wpabuf *tlvs);
+int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs);
+#else /* CONFIG_WPS_STRICT */
+static inline int wps_validate_beacon(const struct wpabuf *wps_ie){
+ return 0;
+}
+
+static inline int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie,
+ int probe, const u8 *addr)
+{
+ return 0;
+}
+
+static inline int wps_validate_probe_req(const struct wpabuf *wps_ie,
+ const u8 *addr)
+{
+ return 0;
+}
+
+static inline int wps_validate_assoc_req(const struct wpabuf *wps_ie)
+{
+ return 0;
+}
+
+static inline int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
+{
+ return 0;
+}
+
+static inline int wps_validate_m1(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m2(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m2d(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m3(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m4(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m5(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m6(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m7(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap,
+ int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m8(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap,
+ int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_wsc_ack(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_wsc_nack(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_wsc_done(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_upnp_set_selected_registrar(
+ const struct wpabuf *tlvs)
+{
+ return 0;
+}
+#endif /* CONFIG_WPS_STRICT */
+
+#endif /* WPS_H */
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
new file mode 100644
index 0000000..d2ca31a
--- /dev/null
+++ b/src/wps/wps_attr_build.c
@@ -0,0 +1,422 @@
+/*
+ * Wi-Fi Protected Setup - attribute building
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_group5.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "wps_i.h"
+
+
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
+{
+ struct wpabuf *pubkey;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Public Key");
+ wpabuf_free(wps->dh_privkey);
+ if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey) {
+ wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys");
+ wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey);
+ wps->dh_ctx = wps->wps->dh_ctx;
+ wps->wps->dh_ctx = NULL;
+ pubkey = wpabuf_dup(wps->wps->dh_pubkey);
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys");
+ wps->dh_privkey = NULL;
+ dh5_free(wps->dh_ctx);
+ wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey);
+ pubkey = wpabuf_zeropad(pubkey, 192);
+ }
+ if (wps->dh_ctx == NULL || wps->dh_privkey == NULL || pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
+ "Diffie-Hellman handshake");
+ wpabuf_free(pubkey);
+ return -1;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey);
+
+ wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
+ wpabuf_put_be16(msg, wpabuf_len(pubkey));
+ wpabuf_put_buf(msg, pubkey);
+
+ if (wps->registrar) {
+ wpabuf_free(wps->dh_pubkey_r);
+ wps->dh_pubkey_r = pubkey;
+ } else {
+ wpabuf_free(wps->dh_pubkey_e);
+ wps->dh_pubkey_e = pubkey;
+ }
+
+ return 0;
+}
+
+
+int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Request Type");
+ wpabuf_put_be16(msg, ATTR_REQUEST_TYPE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, type);
+ return 0;
+}
+
+
+int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Response Type (%d)", type);
+ wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, type);
+ return 0;
+}
+
+
+int wps_build_config_methods(struct wpabuf *msg, u16 methods)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
+ wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, methods);
+ return 0;
+}
+
+
+int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * UUID-E");
+ wpabuf_put_be16(msg, ATTR_UUID_E);
+ wpabuf_put_be16(msg, WPS_UUID_LEN);
+ wpabuf_put_data(msg, uuid, WPS_UUID_LEN);
+ return 0;
+}
+
+
+int wps_build_dev_password_id(struct wpabuf *msg, u16 id)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id);
+ wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, id);
+ return 0;
+}
+
+
+int wps_build_config_error(struct wpabuf *msg, u16 err)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Configuration Error (%d)", err);
+ wpabuf_put_be16(msg, ATTR_CONFIG_ERROR);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, err);
+ return 0;
+}
+
+
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+
+ if (wps->last_msg == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
+ "building authenticator");
+ return -1;
+ }
+
+ /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+ * (M_curr* is M_curr without the Authenticator attribute)
+ */
+ addr[0] = wpabuf_head(wps->last_msg);
+ len[0] = wpabuf_len(wps->last_msg);
+ addr[1] = wpabuf_head(msg);
+ len[1] = wpabuf_len(msg);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+
+ wpa_printf(MSG_DEBUG, "WPS: * Authenticator");
+ wpabuf_put_be16(msg, ATTR_AUTHENTICATOR);
+ wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN);
+ wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN);
+
+ return 0;
+}
+
+
+int wps_build_version(struct wpabuf *msg)
+{
+ /*
+ * Note: This attribute is deprecated and set to hardcoded 0x10 for
+ * backwards compatibility reasons. The real version negotiation is
+ * done with Version2.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: * Version (hardcoded 0x10)");
+ wpabuf_put_be16(msg, ATTR_VERSION);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 0x10);
+ return 0;
+}
+
+
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+ const u8 *auth_macs, size_t auth_macs_count)
+{
+#ifdef CONFIG_WPS2
+ u8 *len;
+
+ wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+ len = wpabuf_put(msg, 2); /* to be filled */
+ wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA);
+
+ wpa_printf(MSG_DEBUG, "WPS: * Version2 (0x%x)", WPS_VERSION);
+ wpabuf_put_u8(msg, WFA_ELEM_VERSION2);
+ wpabuf_put_u8(msg, 1);
+ wpabuf_put_u8(msg, WPS_VERSION);
+
+ if (req_to_enroll) {
+ wpa_printf(MSG_DEBUG, "WPS: * Request to Enroll (1)");
+ wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL);
+ wpabuf_put_u8(msg, 1);
+ wpabuf_put_u8(msg, 1);
+ }
+
+ if (auth_macs && auth_macs_count) {
+ size_t i;
+ wpa_printf(MSG_DEBUG, "WPS: * AuthorizedMACs (count=%d)",
+ (int) auth_macs_count);
+ wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS);
+ wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN);
+ wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN);
+ for (i = 0; i < auth_macs_count; i++)
+ wpa_printf(MSG_DEBUG, "WPS: AuthorizedMAC: " MACSTR,
+ MAC2STR(&auth_macs[i * ETH_ALEN]));
+ }
+
+ WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_WPS_TESTING
+ if (WPS_VERSION > 0x20) {
+ wpa_printf(MSG_DEBUG, "WPS: * Extensibility Testing - extra "
+ "attribute");
+ wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 42);
+ }
+#endif /* CONFIG_WPS_TESTING */
+ return 0;
+}
+
+
+int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Message Type (%d)", msg_type);
+ wpabuf_put_be16(msg, ATTR_MSG_TYPE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, msg_type);
+ return 0;
+}
+
+
+int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Enrollee Nonce");
+ wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+ wpabuf_put_be16(msg, WPS_NONCE_LEN);
+ wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN);
+ return 0;
+}
+
+
+int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Registrar Nonce");
+ wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
+ wpabuf_put_be16(msg, WPS_NONCE_LEN);
+ wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN);
+ return 0;
+}
+
+
+int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+ u16 auth_types = WPS_AUTH_TYPES;
+#ifdef CONFIG_WPS2
+ auth_types &= ~WPS_AUTH_SHARED;
+#endif /* CONFIG_WPS2 */
+ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags");
+ wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, auth_types);
+ return 0;
+}
+
+
+int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+ u16 encr_types = WPS_ENCR_TYPES;
+#ifdef CONFIG_WPS2
+ encr_types &= ~WPS_ENCR_WEP;
+#endif /* CONFIG_WPS2 */
+ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags");
+ wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, encr_types);
+ return 0;
+}
+
+
+int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Connection Type Flags");
+ wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, WPS_CONN_ESS);
+ return 0;
+}
+
+
+int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Association State");
+ wpabuf_put_be16(msg, ATTR_ASSOC_STATE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC);
+ return 0;
+}
+
+
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 hash[SHA256_MAC_LEN];
+
+ wpa_printf(MSG_DEBUG, "WPS: * Key Wrap Authenticator");
+ hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
+ wpabuf_len(msg), hash);
+
+ wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH);
+ wpabuf_put_be16(msg, WPS_KWA_LEN);
+ wpabuf_put_data(msg, hash, WPS_KWA_LEN);
+ return 0;
+}
+
+
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+ struct wpabuf *plain)
+{
+ size_t pad_len;
+ const size_t block_size = 16;
+ u8 *iv, *data;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Encrypted Settings");
+
+ /* PKCS#5 v2.0 pad */
+ pad_len = block_size - wpabuf_len(plain) % block_size;
+ os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len);
+
+ wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS);
+ wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
+
+ iv = wpabuf_put(msg, block_size);
+ if (random_get_bytes(iv, block_size) < 0)
+ return -1;
+
+ data = wpabuf_put(msg, 0);
+ wpabuf_put_buf(msg, plain);
+ if (aes_128_cbc_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain)))
+ return -1;
+
+ return 0;
+}
+
+
+#ifdef CONFIG_WPS_OOB
+int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
+{
+ size_t hash_len;
+ const u8 *addr[1];
+ u8 pubkey_hash[WPS_HASH_LEN];
+ u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN];
+
+ wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password");
+
+ addr[0] = wpabuf_head(wps->dh_pubkey);
+ hash_len = wpabuf_len(wps->dh_pubkey);
+ sha256_vector(1, addr, &hash_len, pubkey_hash);
+
+ if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) {
+ wpa_printf(MSG_ERROR, "WPS: device password id "
+ "generation error");
+ return -1;
+ }
+ wps->oob_dev_pw_id |= 0x0010;
+
+ if (random_get_bytes(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) <
+ 0) {
+ wpa_printf(MSG_ERROR, "WPS: OOB device password "
+ "generation error");
+ return -1;
+ }
+
+ wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
+ wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
+ wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpabuf_put_be16(msg, wps->oob_dev_pw_id);
+ wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
+
+ wpa_snprintf_hex_uppercase(
+ wpabuf_put(wps->oob_conf.dev_password,
+ wpabuf_size(wps->oob_conf.dev_password)),
+ wpabuf_size(wps->oob_conf.dev_password),
+ dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
+
+ return 0;
+}
+#endif /* CONFIG_WPS_OOB */
+
+
+/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
+{
+ struct wpabuf *ie;
+ const u8 *pos, *end;
+
+ ie = wpabuf_alloc(wpabuf_len(data) + 100);
+ if (ie == NULL) {
+ wpabuf_free(data);
+ return NULL;
+ }
+
+ pos = wpabuf_head(data);
+ end = pos + wpabuf_len(data);
+
+ while (end > pos) {
+ size_t frag_len = end - pos;
+ if (frag_len > 251)
+ frag_len = 251;
+ wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(ie, 4 + frag_len);
+ wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+ wpabuf_put_data(ie, pos, frag_len);
+ pos += frag_len;
+ }
+
+ wpabuf_free(data);
+
+ return ie;
+}
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
new file mode 100644
index 0000000..7b462d3
--- /dev/null
+++ b/src/wps/wps_attr_parse.c
@@ -0,0 +1,630 @@
+/*
+ * Wi-Fi Protected Setup - attribute parsing
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wps_i.h"
+
+#ifndef CONFIG_WPS_STRICT
+#define WPS_WORKAROUNDS
+#endif /* CONFIG_WPS_STRICT */
+
+
+static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
+ u8 id, u8 len, const u8 *pos)
+{
+ wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
+ id, len);
+ switch (id) {
+ case WFA_ELEM_VERSION2:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
+ "%u", len);
+ return -1;
+ }
+ attr->version2 = pos;
+ break;
+ case WFA_ELEM_AUTHORIZEDMACS:
+ attr->authorized_macs = pos;
+ attr->authorized_macs_len = len;
+ break;
+ case WFA_ELEM_NETWORK_KEY_SHAREABLE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
+ "Shareable length %u", len);
+ return -1;
+ }
+ attr->network_key_shareable = pos;
+ break;
+ case WFA_ELEM_REQUEST_TO_ENROLL:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
+ "length %u", len);
+ return -1;
+ }
+ attr->request_to_enroll = pos;
+ break;
+ case WFA_ELEM_SETTINGS_DELAY_TIME:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
+ "Time length %u", len);
+ return -1;
+ }
+ attr->settings_delay_time = pos;
+ break;
+ default:
+ wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
+ "Extension subelement %u", id);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
+ u16 len)
+{
+ const u8 *end = pos + len;
+ u8 id, elen;
+
+ while (pos + 2 < end) {
+ id = *pos++;
+ elen = *pos++;
+ if (pos + elen > end)
+ break;
+ if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
+ return -1;
+ pos += elen;
+ }
+
+ return 0;
+}
+
+
+static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
+ u16 len)
+{
+ u32 vendor_id;
+
+ if (len < 3) {
+ wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
+ return 0;
+ }
+
+ vendor_id = WPA_GET_BE24(pos);
+ switch (vendor_id) {
+ case WPS_VENDOR_ID_WFA:
+ return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
+ }
+
+ /* Handle unknown vendor extensions */
+
+ wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
+ vendor_id);
+
+ if (len > WPS_MAX_VENDOR_EXT_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
+ len);
+ return -1;
+ }
+
+ if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
+ wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
+ "attribute (max %d vendor extensions)",
+ MAX_WPS_PARSE_VENDOR_EXT);
+ return -1;
+ }
+ attr->vendor_ext[attr->num_vendor_ext] = pos;
+ attr->vendor_ext_len[attr->num_vendor_ext] = len;
+ attr->num_vendor_ext++;
+
+ return 0;
+}
+
+
+static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
+ const u8 *pos, u16 len)
+{
+ switch (type) {
+ case ATTR_VERSION:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
+ len);
+ return -1;
+ }
+ attr->version = pos;
+ break;
+ case ATTR_MSG_TYPE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
+ "length %u", len);
+ return -1;
+ }
+ attr->msg_type = pos;
+ break;
+ case ATTR_ENROLLEE_NONCE:
+ if (len != WPS_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
+ "length %u", len);
+ return -1;
+ }
+ attr->enrollee_nonce = pos;
+ break;
+ case ATTR_REGISTRAR_NONCE:
+ if (len != WPS_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
+ "length %u", len);
+ return -1;
+ }
+ attr->registrar_nonce = pos;
+ break;
+ case ATTR_UUID_E:
+ if (len != WPS_UUID_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
+ len);
+ return -1;
+ }
+ attr->uuid_e = pos;
+ break;
+ case ATTR_UUID_R:
+ if (len != WPS_UUID_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
+ len);
+ return -1;
+ }
+ attr->uuid_r = pos;
+ break;
+ case ATTR_AUTH_TYPE_FLAGS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
+ "Type Flags length %u", len);
+ return -1;
+ }
+ attr->auth_type_flags = pos;
+ break;
+ case ATTR_ENCR_TYPE_FLAGS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
+ "Flags length %u", len);
+ return -1;
+ }
+ attr->encr_type_flags = pos;
+ break;
+ case ATTR_CONN_TYPE_FLAGS:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
+ "Flags length %u", len);
+ return -1;
+ }
+ attr->conn_type_flags = pos;
+ break;
+ case ATTR_CONFIG_METHODS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
+ "length %u", len);
+ return -1;
+ }
+ attr->config_methods = pos;
+ break;
+ case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
+ "Registrar Config Methods length %u", len);
+ return -1;
+ }
+ attr->sel_reg_config_methods = pos;
+ break;
+ case ATTR_PRIMARY_DEV_TYPE:
+ if (len != WPS_DEV_TYPE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->primary_dev_type = pos;
+ break;
+ case ATTR_RF_BANDS:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
+ "%u", len);
+ return -1;
+ }
+ attr->rf_bands = pos;
+ break;
+ case ATTR_ASSOC_STATE:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
+ "length %u", len);
+ return -1;
+ }
+ attr->assoc_state = pos;
+ break;
+ case ATTR_CONFIG_ERROR:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
+ "Error length %u", len);
+ return -1;
+ }
+ attr->config_error = pos;
+ break;
+ case ATTR_DEV_PASSWORD_ID:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
+ "ID length %u", len);
+ return -1;
+ }
+ attr->dev_password_id = pos;
+ break;
+ case ATTR_OOB_DEVICE_PASSWORD:
+ if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
+ "Password length %u", len);
+ return -1;
+ }
+ attr->oob_dev_password = pos;
+ break;
+ case ATTR_OS_VERSION:
+ if (len != 4) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
+ "%u", len);
+ return -1;
+ }
+ attr->os_version = pos;
+ break;
+ case ATTR_WPS_STATE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
+ "Setup State length %u", len);
+ return -1;
+ }
+ attr->wps_state = pos;
+ break;
+ case ATTR_AUTHENTICATOR:
+ if (len != WPS_AUTHENTICATOR_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
+ "length %u", len);
+ return -1;
+ }
+ attr->authenticator = pos;
+ break;
+ case ATTR_R_HASH1:
+ if (len != WPS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
+ len);
+ return -1;
+ }
+ attr->r_hash1 = pos;
+ break;
+ case ATTR_R_HASH2:
+ if (len != WPS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
+ len);
+ return -1;
+ }
+ attr->r_hash2 = pos;
+ break;
+ case ATTR_E_HASH1:
+ if (len != WPS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
+ len);
+ return -1;
+ }
+ attr->e_hash1 = pos;
+ break;
+ case ATTR_E_HASH2:
+ if (len != WPS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
+ len);
+ return -1;
+ }
+ attr->e_hash2 = pos;
+ break;
+ case ATTR_R_SNONCE1:
+ if (len != WPS_SECRET_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
+ "%u", len);
+ return -1;
+ }
+ attr->r_snonce1 = pos;
+ break;
+ case ATTR_R_SNONCE2:
+ if (len != WPS_SECRET_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
+ "%u", len);
+ return -1;
+ }
+ attr->r_snonce2 = pos;
+ break;
+ case ATTR_E_SNONCE1:
+ if (len != WPS_SECRET_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
+ "%u", len);
+ return -1;
+ }
+ attr->e_snonce1 = pos;
+ break;
+ case ATTR_E_SNONCE2:
+ if (len != WPS_SECRET_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
+ "%u", len);
+ return -1;
+ }
+ attr->e_snonce2 = pos;
+ break;
+ case ATTR_KEY_WRAP_AUTH:
+ if (len != WPS_KWA_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
+ "Authenticator length %u", len);
+ return -1;
+ }
+ attr->key_wrap_auth = pos;
+ break;
+ case ATTR_AUTH_TYPE:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->auth_type = pos;
+ break;
+ case ATTR_ENCR_TYPE:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->encr_type = pos;
+ break;
+ case ATTR_NETWORK_INDEX:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
+ "length %u", len);
+ return -1;
+ }
+ attr->network_idx = pos;
+ break;
+ case ATTR_NETWORK_KEY_INDEX:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
+ "length %u", len);
+ return -1;
+ }
+ attr->network_key_idx = pos;
+ break;
+ case ATTR_MAC_ADDR:
+ if (len != ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
+ "length %u", len);
+ return -1;
+ }
+ attr->mac_addr = pos;
+ break;
+ case ATTR_KEY_PROVIDED_AUTO:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
+ "Automatically length %u", len);
+ return -1;
+ }
+ attr->key_prov_auto = pos;
+ break;
+ case ATTR_802_1X_ENABLED:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
+ "length %u", len);
+ return -1;
+ }
+ attr->dot1x_enabled = pos;
+ break;
+ case ATTR_SELECTED_REGISTRAR:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
+ " length %u", len);
+ return -1;
+ }
+ attr->selected_registrar = pos;
+ break;
+ case ATTR_REQUEST_TYPE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
+ "length %u", len);
+ return -1;
+ }
+ attr->request_type = pos;
+ break;
+ case ATTR_RESPONSE_TYPE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
+ "length %u", len);
+ return -1;
+ }
+ attr->response_type = pos;
+ break;
+ case ATTR_MANUFACTURER:
+ attr->manufacturer = pos;
+ attr->manufacturer_len = len;
+ break;
+ case ATTR_MODEL_NAME:
+ attr->model_name = pos;
+ attr->model_name_len = len;
+ break;
+ case ATTR_MODEL_NUMBER:
+ attr->model_number = pos;
+ attr->model_number_len = len;
+ break;
+ case ATTR_SERIAL_NUMBER:
+ attr->serial_number = pos;
+ attr->serial_number_len = len;
+ break;
+ case ATTR_DEV_NAME:
+ attr->dev_name = pos;
+ attr->dev_name_len = len;
+ break;
+ case ATTR_PUBLIC_KEY:
+ attr->public_key = pos;
+ attr->public_key_len = len;
+ break;
+ case ATTR_ENCR_SETTINGS:
+ attr->encr_settings = pos;
+ attr->encr_settings_len = len;
+ break;
+ case ATTR_CRED:
+ if (attr->num_cred >= MAX_CRED_COUNT) {
+ wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
+ "attribute (max %d credentials)",
+ MAX_CRED_COUNT);
+ break;
+ }
+ attr->cred[attr->num_cred] = pos;
+ attr->cred_len[attr->num_cred] = len;
+ attr->num_cred++;
+ break;
+ case ATTR_SSID:
+ attr->ssid = pos;
+ attr->ssid_len = len;
+ break;
+ case ATTR_NETWORK_KEY:
+ attr->network_key = pos;
+ attr->network_key_len = len;
+ break;
+ case ATTR_EAP_TYPE:
+ attr->eap_type = pos;
+ attr->eap_type_len = len;
+ break;
+ case ATTR_EAP_IDENTITY:
+ attr->eap_identity = pos;
+ attr->eap_identity_len = len;
+ break;
+ case ATTR_AP_SETUP_LOCKED:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
+ "length %u", len);
+ return -1;
+ }
+ attr->ap_setup_locked = pos;
+ break;
+ case ATTR_REQUESTED_DEV_TYPE:
+ if (len != WPS_DEV_TYPE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
+ "Type length %u", len);
+ return -1;
+ }
+ if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
+ wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
+ "Type attribute (max %u types)",
+ MAX_REQ_DEV_TYPE_COUNT);
+ break;
+ }
+ attr->req_dev_type[attr->num_req_dev_type] = pos;
+ attr->num_req_dev_type++;
+ break;
+ case ATTR_SECONDARY_DEV_TYPE_LIST:
+ if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
+ (len % WPS_DEV_TYPE_LEN) > 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->sec_dev_type_list = pos;
+ attr->sec_dev_type_list_len = len;
+ break;
+ case ATTR_VENDOR_EXT:
+ if (wps_parse_vendor_ext(attr, pos, len) < 0)
+ return -1;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
+ "len=%u", type, len);
+ break;
+ }
+
+ return 0;
+}
+
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
+{
+ const u8 *pos, *end;
+ u16 type, len;
+ u16 prev_type = 0;
+
+ os_memset(attr, 0, sizeof(*attr));
+ pos = wpabuf_head(msg);
+ end = pos + wpabuf_len(msg);
+
+ while (pos < end) {
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
+ "%lu bytes remaining",
+ (unsigned long) (end - pos));
+ return -1;
+ }
+
+ type = WPA_GET_BE16(pos);
+ pos += 2;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
+ type, len);
+ if (len > end - pos) {
+ wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
+ wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
+#ifdef WPS_WORKAROUNDS
+ /*
+ * Some deployed APs seem to have a bug in encoding of
+ * Network Key attribute in the Credential attribute
+ * where they add an extra octet after the Network Key
+ * attribute at least when open network is being
+ * provisioned.
+ */
+ if ((type & 0xff00) != 0x1000 &&
+ prev_type == ATTR_NETWORK_KEY) {
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
+ "to skip unexpected octet after "
+ "Network Key");
+ pos -= 3;
+ continue;
+ }
+#endif /* WPS_WORKAROUNDS */
+ return -1;
+ }
+
+#ifdef WPS_WORKAROUNDS
+ if (type == 0 && len == 0) {
+ /*
+ * Mac OS X 10.6 seems to be adding 0x00 padding to the
+ * end of M1. Skip those to avoid interop issues.
+ */
+ int i;
+ for (i = 0; i < end - pos; i++) {
+ if (pos[i])
+ break;
+ }
+ if (i == end - pos) {
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
+ "unexpected message padding");
+ break;
+ }
+ }
+#endif /* WPS_WORKAROUNDS */
+
+ if (wps_set_attr(attr, type, pos, len) < 0)
+ return -1;
+
+ prev_type = type;
+ pos += len;
+ }
+
+ return 0;
+}
diff --git a/src/wps/wps_attr_process.c b/src/wps/wps_attr_process.c
new file mode 100644
index 0000000..07e087d
--- /dev/null
+++ b/src/wps/wps_attr_process.c
@@ -0,0 +1,335 @@
+/*
+ * Wi-Fi Protected Setup - attribute processing
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha256.h"
+#include "wps_i.h"
+
+
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+ const struct wpabuf *msg)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[2];
+ size_t len[2];
+
+ if (authenticator == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Authenticator attribute "
+ "included");
+ return -1;
+ }
+
+ if (wps->last_msg == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
+ "validating authenticator");
+ return -1;
+ }
+
+ /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+ * (M_curr* is M_curr without the Authenticator attribute)
+ */
+ addr[0] = wpabuf_head(wps->last_msg);
+ len[0] = wpabuf_len(wps->last_msg);
+ addr[1] = wpabuf_head(msg);
+ len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+
+ if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+ const u8 *key_wrap_auth)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *head;
+ size_t len;
+
+ if (key_wrap_auth == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No KWA in decrypted attribute");
+ return -1;
+ }
+
+ head = wpabuf_head(msg);
+ len = wpabuf_len(msg) - 4 - WPS_KWA_LEN;
+ if (head + len != key_wrap_auth - 4) {
+ wpa_printf(MSG_DEBUG, "WPS: KWA not in the end of the "
+ "decrypted attribute");
+ return -1;
+ }
+
+ hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
+ if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_cred_network_idx(struct wps_credential *cred,
+ const u8 *idx)
+{
+ if (idx == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "Network Index");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Network Index: %d", *idx);
+
+ return 0;
+}
+
+
+static int wps_process_cred_ssid(struct wps_credential *cred, const u8 *ssid,
+ size_t ssid_len)
+{
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include SSID");
+ return -1;
+ }
+
+ /* Remove zero-padding since some Registrar implementations seem to use
+ * hardcoded 32-octet length for this attribute */
+ while (ssid_len > 0 && ssid[ssid_len - 1] == 0)
+ ssid_len--;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", ssid, ssid_len);
+ if (ssid_len <= sizeof(cred->ssid)) {
+ os_memcpy(cred->ssid, ssid, ssid_len);
+ cred->ssid_len = ssid_len;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_cred_auth_type(struct wps_credential *cred,
+ const u8 *auth_type)
+{
+ if (auth_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "Authentication Type");
+ return -1;
+ }
+
+ cred->auth_type = WPA_GET_BE16(auth_type);
+ wpa_printf(MSG_DEBUG, "WPS: Authentication Type: 0x%x",
+ cred->auth_type);
+
+ return 0;
+}
+
+
+static int wps_process_cred_encr_type(struct wps_credential *cred,
+ const u8 *encr_type)
+{
+ if (encr_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "Encryption Type");
+ return -1;
+ }
+
+ cred->encr_type = WPA_GET_BE16(encr_type);
+ wpa_printf(MSG_DEBUG, "WPS: Encryption Type: 0x%x",
+ cred->encr_type);
+
+ return 0;
+}
+
+
+static int wps_process_cred_network_key_idx(struct wps_credential *cred,
+ const u8 *key_idx)
+{
+ if (key_idx == NULL)
+ return 0; /* optional attribute */
+
+ wpa_printf(MSG_DEBUG, "WPS: Network Key Index: %d", *key_idx);
+ cred->key_idx = *key_idx;
+
+ return 0;
+}
+
+
+static int wps_process_cred_network_key(struct wps_credential *cred,
+ const u8 *key, size_t key_len)
+{
+ if (key == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "Network Key");
+ if (cred->auth_type == WPS_AUTH_OPEN &&
+ cred->encr_type == WPS_ENCR_NONE) {
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - Allow "
+ "missing mandatory Network Key attribute "
+ "for open network");
+ return 0;
+ }
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", key, key_len);
+ if (key_len <= sizeof(cred->key)) {
+ os_memcpy(cred->key, key, key_len);
+ cred->key_len = key_len;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_cred_mac_addr(struct wps_credential *cred,
+ const u8 *mac_addr)
+{
+ if (mac_addr == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+ "MAC Address");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(mac_addr));
+ os_memcpy(cred->mac_addr, mac_addr, ETH_ALEN);
+
+ return 0;
+}
+
+
+static int wps_process_cred_eap_type(struct wps_credential *cred,
+ const u8 *eap_type, size_t eap_type_len)
+{
+ if (eap_type == NULL)
+ return 0; /* optional attribute */
+
+ wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len);
+
+ return 0;
+}
+
+
+static int wps_process_cred_eap_identity(struct wps_credential *cred,
+ const u8 *identity,
+ size_t identity_len)
+{
+ if (identity == NULL)
+ return 0; /* optional attribute */
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity",
+ identity, identity_len);
+
+ return 0;
+}
+
+
+static int wps_process_cred_key_prov_auto(struct wps_credential *cred,
+ const u8 *key_prov_auto)
+{
+ if (key_prov_auto == NULL)
+ return 0; /* optional attribute */
+
+ wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d",
+ *key_prov_auto);
+
+ return 0;
+}
+
+
+static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
+ const u8 *dot1x_enabled)
+{
+ if (dot1x_enabled == NULL)
+ return 0; /* optional attribute */
+
+ wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled);
+
+ return 0;
+}
+
+
+static int wps_workaround_cred_key(struct wps_credential *cred)
+{
+ if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
+ cred->key_len > 8 && cred->key_len < 64 &&
+ cred->key[cred->key_len - 1] == 0) {
+#ifdef CONFIG_WPS_STRICT
+ wpa_printf(MSG_INFO, "WPS: WPA/WPA2-Personal passphrase uses "
+ "forbidden NULL termination");
+ wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key",
+ cred->key, cred->key_len);
+ return -1;
+#else /* CONFIG_WPS_STRICT */
+ /*
+ * A deployed external registrar is known to encode ASCII
+ * passphrases incorrectly. Remove the extra NULL termination
+ * to fix the encoding.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL "
+ "termination from ASCII passphrase");
+ cred->key_len--;
+#endif /* CONFIG_WPS_STRICT */
+ }
+ return 0;
+}
+
+
+int wps_process_cred(struct wps_parse_attr *attr,
+ struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Process Credential");
+
+ /* TODO: support multiple Network Keys */
+ if (wps_process_cred_network_idx(cred, attr->network_idx) ||
+ wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
+ wps_process_cred_auth_type(cred, attr->auth_type) ||
+ wps_process_cred_encr_type(cred, attr->encr_type) ||
+ wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
+ wps_process_cred_network_key(cred, attr->network_key,
+ attr->network_key_len) ||
+ wps_process_cred_mac_addr(cred, attr->mac_addr) ||
+ wps_process_cred_eap_type(cred, attr->eap_type,
+ attr->eap_type_len) ||
+ wps_process_cred_eap_identity(cred, attr->eap_identity,
+ attr->eap_identity_len) ||
+ wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) ||
+ wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled))
+ return -1;
+
+ return wps_workaround_cred_key(cred);
+}
+
+
+int wps_process_ap_settings(struct wps_parse_attr *attr,
+ struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Processing AP Settings");
+ os_memset(cred, 0, sizeof(*cred));
+ /* TODO: optional attributes New Password and Device Password ID */
+ if (wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
+ wps_process_cred_auth_type(cred, attr->auth_type) ||
+ wps_process_cred_encr_type(cred, attr->encr_type) ||
+ wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
+ wps_process_cred_network_key(cred, attr->network_key,
+ attr->network_key_len) ||
+ wps_process_cred_mac_addr(cred, attr->mac_addr))
+ return -1;
+
+ return wps_workaround_cred_key(cred);
+}
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
new file mode 100644
index 0000000..5d0508c
--- /dev/null
+++ b/src/wps/wps_common.c
@@ -0,0 +1,704 @@
+/*
+ * Wi-Fi Protected Setup - common functionality
+ * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_group5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
+ const char *label, u8 *res, size_t res_len)
+{
+ u8 i_buf[4], key_bits[4];
+ const u8 *addr[4];
+ size_t len[4];
+ int i, iter;
+ u8 hash[SHA256_MAC_LEN], *opos;
+ size_t left;
+
+ WPA_PUT_BE32(key_bits, res_len * 8);
+
+ addr[0] = i_buf;
+ len[0] = sizeof(i_buf);
+ addr[1] = label_prefix;
+ len[1] = label_prefix_len;
+ addr[2] = (const u8 *) label;
+ len[2] = os_strlen(label);
+ addr[3] = key_bits;
+ len[3] = sizeof(key_bits);
+
+ iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN;
+ opos = res;
+ left = res_len;
+
+ for (i = 1; i <= iter; i++) {
+ WPA_PUT_BE32(i_buf, i);
+ hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, len, hash);
+ if (i < iter) {
+ os_memcpy(opos, hash, SHA256_MAC_LEN);
+ opos += SHA256_MAC_LEN;
+ left -= SHA256_MAC_LEN;
+ } else
+ os_memcpy(opos, hash, left);
+ }
+}
+
+
+int wps_derive_keys(struct wps_data *wps)
+{
+ struct wpabuf *pubkey, *dh_shared;
+ u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN];
+
+ if (wps->dh_privkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available");
+ return -1;
+ }
+
+ pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r;
+ if (pubkey == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available");
+ return -1;
+ }
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey);
+ dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey);
+ dh5_free(wps->dh_ctx);
+ wps->dh_ctx = NULL;
+ dh_shared = wpabuf_zeropad(dh_shared, 192);
+ if (dh_shared == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key");
+ return -1;
+ }
+
+ /* Own DH private key is not needed anymore */
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = NULL;
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared);
+
+ /* DHKey = SHA-256(g^AB mod p) */
+ addr[0] = wpabuf_head(dh_shared);
+ len[0] = wpabuf_len(dh_shared);
+ sha256_vector(1, addr, len, dhkey);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey));
+ wpabuf_free(dh_shared);
+
+ /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */
+ addr[0] = wps->nonce_e;
+ len[0] = WPS_NONCE_LEN;
+ addr[1] = wps->mac_addr_e;
+ len[1] = ETH_ALEN;
+ addr[2] = wps->nonce_r;
+ len[2] = WPS_NONCE_LEN;
+ hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk));
+
+ wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation",
+ keys, sizeof(keys));
+ os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN);
+ os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN);
+ os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN,
+ WPS_EMSK_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey",
+ wps->authkey, WPS_AUTHKEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey",
+ wps->keywrapkey, WPS_KEYWRAPKEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN);
+
+ return 0;
+}
+
+
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+ size_t dev_passwd_len)
+{
+ u8 hash[SHA256_MAC_LEN];
+
+ hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
+ (dev_passwd_len + 1) / 2, hash);
+ os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
+ hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
+ dev_passwd + (dev_passwd_len + 1) / 2,
+ dev_passwd_len / 2, hash);
+ os_memcpy(wps->psk2, hash, WPS_PSK_LEN);
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password",
+ dev_passwd, dev_passwd_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN);
+}
+
+
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+ size_t encr_len)
+{
+ struct wpabuf *decrypted;
+ const size_t block_size = 16;
+ size_t i;
+ u8 pad;
+ const u8 *pos;
+
+ /* AES-128-CBC */
+ if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: No Encrypted Settings received");
+ return NULL;
+ }
+
+ decrypted = wpabuf_alloc(encr_len - block_size);
+ if (decrypted == NULL)
+ return NULL;
+
+ wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len);
+ wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size);
+ if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
+ wpabuf_len(decrypted))) {
+ wpabuf_free(decrypted);
+ return NULL;
+ }
+
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings",
+ decrypted);
+
+ pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1;
+ pad = *pos;
+ if (pad > wpabuf_len(decrypted)) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value");
+ wpabuf_free(decrypted);
+ return NULL;
+ }
+ for (i = 0; i < pad; i++) {
+ if (*pos-- != pad) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad "
+ "string");
+ wpabuf_free(decrypted);
+ return NULL;
+ }
+ }
+ decrypted->used -= pad;
+
+ return decrypted;
+}
+
+
+/**
+ * wps_pin_checksum - Compute PIN checksum
+ * @pin: Seven digit PIN (i.e., eight digit PIN without the checksum digit)
+ * Returns: Checksum digit
+ */
+unsigned int wps_pin_checksum(unsigned int pin)
+{
+ unsigned int accum = 0;
+ while (pin) {
+ accum += 3 * (pin % 10);
+ pin /= 10;
+ accum += pin % 10;
+ pin /= 10;
+ }
+
+ return (10 - accum % 10) % 10;
+}
+
+
+/**
+ * wps_pin_valid - Check whether a PIN has a valid checksum
+ * @pin: Eight digit PIN (i.e., including the checksum digit)
+ * Returns: 1 if checksum digit is valid, or 0 if not
+ */
+unsigned int wps_pin_valid(unsigned int pin)
+{
+ return wps_pin_checksum(pin / 10) == (pin % 10);
+}
+
+
+/**
+ * wps_generate_pin - Generate a random PIN
+ * Returns: Eight digit PIN (i.e., including the checksum digit)
+ */
+unsigned int wps_generate_pin(void)
+{
+ unsigned int val;
+
+ /* Generate seven random digits for the PIN */
+ if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) {
+ struct os_time now;
+ os_get_time(&now);
+ val = os_random() ^ now.sec ^ now.usec;
+ }
+ val %= 10000000;
+
+ /* Append checksum digit */
+ return val * 10 + wps_pin_checksum(val);
+}
+
+
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+ u16 config_error, u16 error_indication)
+{
+ union wps_event_data data;
+
+ if (wps->event_cb == NULL)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ data.fail.msg = msg;
+ data.fail.config_error = config_error;
+ data.fail.error_indication = error_indication;
+ wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data);
+}
+
+
+void wps_success_event(struct wps_context *wps)
+{
+ if (wps->event_cb == NULL)
+ return;
+
+ wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL);
+}
+
+
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
+{
+ union wps_event_data data;
+
+ if (wps->event_cb == NULL)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ data.pwd_auth_fail.enrollee = enrollee;
+ data.pwd_auth_fail.part = part;
+ wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data);
+}
+
+
+void wps_pbc_overlap_event(struct wps_context *wps)
+{
+ if (wps->event_cb == NULL)
+ return;
+
+ wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL);
+}
+
+
+void wps_pbc_timeout_event(struct wps_context *wps)
+{
+ if (wps->event_cb == NULL)
+ return;
+
+ wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL);
+}
+
+
+#ifdef CONFIG_WPS_OOB
+
+static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+{
+ struct wps_data data;
+ struct wpabuf *plain;
+
+ plain = wpabuf_alloc(500);
+ if (plain == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+ "credential");
+ return NULL;
+ }
+
+ os_memset(&data, 0, sizeof(data));
+ data.wps = wps;
+ data.auth_type = wps->auth_types;
+ data.encr_type = wps->encr_types;
+ if (wps_build_version(plain) ||
+ wps_build_cred(&data, plain) ||
+ wps_build_wfa_ext(plain, 0, NULL, 0)) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ return plain;
+}
+
+
+static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps)
+{
+ struct wpabuf *data;
+
+ data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
+ if (data == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+ "device password attribute");
+ return NULL;
+ }
+
+ wpabuf_free(wps->oob_conf.dev_password);
+ wps->oob_conf.dev_password =
+ wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
+ if (wps->oob_conf.dev_password == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+ "device password");
+ wpabuf_free(data);
+ return NULL;
+ }
+
+ if (wps_build_version(data) ||
+ wps_build_oob_dev_password(data, wps) ||
+ wps_build_wfa_ext(data, 0, NULL, 0)) {
+ wpa_printf(MSG_ERROR, "WPS: Build OOB device password "
+ "attribute error");
+ wpabuf_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+static int wps_parse_oob_dev_pwd(struct wps_context *wps,
+ struct wpabuf *data)
+{
+ struct oob_conf_data *oob_conf = &wps->oob_conf;
+ struct wps_parse_attr attr;
+ const u8 *pos;
+
+ if (wps_parse_msg(data, &attr) < 0 ||
+ attr.oob_dev_password == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: OOB device password not found");
+ return -1;
+ }
+
+ pos = attr.oob_dev_password;
+
+ oob_conf->pubkey_hash =
+ wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN);
+ if (oob_conf->pubkey_hash == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+ "public key hash");
+ return -1;
+ }
+ pos += WPS_OOB_PUBKEY_HASH_LEN;
+
+ wps->oob_dev_pw_id = WPA_GET_BE16(pos);
+ pos += sizeof(wps->oob_dev_pw_id);
+
+ oob_conf->dev_password =
+ wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
+ if (oob_conf->dev_password == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+ "device password");
+ return -1;
+ }
+ wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password,
+ wpabuf_size(oob_conf->dev_password)),
+ wpabuf_size(oob_conf->dev_password), pos,
+ WPS_OOB_DEVICE_PASSWORD_LEN);
+
+ return 0;
+}
+
+
+static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data)
+{
+ struct wpabuf msg;
+ struct wps_parse_attr attr;
+ size_t i;
+
+ if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) {
+ wpa_printf(MSG_ERROR, "WPS: OOB credential not found");
+ return -1;
+ }
+
+ for (i = 0; i < attr.num_cred; i++) {
+ struct wps_credential local_cred;
+ struct wps_parse_attr cattr;
+
+ os_memset(&local_cred, 0, sizeof(local_cred));
+ wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]);
+ if (wps_parse_msg(&msg, &cattr) < 0 ||
+ wps_process_cred(&cattr, &local_cred)) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
+ "credential");
+ return -1;
+ }
+ wps->cred_cb(wps->cb_ctx, &local_cred);
+ }
+
+ return 0;
+}
+
+
+int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
+ int registrar)
+{
+ struct wpabuf *data;
+ int ret, write_f, oob_method = wps->oob_conf.oob_method;
+ void *oob_priv;
+
+ write_f = oob_method == OOB_METHOD_DEV_PWD_E ? !registrar : registrar;
+
+ oob_priv = oob_dev->init_func(wps, oob_dev, registrar);
+ if (oob_priv == NULL) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to initialize OOB device");
+ return -1;
+ }
+
+ if (write_f) {
+ if (oob_method == OOB_METHOD_CRED)
+ data = wps_get_oob_cred(wps);
+ else
+ data = wps_get_oob_dev_pwd(wps);
+
+ ret = 0;
+ if (data == NULL || oob_dev->write_func(oob_priv, data) < 0)
+ ret = -1;
+ } else {
+ data = oob_dev->read_func(oob_priv);
+ if (data == NULL)
+ ret = -1;
+ else {
+ if (oob_method == OOB_METHOD_CRED)
+ ret = wps_parse_oob_cred(wps, data);
+ else
+ ret = wps_parse_oob_dev_pwd(wps, data);
+ }
+ }
+ wpabuf_free(data);
+ oob_dev->deinit_func(oob_priv);
+
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to process OOB data");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+struct oob_device_data * wps_get_oob_device(char *device_type)
+{
+#ifdef CONFIG_WPS_UFD
+ if (os_strstr(device_type, "ufd") != NULL)
+ return &oob_ufd_device_data;
+#endif /* CONFIG_WPS_UFD */
+#ifdef CONFIG_WPS_NFC
+ if (os_strstr(device_type, "nfc") != NULL)
+ return &oob_nfc_device_data;
+#endif /* CONFIG_WPS_NFC */
+
+ return NULL;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name)
+{
+ if (device_name == NULL)
+ return NULL;
+#ifdef CONFIG_WPS_NFC_PN531
+ if (os_strstr(device_name, "pn531") != NULL)
+ return &oob_nfc_pn531_device_data;
+#endif /* CONFIG_WPS_NFC_PN531 */
+
+ return NULL;
+}
+#endif /* CONFIG_WPS_NFC */
+
+
+int wps_get_oob_method(char *method)
+{
+ if (os_strstr(method, "pin-e") != NULL)
+ return OOB_METHOD_DEV_PWD_E;
+ if (os_strstr(method, "pin-r") != NULL)
+ return OOB_METHOD_DEV_PWD_R;
+ if (os_strstr(method, "cred") != NULL)
+ return OOB_METHOD_CRED;
+ return OOB_METHOD_UNKNOWN;
+}
+
+#endif /* CONFIG_WPS_OOB */
+
+
+int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN])
+{
+ const char *pos;
+
+ /* <categ>-<OUI>-<subcateg> */
+ WPA_PUT_BE16(dev_type, atoi(str));
+ pos = os_strchr(str, '-');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ if (hexstr2bin(pos, &dev_type[2], 4))
+ return -1;
+ pos = os_strchr(pos, '-');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ WPA_PUT_BE16(&dev_type[6], atoi(pos));
+
+
+ return 0;
+}
+
+
+char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
+ size_t buf_len)
+{
+ int ret;
+
+ ret = os_snprintf(buf, buf_len, "%u-%08X-%u",
+ WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]),
+ WPA_GET_BE16(&dev_type[6]));
+ if (ret < 0 || (unsigned int) ret >= buf_len)
+ return NULL;
+
+ return buf;
+}
+
+
+void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 hash[SHA1_MAC_LEN];
+ u8 nsid[16] = {
+ 0x52, 0x64, 0x80, 0xf8,
+ 0xc9, 0x9b,
+ 0x4b, 0xe5,
+ 0xa6, 0x55,
+ 0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84
+ };
+
+ addr[0] = nsid;
+ len[0] = sizeof(nsid);
+ addr[1] = mac_addr;
+ len[1] = 6;
+ sha1_vector(2, addr, len, hash);
+ os_memcpy(uuid, hash, 16);
+
+ /* Version: 5 = named-based version using SHA-1 */
+ uuid[6] = (5 << 4) | (uuid[6] & 0x0f);
+
+ /* Variant specified in RFC 4122 */
+ uuid[8] = 0x80 | (uuid[8] & 0x3f);
+}
+
+
+u16 wps_config_methods_str2bin(const char *str)
+{
+ u16 methods = 0;
+
+ if (str == NULL) {
+ /* Default to enabling methods based on build configuration */
+ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+ methods |= WPS_CONFIG_VIRT_DISPLAY;
+#endif /* CONFIG_WPS2 */
+#ifdef CONFIG_WPS_UFD
+ methods |= WPS_CONFIG_USBA;
+#endif /* CONFIG_WPS_UFD */
+#ifdef CONFIG_WPS_NFC
+ methods |= WPS_CONFIG_NFC_INTERFACE;
+#endif /* CONFIG_WPS_NFC */
+ } else {
+ if (os_strstr(str, "usba"))
+ methods |= WPS_CONFIG_USBA;
+ if (os_strstr(str, "ethernet"))
+ methods |= WPS_CONFIG_ETHERNET;
+ if (os_strstr(str, "label"))
+ methods |= WPS_CONFIG_LABEL;
+ if (os_strstr(str, "display"))
+ methods |= WPS_CONFIG_DISPLAY;
+ if (os_strstr(str, "ext_nfc_token"))
+ methods |= WPS_CONFIG_EXT_NFC_TOKEN;
+ if (os_strstr(str, "int_nfc_token"))
+ methods |= WPS_CONFIG_INT_NFC_TOKEN;
+ if (os_strstr(str, "nfc_interface"))
+ methods |= WPS_CONFIG_NFC_INTERFACE;
+ if (os_strstr(str, "push_button"))
+ methods |= WPS_CONFIG_PUSHBUTTON;
+ if (os_strstr(str, "keypad"))
+ methods |= WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+ if (os_strstr(str, "virtual_display"))
+ methods |= WPS_CONFIG_VIRT_DISPLAY;
+ if (os_strstr(str, "physical_display"))
+ methods |= WPS_CONFIG_PHY_DISPLAY;
+ if (os_strstr(str, "virtual_push_button"))
+ methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ if (os_strstr(str, "physical_push_button"))
+ methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+#endif /* CONFIG_WPS2 */
+ }
+
+ return methods;
+}
+
+
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_ACK) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_NACK) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_config_error(msg, wps->config_error) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
new file mode 100644
index 0000000..43311f3
--- /dev/null
+++ b/src/wps/wps_defs.h
@@ -0,0 +1,336 @@
+/*
+ * Wi-Fi Protected Setup - message definitions
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPS_DEFS_H
+#define WPS_DEFS_H
+
+#ifdef CONFIG_WPS_TESTING
+
+extern int wps_version_number;
+extern int wps_testing_dummy_cred;
+#define WPS_VERSION wps_version_number
+
+#else /* CONFIG_WPS_TESTING */
+
+#ifdef CONFIG_WPS2
+#define WPS_VERSION 0x20
+#else /* CONFIG_WPS2 */
+#define WPS_VERSION 0x10
+#endif /* CONFIG_WPS2 */
+
+#endif /* CONFIG_WPS_TESTING */
+
+/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
+#define WPS_DH_GROUP 5
+
+#define WPS_UUID_LEN 16
+#define WPS_NONCE_LEN 16
+#define WPS_AUTHENTICATOR_LEN 8
+#define WPS_AUTHKEY_LEN 32
+#define WPS_KEYWRAPKEY_LEN 16
+#define WPS_EMSK_LEN 32
+#define WPS_PSK_LEN 16
+#define WPS_SECRET_NONCE_LEN 16
+#define WPS_HASH_LEN 32
+#define WPS_KWA_LEN 8
+#define WPS_MGMTAUTHKEY_LEN 32
+#define WPS_MGMTENCKEY_LEN 16
+#define WPS_MGMT_KEY_ID_LEN 16
+#define WPS_OOB_DEVICE_PASSWORD_ATTR_LEN 54
+#define WPS_OOB_DEVICE_PASSWORD_LEN 32
+#define WPS_OOB_PUBKEY_HASH_LEN 20
+
+/* Attribute Types */
+enum wps_attribute {
+ ATTR_AP_CHANNEL = 0x1001,
+ ATTR_ASSOC_STATE = 0x1002,
+ ATTR_AUTH_TYPE = 0x1003,
+ ATTR_AUTH_TYPE_FLAGS = 0x1004,
+ ATTR_AUTHENTICATOR = 0x1005,
+ ATTR_CONFIG_METHODS = 0x1008,
+ ATTR_CONFIG_ERROR = 0x1009,
+ ATTR_CONFIRM_URL4 = 0x100a,
+ ATTR_CONFIRM_URL6 = 0x100b,
+ ATTR_CONN_TYPE = 0x100c,
+ ATTR_CONN_TYPE_FLAGS = 0x100d,
+ ATTR_CRED = 0x100e,
+ ATTR_ENCR_TYPE = 0x100f,
+ ATTR_ENCR_TYPE_FLAGS = 0x1010,
+ ATTR_DEV_NAME = 0x1011,
+ ATTR_DEV_PASSWORD_ID = 0x1012,
+ ATTR_E_HASH1 = 0x1014,
+ ATTR_E_HASH2 = 0x1015,
+ ATTR_E_SNONCE1 = 0x1016,
+ ATTR_E_SNONCE2 = 0x1017,
+ ATTR_ENCR_SETTINGS = 0x1018,
+ ATTR_ENROLLEE_NONCE = 0x101a,
+ ATTR_FEATURE_ID = 0x101b,
+ ATTR_IDENTITY = 0x101c,
+ ATTR_IDENTITY_PROOF = 0x101d,
+ ATTR_KEY_WRAP_AUTH = 0x101e,
+ ATTR_KEY_ID = 0x101f,
+ ATTR_MAC_ADDR = 0x1020,
+ ATTR_MANUFACTURER = 0x1021,
+ ATTR_MSG_TYPE = 0x1022,
+ ATTR_MODEL_NAME = 0x1023,
+ ATTR_MODEL_NUMBER = 0x1024,
+ ATTR_NETWORK_INDEX = 0x1026,
+ ATTR_NETWORK_KEY = 0x1027,
+ ATTR_NETWORK_KEY_INDEX = 0x1028,
+ ATTR_NEW_DEVICE_NAME = 0x1029,
+ ATTR_NEW_PASSWORD = 0x102a,
+ ATTR_OOB_DEVICE_PASSWORD = 0x102c,
+ ATTR_OS_VERSION = 0x102d,
+ ATTR_POWER_LEVEL = 0x102f,
+ ATTR_PSK_CURRENT = 0x1030,
+ ATTR_PSK_MAX = 0x1031,
+ ATTR_PUBLIC_KEY = 0x1032,
+ ATTR_RADIO_ENABLE = 0x1033,
+ ATTR_REBOOT = 0x1034,
+ ATTR_REGISTRAR_CURRENT = 0x1035,
+ ATTR_REGISTRAR_ESTABLISHED = 0x1036,
+ ATTR_REGISTRAR_LIST = 0x1037,
+ ATTR_REGISTRAR_MAX = 0x1038,
+ ATTR_REGISTRAR_NONCE = 0x1039,
+ ATTR_REQUEST_TYPE = 0x103a,
+ ATTR_RESPONSE_TYPE = 0x103b,
+ ATTR_RF_BANDS = 0x103c,
+ ATTR_R_HASH1 = 0x103d,
+ ATTR_R_HASH2 = 0x103e,
+ ATTR_R_SNONCE1 = 0x103f,
+ ATTR_R_SNONCE2 = 0x1040,
+ ATTR_SELECTED_REGISTRAR = 0x1041,
+ ATTR_SERIAL_NUMBER = 0x1042,
+ ATTR_WPS_STATE = 0x1044,
+ ATTR_SSID = 0x1045,
+ ATTR_TOTAL_NETWORKS = 0x1046,
+ ATTR_UUID_E = 0x1047,
+ ATTR_UUID_R = 0x1048,
+ ATTR_VENDOR_EXT = 0x1049,
+ ATTR_VERSION = 0x104a,
+ ATTR_X509_CERT_REQ = 0x104b,
+ ATTR_X509_CERT = 0x104c,
+ ATTR_EAP_IDENTITY = 0x104d,
+ ATTR_MSG_COUNTER = 0x104e,
+ ATTR_PUBKEY_HASH = 0x104f,
+ ATTR_REKEY_KEY = 0x1050,
+ ATTR_KEY_LIFETIME = 0x1051,
+ ATTR_PERMITTED_CFG_METHODS = 0x1052,
+ ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053,
+ ATTR_PRIMARY_DEV_TYPE = 0x1054,
+ ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055,
+ ATTR_PORTABLE_DEV = 0x1056,
+ ATTR_AP_SETUP_LOCKED = 0x1057,
+ ATTR_APPLICATION_EXT = 0x1058,
+ ATTR_EAP_TYPE = 0x1059,
+ ATTR_IV = 0x1060,
+ ATTR_KEY_PROVIDED_AUTO = 0x1061,
+ ATTR_802_1X_ENABLED = 0x1062,
+ ATTR_APPSESSIONKEY = 0x1063,
+ ATTR_WEPTRANSMITKEY = 0x1064,
+ ATTR_REQUESTED_DEV_TYPE = 0x106a,
+ ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */
+};
+
+#define WPS_VENDOR_ID_WFA 14122
+
+/* WFA Vendor Extension subelements */
+enum {
+ WFA_ELEM_VERSION2 = 0x00,
+ WFA_ELEM_AUTHORIZEDMACS = 0x01,
+ WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
+ WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
+ WFA_ELEM_SETTINGS_DELAY_TIME = 0x04
+};
+
+/* Device Password ID */
+enum wps_dev_password_id {
+ DEV_PW_DEFAULT = 0x0000,
+ DEV_PW_USER_SPECIFIED = 0x0001,
+ DEV_PW_MACHINE_SPECIFIED = 0x0002,
+ DEV_PW_REKEY = 0x0003,
+ DEV_PW_PUSHBUTTON = 0x0004,
+ DEV_PW_REGISTRAR_SPECIFIED = 0x0005
+};
+
+/* Message Type */
+enum wps_msg_type {
+ WPS_Beacon = 0x01,
+ WPS_ProbeRequest = 0x02,
+ WPS_ProbeResponse = 0x03,
+ WPS_M1 = 0x04,
+ WPS_M2 = 0x05,
+ WPS_M2D = 0x06,
+ WPS_M3 = 0x07,
+ WPS_M4 = 0x08,
+ WPS_M5 = 0x09,
+ WPS_M6 = 0x0a,
+ WPS_M7 = 0x0b,
+ WPS_M8 = 0x0c,
+ WPS_WSC_ACK = 0x0d,
+ WPS_WSC_NACK = 0x0e,
+ WPS_WSC_DONE = 0x0f
+};
+
+/* Authentication Type Flags */
+#define WPS_AUTH_OPEN 0x0001
+#define WPS_AUTH_WPAPSK 0x0002
+#define WPS_AUTH_SHARED 0x0004
+#define WPS_AUTH_WPA 0x0008
+#define WPS_AUTH_WPA2 0x0010
+#define WPS_AUTH_WPA2PSK 0x0020
+#define WPS_AUTH_TYPES (WPS_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \
+ WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)
+
+/* Encryption Type Flags */
+#define WPS_ENCR_NONE 0x0001
+#define WPS_ENCR_WEP 0x0002
+#define WPS_ENCR_TKIP 0x0004
+#define WPS_ENCR_AES 0x0008
+#define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \
+ WPS_ENCR_AES)
+
+/* Configuration Error */
+enum wps_config_error {
+ WPS_CFG_NO_ERROR = 0,
+ WPS_CFG_OOB_IFACE_READ_ERROR = 1,
+ WPS_CFG_DECRYPTION_CRC_FAILURE = 2,
+ WPS_CFG_24_CHAN_NOT_SUPPORTED = 3,
+ WPS_CFG_50_CHAN_NOT_SUPPORTED = 4,
+ WPS_CFG_SIGNAL_TOO_WEAK = 5,
+ WPS_CFG_NETWORK_AUTH_FAILURE = 6,
+ WPS_CFG_NETWORK_ASSOC_FAILURE = 7,
+ WPS_CFG_NO_DHCP_RESPONSE = 8,
+ WPS_CFG_FAILED_DHCP_CONFIG = 9,
+ WPS_CFG_IP_ADDR_CONFLICT = 10,
+ WPS_CFG_NO_CONN_TO_REGISTRAR = 11,
+ WPS_CFG_MULTIPLE_PBC_DETECTED = 12,
+ WPS_CFG_ROGUE_SUSPECTED = 13,
+ WPS_CFG_DEVICE_BUSY = 14,
+ WPS_CFG_SETUP_LOCKED = 15,
+ WPS_CFG_MSG_TIMEOUT = 16,
+ WPS_CFG_REG_SESS_TIMEOUT = 17,
+ WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
+};
+
+/* Vendor specific Error Indication for WPS event messages */
+enum wps_error_indication {
+ WPS_EI_NO_ERROR,
+ WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED,
+ WPS_EI_SECURITY_WEP_PROHIBITED,
+ NUM_WPS_EI_VALUES
+};
+
+/* RF Bands */
+#define WPS_RF_24GHZ 0x01
+#define WPS_RF_50GHZ 0x02
+
+/* Config Methods */
+#define WPS_CONFIG_USBA 0x0001
+#define WPS_CONFIG_ETHERNET 0x0002
+#define WPS_CONFIG_LABEL 0x0004
+#define WPS_CONFIG_DISPLAY 0x0008
+#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010
+#define WPS_CONFIG_INT_NFC_TOKEN 0x0020
+#define WPS_CONFIG_NFC_INTERFACE 0x0040
+#define WPS_CONFIG_PUSHBUTTON 0x0080
+#define WPS_CONFIG_KEYPAD 0x0100
+#ifdef CONFIG_WPS2
+#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
+#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
+#define WPS_CONFIG_VIRT_DISPLAY 0x2008
+#define WPS_CONFIG_PHY_DISPLAY 0x4008
+#endif /* CONFIG_WPS2 */
+
+/* Connection Type Flags */
+#define WPS_CONN_ESS 0x01
+#define WPS_CONN_IBSS 0x02
+
+/* Wi-Fi Protected Setup State */
+enum wps_state {
+ WPS_STATE_NOT_CONFIGURED = 1,
+ WPS_STATE_CONFIGURED = 2
+};
+
+/* Association State */
+enum wps_assoc_state {
+ WPS_ASSOC_NOT_ASSOC = 0,
+ WPS_ASSOC_CONN_SUCCESS = 1,
+ WPS_ASSOC_CFG_FAILURE = 2,
+ WPS_ASSOC_FAILURE = 3,
+ WPS_ASSOC_IP_FAILURE = 4
+};
+
+
+#define WPS_DEV_OUI_WFA 0x0050f204
+
+enum wps_dev_categ {
+ WPS_DEV_COMPUTER = 1,
+ WPS_DEV_INPUT = 2,
+ WPS_DEV_PRINTER = 3,
+ WPS_DEV_CAMERA = 4,
+ WPS_DEV_STORAGE = 5,
+ WPS_DEV_NETWORK_INFRA = 6,
+ WPS_DEV_DISPLAY = 7,
+ WPS_DEV_MULTIMEDIA = 8,
+ WPS_DEV_GAMING = 9,
+ WPS_DEV_PHONE = 10
+};
+
+enum wps_dev_subcateg {
+ WPS_DEV_COMPUTER_PC = 1,
+ WPS_DEV_COMPUTER_SERVER = 2,
+ WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
+ WPS_DEV_PRINTER_PRINTER = 1,
+ WPS_DEV_PRINTER_SCANNER = 2,
+ WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
+ WPS_DEV_STORAGE_NAS = 1,
+ WPS_DEV_NETWORK_INFRA_AP = 1,
+ WPS_DEV_NETWORK_INFRA_ROUTER = 2,
+ WPS_DEV_NETWORK_INFRA_SWITCH = 3,
+ WPS_DEV_DISPLAY_TV = 1,
+ WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
+ WPS_DEV_DISPLAY_PROJECTOR = 3,
+ WPS_DEV_MULTIMEDIA_DAR = 1,
+ WPS_DEV_MULTIMEDIA_PVR = 2,
+ WPS_DEV_MULTIMEDIA_MCX = 3,
+ WPS_DEV_GAMING_XBOX = 1,
+ WPS_DEV_GAMING_XBOX360 = 2,
+ WPS_DEV_GAMING_PLAYSTATION = 3,
+ WPS_DEV_PHONE_WINDOWS_MOBILE = 1
+};
+
+
+/* Request Type */
+enum wps_request_type {
+ WPS_REQ_ENROLLEE_INFO = 0,
+ WPS_REQ_ENROLLEE = 1,
+ WPS_REQ_REGISTRAR = 2,
+ WPS_REQ_WLAN_MANAGER_REGISTRAR = 3
+};
+
+/* Response Type */
+enum wps_response_type {
+ WPS_RESP_ENROLLEE_INFO = 0,
+ WPS_RESP_ENROLLEE = 1,
+ WPS_RESP_REGISTRAR = 2,
+ WPS_RESP_AP = 3
+};
+
+/* Walk Time for push button configuration (in seconds) */
+#define WPS_PBC_WALK_TIME 120
+
+#define WPS_MAX_AUTHORIZED_MACS 5
+
+#endif /* WPS_DEFS_H */
diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c
new file mode 100644
index 0000000..f2fb03a
--- /dev/null
+++ b/src/wps/wps_dev_attr.c
@@ -0,0 +1,444 @@
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Manufacturer");
+ wpabuf_put_be16(msg, ATTR_MANUFACTURER);
+ len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
+#ifndef CONFIG_WPS_STRICT
+ if (len == 0) {
+ /*
+ * Some deployed WPS implementations fail to parse zero-length
+ * attributes. As a workaround, send a space character if the
+ * device attribute string is empty.
+ */
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->manufacturer, len);
+ return 0;
+}
+
+
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Model Name");
+ wpabuf_put_be16(msg, ATTR_MODEL_NAME);
+ len = dev->model_name ? os_strlen(dev->model_name) : 0;
+#ifndef CONFIG_WPS_STRICT
+ if (len == 0) {
+ /*
+ * Some deployed WPS implementations fail to parse zero-length
+ * attributes. As a workaround, send a space character if the
+ * device attribute string is empty.
+ */
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->model_name, len);
+ return 0;
+}
+
+
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Model Number");
+ wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
+ len = dev->model_number ? os_strlen(dev->model_number) : 0;
+#ifndef CONFIG_WPS_STRICT
+ if (len == 0) {
+ /*
+ * Some deployed WPS implementations fail to parse zero-length
+ * attributes. As a workaround, send a space character if the
+ * device attribute string is empty.
+ */
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->model_number, len);
+ return 0;
+}
+
+
+static int wps_build_serial_number(struct wps_device_data *dev,
+ struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Serial Number");
+ wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
+ len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
+#ifndef CONFIG_WPS_STRICT
+ if (len == 0) {
+ /*
+ * Some deployed WPS implementations fail to parse zero-length
+ * attributes. As a workaround, send a space character if the
+ * device attribute string is empty.
+ */
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->serial_number, len);
+ return 0;
+}
+
+
+int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Primary Device Type");
+ wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE);
+ wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
+ wpabuf_put_data(msg, dev->pri_dev_type, WPS_DEV_TYPE_LEN);
+ return 0;
+}
+
+
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+ struct wpabuf *msg)
+{
+ if (!dev->num_sec_dev_types)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Secondary Device Type");
+ wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST);
+ wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+ wpabuf_put_data(msg, dev->sec_dev_type,
+ WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+
+ return 0;
+}
+
+
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_req_dev_types; i++) {
+ wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type",
+ req_dev_types + i * WPS_DEV_TYPE_LEN,
+ WPS_DEV_TYPE_LEN);
+ wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE);
+ wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
+ wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN,
+ WPS_DEV_TYPE_LEN);
+ }
+
+ return 0;
+}
+
+
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ size_t len;
+ wpa_printf(MSG_DEBUG, "WPS: * Device Name");
+ wpabuf_put_be16(msg, ATTR_DEV_NAME);
+ len = dev->device_name ? os_strlen(dev->device_name) : 0;
+#ifndef CONFIG_WPS_STRICT
+ if (len == 0) {
+ /*
+ * Some deployed WPS implementations fail to parse zero-length
+ * attributes. As a workaround, send a space character if the
+ * device attribute string is empty.
+ */
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->device_name, len);
+ return 0;
+}
+
+
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ if (wps_build_manufacturer(dev, msg) ||
+ wps_build_model_name(dev, msg) ||
+ wps_build_model_number(dev, msg) ||
+ wps_build_serial_number(dev, msg) ||
+ wps_build_primary_dev_type(dev, msg) ||
+ wps_build_dev_name(dev, msg))
+ return -1;
+ return 0;
+}
+
+
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * OS Version");
+ wpabuf_put_be16(msg, ATTR_OS_VERSION);
+ wpabuf_put_be16(msg, 4);
+ wpabuf_put_be32(msg, 0x80000000 | dev->os_version);
+ return 0;
+}
+
+
+int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands);
+ wpabuf_put_be16(msg, ATTR_RF_BANDS);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, dev->rf_bands);
+ return 0;
+}
+
+
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ int i;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ if (dev->vendor_ext[i] == NULL)
+ continue;
+ wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension",
+ wpabuf_head_u8(dev->vendor_ext[i]),
+ wpabuf_len(dev->vendor_ext[i]));
+ wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+ wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i]));
+ wpabuf_put_buf(msg, dev->vendor_ext[i]);
+ }
+
+ return 0;
+}
+
+
+static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
+ size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len);
+
+ os_free(dev->manufacturer);
+ dev->manufacturer = os_malloc(str_len + 1);
+ if (dev->manufacturer == NULL)
+ return -1;
+ os_memcpy(dev->manufacturer, str, str_len);
+ dev->manufacturer[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_model_name(struct wps_device_data *dev, const u8 *str,
+ size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Model Name received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len);
+
+ os_free(dev->model_name);
+ dev->model_name = os_malloc(str_len + 1);
+ if (dev->model_name == NULL)
+ return -1;
+ os_memcpy(dev->model_name, str, str_len);
+ dev->model_name[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_model_number(struct wps_device_data *dev, const u8 *str,
+ size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Model Number received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len);
+
+ os_free(dev->model_number);
+ dev->model_number = os_malloc(str_len + 1);
+ if (dev->model_number == NULL)
+ return -1;
+ os_memcpy(dev->model_number, str, str_len);
+ dev->model_number[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_serial_number(struct wps_device_data *dev,
+ const u8 *str, size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Serial Number received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len);
+
+ os_free(dev->serial_number);
+ dev->serial_number = os_malloc(str_len + 1);
+ if (dev->serial_number == NULL)
+ return -1;
+ os_memcpy(dev->serial_number, str, str_len);
+ dev->serial_number[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str,
+ size_t str_len)
+{
+ if (str == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Name received");
+ return -1;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len);
+
+ os_free(dev->device_name);
+ dev->device_name = os_malloc(str_len + 1);
+ if (dev->device_name == NULL)
+ return -1;
+ os_memcpy(dev->device_name, str, str_len);
+ dev->device_name[str_len] = '\0';
+
+ return 0;
+}
+
+
+static int wps_process_primary_dev_type(struct wps_device_data *dev,
+ const u8 *dev_type)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+ if (dev_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received");
+ return -1;
+ }
+
+ os_memcpy(dev->pri_dev_type, dev_type, WPS_DEV_TYPE_LEN);
+ wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: %s",
+ wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+ sizeof(devtype)));
+
+ return 0;
+}
+
+
+int wps_process_device_attrs(struct wps_device_data *dev,
+ struct wps_parse_attr *attr)
+{
+ if (wps_process_manufacturer(dev, attr->manufacturer,
+ attr->manufacturer_len) ||
+ wps_process_model_name(dev, attr->model_name,
+ attr->model_name_len) ||
+ wps_process_model_number(dev, attr->model_number,
+ attr->model_number_len) ||
+ wps_process_serial_number(dev, attr->serial_number,
+ attr->serial_number_len) ||
+ wps_process_primary_dev_type(dev, attr->primary_dev_type) ||
+ wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len))
+ return -1;
+ return 0;
+}
+
+
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver)
+{
+ if (ver == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No OS Version received");
+ return -1;
+ }
+
+ dev->os_version = WPA_GET_BE32(ver);
+ wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version);
+
+ return 0;
+}
+
+
+int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
+{
+ if (bands == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No RF Bands received");
+ return -1;
+ }
+
+ dev->rf_bands = *bands;
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", dev->rf_bands);
+
+ return 0;
+}
+
+
+void wps_device_data_dup(struct wps_device_data *dst,
+ const struct wps_device_data *src)
+{
+ if (src->device_name)
+ dst->device_name = os_strdup(src->device_name);
+ if (src->manufacturer)
+ dst->manufacturer = os_strdup(src->manufacturer);
+ if (src->model_name)
+ dst->model_name = os_strdup(src->model_name);
+ if (src->model_number)
+ dst->model_number = os_strdup(src->model_number);
+ if (src->serial_number)
+ dst->serial_number = os_strdup(src->serial_number);
+ os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
+ dst->os_version = src->os_version;
+ dst->rf_bands = src->rf_bands;
+}
+
+
+void wps_device_data_free(struct wps_device_data *dev)
+{
+ os_free(dev->device_name);
+ dev->device_name = NULL;
+ os_free(dev->manufacturer);
+ dev->manufacturer = NULL;
+ os_free(dev->model_name);
+ dev->model_name = NULL;
+ os_free(dev->model_number);
+ dev->model_number = NULL;
+ os_free(dev->serial_number);
+ dev->serial_number = NULL;
+}
diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h
new file mode 100644
index 0000000..f26a05b
--- /dev/null
+++ b/src/wps/wps_dev_attr.h
@@ -0,0 +1,44 @@
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPS_DEV_ATTR_H
+#define WPS_DEV_ATTR_H
+
+struct wps_parse_attr;
+
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_primary_dev_type(struct wps_device_data *dev,
+ struct wpabuf *msg);
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+ struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_process_device_attrs(struct wps_device_data *dev,
+ struct wps_parse_attr *attr);
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
+int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
+void wps_device_data_dup(struct wps_device_data *dst,
+ const struct wps_device_data *src);
+void wps_device_data_free(struct wps_device_data *dev);
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types);
+
+#endif /* WPS_DEV_ATTR_H */
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
new file mode 100644
index 0000000..390254e
--- /dev/null
+++ b/src/wps/wps_enrollee.c
@@ -0,0 +1,1350 @@
+/*
+ * Wi-Fi Protected Setup - Enrollee
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * MAC Address");
+ wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+ wpabuf_put_be16(msg, ETH_ALEN);
+ wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN);
+ return 0;
+}
+
+
+static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 state;
+ if (wps->wps->ap)
+ state = wps->wps->wps_state;
+ else
+ state = WPS_STATE_NOT_CONFIGURED;
+ wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State (%d)",
+ state);
+ wpabuf_put_be16(msg, ATTR_WPS_STATE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, state);
+ return 0;
+}
+
+
+static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 *hash;
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
+ wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+ if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
+ "E-Hash derivation");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: * E-Hash1");
+ wpabuf_put_be16(msg, ATTR_E_HASH1);
+ wpabuf_put_be16(msg, SHA256_MAC_LEN);
+ hash = wpabuf_put(msg, SHA256_MAC_LEN);
+ /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+ addr[0] = wps->snonce;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk1;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPS: * E-Hash2");
+ wpabuf_put_be16(msg, ATTR_E_HASH2);
+ wpabuf_put_be16(msg, SHA256_MAC_LEN);
+ hash = wpabuf_put(msg, SHA256_MAC_LEN);
+ /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+ addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk2;
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
+
+ return 0;
+}
+
+
+static int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * E-SNonce1");
+ wpabuf_put_be16(msg, ATTR_E_SNONCE1);
+ wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+ wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+ return 0;
+}
+
+
+static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * E-SNonce2");
+ wpabuf_put_be16(msg, ATTR_E_SNONCE2);
+ wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+ wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+ WPS_SECRET_NONCE_LEN);
+ return 0;
+}
+
+
+static struct wpabuf * wps_build_m1(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+ u16 config_methods;
+
+ if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+ wps->nonce_e, WPS_NONCE_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ config_methods = wps->wps->config_methods;
+ if (wps->wps->ap) {
+ /*
+ * These are the methods that the AP supports as an Enrollee
+ * for adding external Registrars, so remove PushButton.
+ */
+ config_methods &= ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M1) ||
+ wps_build_uuid_e(msg, wps->uuid_e) ||
+ wps_build_mac_addr(wps, msg) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_public_key(wps, msg) ||
+ wps_build_auth_type_flags(wps, msg) ||
+ wps_build_encr_type_flags(wps, msg) ||
+ wps_build_conn_type_flags(wps, msg) ||
+ wps_build_config_methods(msg, config_methods) ||
+ wps_build_wps_state(wps, msg) ||
+ wps_build_device_attrs(&wps->wps->dev, msg) ||
+ wps_build_rf_bands(&wps->wps->dev, msg) ||
+ wps_build_assoc_state(wps, msg) ||
+ wps_build_dev_password_id(msg, wps->dev_pw_id) ||
+ wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
+ wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->state = RECV_M2;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m3(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
+
+ if (wps->dev_password == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
+ return NULL;
+ }
+ wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M3) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_e_hash(wps, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->state = RECV_M4;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m5(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M5");
+
+ plain = wpabuf_alloc(200);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M5) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_e_snonce1(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->state = RECV_M6;
+ return msg;
+}
+
+
+static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * SSID");
+ wpabuf_put_be16(msg, ATTR_SSID);
+ wpabuf_put_be16(msg, wps->wps->ssid_len);
+ wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
+ return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type");
+ wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, wps->wps->auth_types);
+ return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type");
+ wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, wps->wps->encr_types);
+ return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Network Key");
+ wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+ wpabuf_put_be16(msg, wps->wps->network_key_len);
+ wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
+ return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * MAC Address (AP BSSID)");
+ wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+ wpabuf_put_be16(msg, ETH_ALEN);
+ wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
+ return 0;
+}
+
+
+static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
+{
+ if (wps->wps->ap_settings) {
+ wpa_printf(MSG_DEBUG, "WPS: * AP Settings (pre-configured)");
+ wpabuf_put_data(plain, wps->wps->ap_settings,
+ wps->wps->ap_settings_len);
+ return 0;
+ }
+
+ return wps_build_cred_ssid(wps, plain) ||
+ wps_build_cred_mac_addr(wps, plain) ||
+ wps_build_cred_auth_type(wps, plain) ||
+ wps_build_cred_encr_type(wps, plain) ||
+ wps_build_cred_network_key(wps, plain);
+}
+
+
+static struct wpabuf * wps_build_m7(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
+
+ plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M7) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_e_snonce2(wps, plain) ||
+ (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ if (wps->wps->ap && wps->wps->registrar) {
+ /*
+ * If the Registrar is only learning our current configuration,
+ * it may not continue protocol run to successful completion.
+ * Store information here to make sure it remains available.
+ */
+ wps_device_store(wps->wps->registrar, &wps->peer_dev,
+ wps->uuid_r);
+ }
+
+ wps->state = RECV_M8;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_DONE) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ if (wps->wps->ap)
+ wps->state = RECV_ACK;
+ else {
+ wps_success_event(wps->wps);
+ wps->state = WPS_FINISHED;
+ }
+ return msg;
+}
+
+
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
+ enum wsc_op_code *op_code)
+{
+ struct wpabuf *msg;
+
+ switch (wps->state) {
+ case SEND_M1:
+ msg = wps_build_m1(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M3:
+ msg = wps_build_m3(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M5:
+ msg = wps_build_m5(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M7:
+ msg = wps_build_m7(wps);
+ *op_code = WSC_MSG;
+ break;
+ case RECEIVED_M2D:
+ if (wps->wps->ap) {
+ msg = wps_build_wsc_nack(wps);
+ *op_code = WSC_NACK;
+ break;
+ }
+ msg = wps_build_wsc_ack(wps);
+ *op_code = WSC_ACK;
+ if (msg) {
+ /* Another M2/M2D may be received */
+ wps->state = RECV_M2;
+ }
+ break;
+ case SEND_WSC_NACK:
+ msg = wps_build_wsc_nack(wps);
+ *op_code = WSC_NACK;
+ break;
+ case WPS_MSG_DONE:
+ msg = wps_build_wsc_done(wps);
+ *op_code = WSC_Done;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
+ "a message", wps->state);
+ msg = NULL;
+ break;
+ }
+
+ if (*op_code == WSC_MSG && msg) {
+ /* Save a copy of the last message for Authenticator derivation
+ */
+ wpabuf_free(wps->last_msg);
+ wps->last_msg = wpabuf_dup(msg);
+ }
+
+ return msg;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+ if (r_nonce == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
+ return -1;
+ }
+
+ os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+ wps->nonce_r, WPS_NONCE_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+ if (e_nonce == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
+ return -1;
+ }
+
+ if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
+{
+ if (uuid_r == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
+ return -1;
+ }
+
+ os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+ size_t pk_len)
+{
+ if (pk == NULL || pk_len == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
+ return -1;
+ }
+
+#ifdef CONFIG_WPS_OOB
+ if (wps->dev_pw_id != DEV_PW_DEFAULT &&
+ wps->wps->oob_conf.pubkey_hash) {
+ const u8 *addr[1];
+ u8 hash[WPS_HASH_LEN];
+
+ addr[0] = pk;
+ sha256_vector(1, addr, &pk_len, hash);
+ if (os_memcmp(hash,
+ wpabuf_head(wps->wps->oob_conf.pubkey_hash),
+ WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "WPS: Public Key hash error");
+ return -1;
+ }
+ }
+#endif /* CONFIG_WPS_OOB */
+
+ wpabuf_free(wps->dh_pubkey_r);
+ wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
+ if (wps->dh_pubkey_r == NULL)
+ return -1;
+
+ if (wps_derive_keys(wps) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
+{
+ if (r_hash1 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
+ return -1;
+ }
+
+ os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
+{
+ if (r_hash2 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
+ return -1;
+ }
+
+ os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (r_snonce1 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
+ WPS_SECRET_NONCE_LEN);
+
+ /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+ addr[0] = r_snonce1;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk1;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+ if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
+ "not match with the pre-committed value");
+ wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+ wps_pwd_auth_fail_event(wps->wps, 1, 1);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
+ "half of the device password");
+
+ return 0;
+}
+
+
+static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (r_snonce2 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
+ WPS_SECRET_NONCE_LEN);
+
+ /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+ addr[0] = r_snonce2;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk2;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+ if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
+ "not match with the pre-committed value");
+ wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+ wps_pwd_auth_fail_event(wps->wps, 1, 2);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
+ "half of the device password");
+
+ return 0;
+}
+
+
+static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
+ size_t cred_len, int wps2)
+{
+ struct wps_parse_attr attr;
+ struct wpabuf msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received Credential");
+ os_memset(&wps->cred, 0, sizeof(wps->cred));
+ wpabuf_set(&msg, cred, cred_len);
+ if (wps_parse_msg(&msg, &attr) < 0 ||
+ wps_process_cred(&attr, &wps->cred))
+ return -1;
+
+ if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
+ 0) {
+ wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential ("
+ MACSTR ") does not match with own address (" MACSTR
+ ")", MAC2STR(wps->cred.mac_addr),
+ MAC2STR(wps->wps->dev.mac_addr));
+ /*
+ * In theory, this could be consider fatal error, but there are
+ * number of deployed implementations using other address here
+ * due to unclarity in the specification. For interoperability
+ * reasons, allow this to be processed since we do not really
+ * use the MAC Address information for anything.
+ */
+#ifdef CONFIG_WPS_STRICT
+ if (wps2) {
+ wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
+ "MAC Address in AP Settings");
+ return -1;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ }
+
+#ifdef CONFIG_WPS2
+ if (!(wps->cred.encr_type &
+ (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+ if (wps->cred.encr_type & WPS_ENCR_WEP) {
+ wpa_printf(MSG_INFO, "WPS: Reject Credential "
+ "due to WEP configuration");
+ wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
+ return -2;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
+ "invalid encr_type 0x%x", wps->cred.encr_type);
+ return -1;
+ }
+#endif /* CONFIG_WPS2 */
+
+ if (wps->wps->cred_cb) {
+ wps->cred.cred_attr = cred - 4;
+ wps->cred.cred_attr_len = cred_len + 4;
+ wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
+ wps->cred.cred_attr = NULL;
+ wps->cred.cred_attr_len = 0;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
+ size_t cred_len[], size_t num_cred, int wps2)
+{
+ size_t i;
+ int ok = 0;
+
+ if (wps->wps->ap)
+ return 0;
+
+ if (num_cred == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
+ "received");
+ return -1;
+ }
+
+ for (i = 0; i < num_cred; i++) {
+ int res;
+ res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
+ if (res == 0)
+ ok++;
+ else if (res == -2)
+ wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped");
+ else
+ return -1;
+ }
+
+ if (ok == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute "
+ "received");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_ap_settings_e(struct wps_data *wps,
+ struct wps_parse_attr *attr,
+ struct wpabuf *attrs, int wps2)
+{
+ struct wps_credential cred;
+
+ if (!wps->wps->ap)
+ return 0;
+
+ if (wps_process_ap_settings(attr, &cred) < 0)
+ return -1;
+
+ wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
+ "Registrar");
+
+ if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
+ 0) {
+ wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
+ MACSTR ") does not match with own address (" MACSTR
+ ")", MAC2STR(cred.mac_addr),
+ MAC2STR(wps->wps->dev.mac_addr));
+ /*
+ * In theory, this could be consider fatal error, but there are
+ * number of deployed implementations using other address here
+ * due to unclarity in the specification. For interoperability
+ * reasons, allow this to be processed since we do not really
+ * use the MAC Address information for anything.
+ */
+#ifdef CONFIG_WPS_STRICT
+ if (wps2) {
+ wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
+ "MAC Address in AP Settings");
+ return -1;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ }
+
+#ifdef CONFIG_WPS2
+ if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
+ {
+ if (cred.encr_type & WPS_ENCR_WEP) {
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
+ "due to WEP configuration");
+ wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+ "invalid encr_type 0x%x", cred.encr_type);
+ return -1;
+ }
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_WPS_STRICT
+ if (wps2) {
+ if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
+ WPS_ENCR_TKIP ||
+ (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+ WPS_AUTH_WPAPSK) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
+ "AP Settings: WPA-Personal/TKIP only");
+ wps->error_indication =
+ WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
+ return -1;
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
+
+#ifdef CONFIG_WPS2
+ if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
+ "TKIP+AES");
+ cred.encr_type |= WPS_ENCR_AES;
+ }
+
+ if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+ WPS_AUTH_WPAPSK) {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
+ "WPAPSK+WPA2PSK");
+ cred.auth_type |= WPS_AUTH_WPA2PSK;
+ }
+#endif /* CONFIG_WPS2 */
+
+ if (wps->wps->cred_cb) {
+ cred.cred_attr = wpabuf_head(attrs);
+ cred.cred_attr_len = wpabuf_len(attrs);
+ wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+ }
+
+ return 0;
+}
+
+
+static enum wps_process_res wps_process_m2(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Received M2");
+
+ if (wps->state != RECV_M2) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M2", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+ wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_uuid_r(wps, attr->uuid_r)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ /*
+ * Stop here on an AP as an Enrollee if AP Setup is locked unless the
+ * special locked mode is used to allow protocol run up to M7 in order
+ * to support external Registrars that only learn the current AP
+ * configuration without changing it.
+ */
+ if (wps->wps->ap &&
+ ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
+ wps->dev_password == NULL)) {
+ wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
+ "registration of a new Registrar");
+ wps->config_error = WPS_CFG_SETUP_LOCKED;
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+ wps_process_authenticator(wps, attr->authenticator, msg) ||
+ wps_process_device_attrs(&wps->peer_dev, attr)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wps->state = SEND_M3;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m2d(struct wps_data *wps,
+ struct wps_parse_attr *attr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Received M2D");
+
+ if (wps->state != RECV_M2) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M2D", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
+ attr->manufacturer, attr->manufacturer_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
+ attr->model_name, attr->model_name_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
+ attr->model_number, attr->model_number_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
+ attr->serial_number, attr->serial_number_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
+ attr->dev_name, attr->dev_name_len);
+
+ if (wps->wps->event_cb) {
+ union wps_event_data data;
+ struct wps_event_m2d *m2d = &data.m2d;
+ os_memset(&data, 0, sizeof(data));
+ if (attr->config_methods)
+ m2d->config_methods =
+ WPA_GET_BE16(attr->config_methods);
+ m2d->manufacturer = attr->manufacturer;
+ m2d->manufacturer_len = attr->manufacturer_len;
+ m2d->model_name = attr->model_name;
+ m2d->model_name_len = attr->model_name_len;
+ m2d->model_number = attr->model_number;
+ m2d->model_number_len = attr->model_number_len;
+ m2d->serial_number = attr->serial_number;
+ m2d->serial_number_len = attr->serial_number_len;
+ m2d->dev_name = attr->dev_name;
+ m2d->dev_name_len = attr->dev_name_len;
+ m2d->primary_dev_type = attr->primary_dev_type;
+ if (attr->config_error)
+ m2d->config_error =
+ WPA_GET_BE16(attr->config_error);
+ if (attr->dev_password_id)
+ m2d->dev_password_id =
+ WPA_GET_BE16(attr->dev_password_id);
+ wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
+ }
+
+ wps->state = RECEIVED_M2D;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m4(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M4");
+
+ if (wps->state != RECV_M4) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M4", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg) ||
+ wps_process_r_hash1(wps, attr->r_hash1) ||
+ wps_process_r_hash2(wps, attr->r_hash2)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_validate_m4_encr(decrypted, attr->version2 != 0) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+ wps_process_r_snonce1(wps, eattr.r_snonce1)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = SEND_M5;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m6(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M6");
+
+ if (wps->state != RECV_M6) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M6", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_validate_m6_encr(decrypted, attr->version2 != 0) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+ wps_process_r_snonce2(wps, eattr.r_snonce2)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = SEND_M7;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m8(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M8");
+
+ if (wps->state != RECV_M8) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M8", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps->wps->ap && wps->wps->ap_setup_locked) {
+ /*
+ * Stop here if special ap_setup_locked == 2 mode allowed the
+ * protocol to continue beyond M2. This allows ER to learn the
+ * current AP settings without changing them.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
+ "registration of a new Registrar");
+ wps->config_error = WPS_CFG_SETUP_LOCKED;
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_validate_m8_encr(decrypted, wps->wps->ap, attr->version2 != 0)
+ < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+ wps_process_creds(wps, eattr.cred, eattr.cred_len,
+ eattr.num_cred, attr->version2 != NULL) ||
+ wps_process_ap_settings_e(wps, &eattr, decrypted,
+ attr->version2 != NULL)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = WPS_MSG_DONE;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+ enum wps_process_res ret = WPS_CONTINUE;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ switch (*attr.msg_type) {
+ case WPS_M2:
+ if (wps_validate_m2(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m2(wps, msg, &attr);
+ break;
+ case WPS_M2D:
+ if (wps_validate_m2d(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m2d(wps, &attr);
+ break;
+ case WPS_M4:
+ if (wps_validate_m4(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m4(wps, msg, &attr);
+ if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+ wps_fail_event(wps->wps, WPS_M4, wps->config_error,
+ wps->error_indication);
+ break;
+ case WPS_M6:
+ if (wps_validate_m6(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m6(wps, msg, &attr);
+ if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+ wps_fail_event(wps->wps, WPS_M6, wps->config_error,
+ wps->error_indication);
+ break;
+ case WPS_M8:
+ if (wps_validate_m8(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m8(wps, msg, &attr);
+ if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+ wps_fail_event(wps->wps, WPS_M8, wps->config_error,
+ wps->error_indication);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ /*
+ * Save a copy of the last message for Authenticator derivation if we
+ * are continuing. However, skip M2D since it is not authenticated and
+ * neither is the ACK/NACK response frame. This allows the possibly
+ * following M2 to be processed correctly by using the previously sent
+ * M1 in Authenticator derivation.
+ */
+ if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) {
+ /* Save a copy of the last message for Authenticator derivation
+ */
+ wpabuf_free(wps->last_msg);
+ wps->last_msg = wpabuf_dup(msg);
+ }
+
+ return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_ACK) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ if (wps->state == RECV_ACK && wps->wps->ap) {
+ wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
+ "completed successfully");
+ wps_success_event(wps->wps);
+ wps->state = WPS_FINISHED;
+ return WPS_DONE;
+ }
+
+ return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+ u16 config_error;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_NACK) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
+ attr.registrar_nonce, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
+ wps->nonce_r, WPS_NONCE_LEN);
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
+ attr.enrollee_nonce, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
+ wps->nonce_e, WPS_NONCE_LEN);
+ return WPS_FAILURE;
+ }
+
+ if (attr.config_error == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
+ "in WSC_NACK");
+ return WPS_FAILURE;
+ }
+
+ config_error = WPA_GET_BE16(attr.config_error);
+ wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
+ "Configuration Error %d", config_error);
+
+ switch (wps->state) {
+ case RECV_M4:
+ wps_fail_event(wps->wps, WPS_M3, config_error,
+ wps->error_indication);
+ break;
+ case RECV_M6:
+ wps_fail_event(wps->wps, WPS_M5, config_error,
+ wps->error_indication);
+ break;
+ case RECV_M8:
+ wps_fail_event(wps->wps, WPS_M7, config_error,
+ wps->error_indication);
+ break;
+ default:
+ break;
+ }
+
+ /* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
+ * Enrollee is Authenticator */
+ wps->state = SEND_WSC_NACK;
+
+ return WPS_FAILURE;
+}
+
+
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
+ enum wsc_op_code op_code,
+ const struct wpabuf *msg)
+{
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
+ "op_code=%d)",
+ (unsigned long) wpabuf_len(msg), op_code);
+
+ if (op_code == WSC_UPnP) {
+ /* Determine the OpCode based on message type attribute */
+ struct wps_parse_attr attr;
+ if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
+ if (*attr.msg_type == WPS_WSC_ACK)
+ op_code = WSC_ACK;
+ else if (*attr.msg_type == WPS_WSC_NACK)
+ op_code = WSC_NACK;
+ }
+ }
+
+ switch (op_code) {
+ case WSC_MSG:
+ case WSC_UPnP:
+ return wps_process_wsc_msg(wps, msg);
+ case WSC_ACK:
+ if (wps_validate_wsc_ack(msg) < 0)
+ return WPS_FAILURE;
+ return wps_process_wsc_ack(wps, msg);
+ case WSC_NACK:
+ if (wps_validate_wsc_nack(msg) < 0)
+ return WPS_FAILURE;
+ return wps_process_wsc_nack(wps, msg);
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
+ return WPS_FAILURE;
+ }
+}
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
new file mode 100644
index 0000000..a461836
--- /dev/null
+++ b/src/wps/wps_er.c
@@ -0,0 +1,1959 @@
+/*
+ * Wi-Fi Protected Setup - External Registrar
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "uuid.h"
+#include "eloop.h"
+#include "httpread.h"
+#include "http_client.h"
+#include "http_server.h"
+#include "upnp_xml.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+#include "wps_er.h"
+
+
+static void wps_er_deinit_finish(void *eloop_data, void *user_ctx);
+static void wps_er_ap_timeout(void *eloop_data, void *user_ctx);
+static void wps_er_sta_timeout(void *eloop_data, void *user_ctx);
+static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg);
+static int wps_er_send_get_device_info(struct wps_er_ap *ap,
+ void (*m1_handler)(struct wps_er_ap *ap,
+ struct wpabuf *m1));
+
+
+static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta,
+ enum wps_event event)
+{
+ union wps_event_data data;
+ struct wps_event_er_enrollee *ev = &data.enrollee;
+
+ if (wps->event_cb == NULL)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ ev->uuid = sta->uuid;
+ ev->mac_addr = sta->addr;
+ ev->m1_received = sta->m1_received;
+ ev->config_methods = sta->config_methods;
+ ev->dev_passwd_id = sta->dev_passwd_id;
+ ev->pri_dev_type = sta->pri_dev_type;
+ ev->dev_name = sta->dev_name;
+ ev->manufacturer = sta->manufacturer;
+ ev->model_name = sta->model_name;
+ ev->model_number = sta->model_number;
+ ev->serial_number = sta->serial_number;
+ wps->event_cb(wps->cb_ctx, event, &data);
+}
+
+
+static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr,
+ const u8 *uuid)
+{
+ struct wps_er_sta *sta;
+ dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) {
+ if ((addr == NULL ||
+ os_memcmp(sta->addr, addr, ETH_ALEN) == 0) &&
+ (uuid == NULL ||
+ os_memcmp(uuid, sta->uuid, WPS_UUID_LEN) == 0))
+ return sta;
+ }
+ return NULL;
+}
+
+
+static void wps_er_sta_free(struct wps_er_sta *sta)
+{
+ wps_er_sta_event(sta->ap->er->wps, sta, WPS_EV_ER_ENROLLEE_REMOVE);
+ if (sta->wps)
+ wps_deinit(sta->wps);
+ os_free(sta->manufacturer);
+ os_free(sta->model_name);
+ os_free(sta->model_number);
+ os_free(sta->serial_number);
+ os_free(sta->dev_name);
+ http_client_free(sta->http);
+ eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
+ os_free(sta->cred);
+ os_free(sta);
+}
+
+
+static void wps_er_sta_remove_all(struct wps_er_ap *ap)
+{
+ struct wps_er_sta *prev, *sta;
+ dl_list_for_each_safe(sta, prev, &ap->sta, struct wps_er_sta, list)
+ wps_er_sta_free(sta);
+}
+
+
+static struct wps_er_ap * wps_er_ap_get(struct wps_er *er,
+ struct in_addr *addr, const u8 *uuid)
+{
+ struct wps_er_ap *ap;
+ dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+ if ((addr == NULL || ap->addr.s_addr == addr->s_addr) &&
+ (uuid == NULL ||
+ os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0))
+ return ap;
+ }
+ return NULL;
+}
+
+
+static struct wps_er_ap * wps_er_ap_get_id(struct wps_er *er, unsigned int id)
+{
+ struct wps_er_ap *ap;
+ dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+ if (ap->id == id)
+ return ap;
+ }
+ return NULL;
+}
+
+
+static void wps_er_ap_event(struct wps_context *wps, struct wps_er_ap *ap,
+ enum wps_event event)
+{
+ union wps_event_data data;
+ struct wps_event_er_ap *evap = &data.ap;
+
+ if (wps->event_cb == NULL)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ evap->uuid = ap->uuid;
+ evap->friendly_name = ap->friendly_name;
+ evap->manufacturer = ap->manufacturer;
+ evap->manufacturer_url = ap->manufacturer_url;
+ evap->model_description = ap->model_description;
+ evap->model_name = ap->model_name;
+ evap->model_number = ap->model_number;
+ evap->model_url = ap->model_url;
+ evap->serial_number = ap->serial_number;
+ evap->upc = ap->upc;
+ evap->pri_dev_type = ap->pri_dev_type;
+ evap->wps_state = ap->wps_state;
+ evap->mac_addr = ap->mac_addr;
+ wps->event_cb(wps->cb_ctx, event, &data);
+}
+
+
+static void wps_er_ap_free(struct wps_er_ap *ap)
+{
+ http_client_free(ap->http);
+ ap->http = NULL;
+
+ os_free(ap->location);
+ os_free(ap->friendly_name);
+ os_free(ap->manufacturer);
+ os_free(ap->manufacturer_url);
+ os_free(ap->model_description);
+ os_free(ap->model_name);
+ os_free(ap->model_number);
+ os_free(ap->model_url);
+ os_free(ap->serial_number);
+ os_free(ap->udn);
+ os_free(ap->upc);
+
+ os_free(ap->scpd_url);
+ os_free(ap->control_url);
+ os_free(ap->event_sub_url);
+
+ os_free(ap->ap_settings);
+
+ os_free(ap);
+}
+
+
+static void wps_er_ap_unsubscribed(struct wps_er *er, struct wps_er_ap *ap)
+{
+ wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from AP %s (%s)",
+ inet_ntoa(ap->addr), ap->location);
+ dl_list_del(&ap->list);
+ wps_er_ap_free(ap);
+
+ if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) {
+ eloop_cancel_timeout(wps_er_deinit_finish, er, NULL);
+ wps_er_deinit_finish(er, NULL);
+ }
+}
+
+
+static void wps_er_http_unsubscribe_cb(void *ctx, struct http_client *c,
+ enum http_client_event event)
+{
+ struct wps_er_ap *ap = ctx;
+
+ switch (event) {
+ case HTTP_CLIENT_OK:
+ wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from events");
+ ap->subscribed = 0;
+ break;
+ case HTTP_CLIENT_FAILED:
+ case HTTP_CLIENT_INVALID_REPLY:
+ case HTTP_CLIENT_TIMEOUT:
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to unsubscribe from "
+ "events");
+ break;
+ }
+ http_client_free(ap->http);
+ ap->http = NULL;
+
+ /*
+ * Need to get rid of the AP entry regardless of whether we managed to
+ * unsubscribe cleanly or not.
+ */
+ wps_er_ap_unsubscribed(ap->er, ap);
+}
+
+
+static void wps_er_ap_unsubscribe(struct wps_er *er, struct wps_er_ap *ap)
+{
+ struct wpabuf *req;
+ struct sockaddr_in dst;
+ char *url, *path;
+ char sid[100];
+
+ if (ap->event_sub_url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot "
+ "subscribe");
+ goto fail;
+ }
+ if (ap->http) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot "
+ "send subscribe request");
+ goto fail;
+ }
+
+ url = http_client_url_parse(ap->event_sub_url, &dst, &path);
+ if (url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
+ goto fail;
+ }
+
+ req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000);
+ if (req == NULL) {
+ os_free(url);
+ goto fail;
+ }
+ uuid_bin2str(ap->sid, sid, sizeof(sid));
+ wpabuf_printf(req,
+ "UNSUBSCRIBE %s HTTP/1.1\r\n"
+ "HOST: %s:%d\r\n"
+ "SID: uuid:%s\r\n"
+ "\r\n",
+ path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), sid);
+ os_free(url);
+ wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Unsubscription request",
+ wpabuf_head(req), wpabuf_len(req));
+
+ ap->http = http_client_addr(&dst, req, 1000,
+ wps_er_http_unsubscribe_cb, ap);
+ if (ap->http == NULL) {
+ wpabuf_free(req);
+ goto fail;
+ }
+ return;
+
+fail:
+ /*
+ * Need to get rid of the AP entry even when we fail to unsubscribe
+ * cleanly.
+ */
+ wps_er_ap_unsubscribed(ap->er, ap);
+}
+
+
+static struct wps_er_ap_settings * wps_er_ap_get_settings(struct wps_er *er,
+ const u8 *uuid)
+{
+ struct wps_er_ap_settings *s;
+ dl_list_for_each(s, &er->ap_settings, struct wps_er_ap_settings, list)
+ if (os_memcmp(uuid, s->uuid, WPS_UUID_LEN) == 0)
+ return s;
+ return NULL;
+}
+
+
+int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr)
+{
+ struct wps_er_ap *ap;
+ struct wps_er_ap_settings *settings;
+
+ ap = wps_er_ap_get(er, addr, NULL);
+ if (ap == NULL || ap->ap_settings == NULL)
+ return -1;
+
+ settings = wps_er_ap_get_settings(er, ap->uuid);
+ if (!settings) {
+ settings = os_zalloc(sizeof(*settings));
+ if (settings == NULL)
+ return -1;
+ os_memcpy(settings->uuid, ap->uuid, WPS_UUID_LEN);
+ dl_list_add(&er->ap_settings, &settings->list);
+ }
+ os_memcpy(&settings->ap_settings, ap->ap_settings,
+ sizeof(struct wps_credential));
+
+ return 0;
+}
+
+
+static int wps_er_ap_use_cached_settings(struct wps_er *er,
+ struct wps_er_ap *ap)
+{
+ struct wps_er_ap_settings *s;
+
+ if (ap->ap_settings)
+ return 0;
+
+ s = wps_er_ap_get_settings(ap->er, ap->uuid);
+ if (!s)
+ return -1;
+
+ ap->ap_settings = os_malloc(sizeof(*ap->ap_settings));
+ if (ap->ap_settings == NULL)
+ return -1;
+
+ os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings));
+ wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings");
+ return 0;
+}
+
+
+static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap)
+{
+ wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)",
+ inet_ntoa(ap->addr), ap->location);
+ eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
+ wps_er_sta_remove_all(ap);
+ wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_REMOVE);
+ http_client_free(ap->http);
+ ap->http = NULL;
+ if (ap->wps) {
+ wps_deinit(ap->wps);
+ ap->wps = NULL;
+ }
+
+ dl_list_del(&ap->list);
+ if (ap->subscribed) {
+ dl_list_add(&er->ap_unsubscribing, &ap->list);
+ wps_er_ap_unsubscribe(er, ap);
+ } else
+ wps_er_ap_free(ap);
+}
+
+
+static void wps_er_ap_timeout(void *eloop_data, void *user_ctx)
+{
+ struct wps_er *er = eloop_data;
+ struct wps_er_ap *ap = user_ctx;
+ wpa_printf(MSG_DEBUG, "WPS ER: AP advertisement timed out");
+ wps_er_ap_remove_entry(er, ap);
+}
+
+
+static int wps_er_get_sid(struct wps_er_ap *ap, char *sid)
+{
+ char *pos;
+ char txt[100];
+
+ if (!sid) {
+ wpa_printf(MSG_DEBUG, "WPS ER: No SID received from %s (%s)",
+ inet_ntoa(ap->addr), ap->location);
+ return -1;
+ }
+
+ pos = os_strstr(sid, "uuid:");
+ if (!pos) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from "
+ "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location,
+ sid);
+ return -1;
+ }
+
+ pos += 5;
+ if (uuid_str2bin(pos, ap->sid) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from "
+ "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location,
+ sid);
+ return -1;
+ }
+
+ uuid_bin2str(ap->sid, txt, sizeof(txt));
+ wpa_printf(MSG_DEBUG, "WPS ER: SID for subscription with %s (%s): %s",
+ inet_ntoa(ap->addr), ap->location, txt);
+
+ return 0;
+}
+
+
+static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c,
+ enum http_client_event event)
+{
+ struct wps_er_ap *ap = ctx;
+
+ switch (event) {
+ case HTTP_CLIENT_OK:
+ wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events");
+ ap->subscribed = 1;
+ wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID"));
+ wps_er_ap_use_cached_settings(ap->er, ap);
+ wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD);
+ break;
+ case HTTP_CLIENT_FAILED:
+ case HTTP_CLIENT_INVALID_REPLY:
+ case HTTP_CLIENT_TIMEOUT:
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to subscribe to events");
+ break;
+ }
+ http_client_free(ap->http);
+ ap->http = NULL;
+}
+
+
+static void wps_er_subscribe(struct wps_er_ap *ap)
+{
+ struct wpabuf *req;
+ struct sockaddr_in dst;
+ char *url, *path;
+
+ if (ap->event_sub_url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot "
+ "subscribe");
+ return;
+ }
+ if (ap->http) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot "
+ "send subscribe request");
+ return;
+ }
+
+ url = http_client_url_parse(ap->event_sub_url, &dst, &path);
+ if (url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
+ return;
+ }
+
+ req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000);
+ if (req == NULL) {
+ os_free(url);
+ return;
+ }
+ wpabuf_printf(req,
+ "SUBSCRIBE %s HTTP/1.1\r\n"
+ "HOST: %s:%d\r\n"
+ "CALLBACK: <http://%s:%d/event/%u/%u>\r\n"
+ "NT: upnp:event\r\n"
+ "TIMEOUT: Second-%d\r\n"
+ "\r\n",
+ path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port),
+ ap->er->ip_addr_text, ap->er->http_port,
+ ap->er->event_id, ap->id, 1800);
+ os_free(url);
+ wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Subscription request",
+ wpabuf_head(req), wpabuf_len(req));
+
+ ap->http = http_client_addr(&dst, req, 1000, wps_er_http_subscribe_cb,
+ ap);
+ if (ap->http == NULL)
+ wpabuf_free(req);
+}
+
+
+static void wps_er_ap_get_m1(struct wps_er_ap *ap, struct wpabuf *m1)
+{
+ struct wps_parse_attr attr;
+
+ if (wps_parse_msg(m1, &attr) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse M1");
+ return;
+ }
+ if (attr.primary_dev_type)
+ os_memcpy(ap->pri_dev_type, attr.primary_dev_type, 8);
+ if (attr.wps_state)
+ ap->wps_state = *attr.wps_state;
+ if (attr.mac_addr)
+ os_memcpy(ap->mac_addr, attr.mac_addr, ETH_ALEN);
+
+ wps_er_subscribe(ap);
+}
+
+
+static void wps_er_get_device_info(struct wps_er_ap *ap)
+{
+ wps_er_send_get_device_info(ap, wps_er_ap_get_m1);
+}
+
+
+static void wps_er_parse_device_description(struct wps_er_ap *ap,
+ struct wpabuf *reply)
+{
+ /* Note: reply includes null termination after the buffer data */
+ const char *data = wpabuf_head(reply);
+ char *pos;
+
+ wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info",
+ wpabuf_head(reply), wpabuf_len(reply));
+
+ ap->friendly_name = xml_get_first_item(data, "friendlyName");
+ wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name);
+
+ ap->manufacturer = xml_get_first_item(data, "manufacturer");
+ wpa_printf(MSG_DEBUG, "WPS ER: manufacturer='%s'", ap->manufacturer);
+
+ ap->manufacturer_url = xml_get_first_item(data, "manufacturerURL");
+ wpa_printf(MSG_DEBUG, "WPS ER: manufacturerURL='%s'",
+ ap->manufacturer_url);
+
+ ap->model_description = xml_get_first_item(data, "modelDescription");
+ wpa_printf(MSG_DEBUG, "WPS ER: modelDescription='%s'",
+ ap->model_description);
+
+ ap->model_name = xml_get_first_item(data, "modelName");
+ wpa_printf(MSG_DEBUG, "WPS ER: modelName='%s'", ap->model_name);
+
+ ap->model_number = xml_get_first_item(data, "modelNumber");
+ wpa_printf(MSG_DEBUG, "WPS ER: modelNumber='%s'", ap->model_number);
+
+ ap->model_url = xml_get_first_item(data, "modelURL");
+ wpa_printf(MSG_DEBUG, "WPS ER: modelURL='%s'", ap->model_url);
+
+ ap->serial_number = xml_get_first_item(data, "serialNumber");
+ wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number);
+
+ ap->udn = xml_get_first_item(data, "UDN");
+ wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn);
+ pos = os_strstr(ap->udn, "uuid:");
+ if (pos) {
+ pos += 5;
+ if (uuid_str2bin(pos, ap->uuid) < 0)
+ wpa_printf(MSG_DEBUG, "WPS ER: Invalid UUID in UDN");
+ }
+
+ ap->upc = xml_get_first_item(data, "UPC");
+ wpa_printf(MSG_DEBUG, "WPS ER: UPC='%s'", ap->upc);
+
+ ap->scpd_url = http_link_update(
+ xml_get_first_item(data, "SCPDURL"), ap->location);
+ wpa_printf(MSG_DEBUG, "WPS ER: SCPDURL='%s'", ap->scpd_url);
+
+ ap->control_url = http_link_update(
+ xml_get_first_item(data, "controlURL"), ap->location);
+ wpa_printf(MSG_DEBUG, "WPS ER: controlURL='%s'", ap->control_url);
+
+ ap->event_sub_url = http_link_update(
+ xml_get_first_item(data, "eventSubURL"), ap->location);
+ wpa_printf(MSG_DEBUG, "WPS ER: eventSubURL='%s'", ap->event_sub_url);
+}
+
+
+static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c,
+ enum http_client_event event)
+{
+ struct wps_er_ap *ap = ctx;
+ struct wpabuf *reply;
+ int ok = 0;
+
+ switch (event) {
+ case HTTP_CLIENT_OK:
+ reply = http_client_get_body(c);
+ if (reply == NULL)
+ break;
+ wps_er_parse_device_description(ap, reply);
+ ok = 1;
+ break;
+ case HTTP_CLIENT_FAILED:
+ case HTTP_CLIENT_INVALID_REPLY:
+ case HTTP_CLIENT_TIMEOUT:
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to fetch device info");
+ break;
+ }
+ http_client_free(ap->http);
+ ap->http = NULL;
+ if (ok)
+ wps_er_get_device_info(ap);
+}
+
+
+void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
+ const char *location, int max_age)
+{
+ struct wps_er_ap *ap;
+
+ ap = wps_er_ap_get(er, addr, uuid);
+ if (ap) {
+ /* Update advertisement timeout */
+ eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
+ eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
+ return;
+ }
+
+ ap = os_zalloc(sizeof(*ap));
+ if (ap == NULL)
+ return;
+ dl_list_init(&ap->sta);
+ ap->er = er;
+ ap->id = ++er->next_ap_id;
+ ap->location = os_strdup(location);
+ if (ap->location == NULL) {
+ os_free(ap);
+ return;
+ }
+ dl_list_add(&er->ap, &ap->list);
+
+ ap->addr.s_addr = addr->s_addr;
+ os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
+ eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
+
+ wpa_printf(MSG_DEBUG, "WPS ER: Added AP entry for %s (%s)",
+ inet_ntoa(ap->addr), ap->location);
+
+ /* Fetch device description */
+ ap->http = http_client_url(ap->location, NULL, 10000,
+ wps_er_http_dev_desc_cb, ap);
+}
+
+
+void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr)
+{
+ struct wps_er_ap *ap;
+ dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+ if (ap->addr.s_addr == addr->s_addr) {
+ wps_er_ap_remove_entry(er, ap);
+ return;
+ }
+ }
+}
+
+
+static void wps_er_ap_remove_all(struct wps_er *er)
+{
+ struct wps_er_ap *prev, *ap;
+ struct wps_er_ap_settings *prev_s, *s;
+ dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list)
+ wps_er_ap_remove_entry(er, ap);
+ dl_list_for_each_safe(s, prev_s, &er->ap_settings,
+ struct wps_er_ap_settings, list)
+ os_free(s);
+}
+
+
+static void http_put_date(struct wpabuf *buf)
+{
+ wpabuf_put_str(buf, "Date: ");
+ format_date(buf);
+ wpabuf_put_str(buf, "\r\n");
+}
+
+
+static void wps_er_http_resp_not_found(struct http_request *req)
+{
+ struct wpabuf *buf;
+ buf = wpabuf_alloc(200);
+ if (buf == NULL) {
+ http_request_deinit(req);
+ return;
+ }
+
+ wpabuf_put_str(buf,
+ "HTTP/1.1 404 Not Found\r\n"
+ "Server: unspecified, UPnP/1.0, unspecified\r\n"
+ "Connection: close\r\n");
+ http_put_date(buf);
+ wpabuf_put_str(buf, "\r\n");
+ http_request_send_and_deinit(req, buf);
+}
+
+
+static void wps_er_http_resp_ok(struct http_request *req)
+{
+ struct wpabuf *buf;
+ buf = wpabuf_alloc(200);
+ if (buf == NULL) {
+ http_request_deinit(req);
+ return;
+ }
+
+ wpabuf_put_str(buf,
+ "HTTP/1.1 200 OK\r\n"
+ "Server: unspecified, UPnP/1.0, unspecified\r\n"
+ "Connection: close\r\n"
+ "Content-Length: 0\r\n");
+ http_put_date(buf);
+ wpabuf_put_str(buf, "\r\n");
+ http_request_send_and_deinit(req, buf);
+}
+
+
+static void wps_er_sta_timeout(void *eloop_data, void *user_ctx)
+{
+ struct wps_er_sta *sta = eloop_data;
+ wpa_printf(MSG_DEBUG, "WPS ER: STA entry timed out");
+ dl_list_del(&sta->list);
+ wps_er_sta_free(sta);
+}
+
+
+static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap,
+ const u8 *addr,
+ struct wps_parse_attr *attr,
+ int probe_req)
+{
+ struct wps_er_sta *sta = wps_er_sta_get(ap, addr, NULL);
+ int new_sta = 0;
+ int m1;
+
+ m1 = !probe_req && attr->msg_type && *attr->msg_type == WPS_M1;
+
+ if (sta == NULL) {
+ /*
+ * Only allow new STA entry to be added based on Probe Request
+ * or M1. This will filter out bogus events and anything that
+ * may have been ongoing at the time ER subscribed for events.
+ */
+ if (!probe_req && !m1)
+ return NULL;
+
+ sta = os_zalloc(sizeof(*sta));
+ if (sta == NULL)
+ return NULL;
+ os_memcpy(sta->addr, addr, ETH_ALEN);
+ sta->ap = ap;
+ dl_list_add(&ap->sta, &sta->list);
+ new_sta = 1;
+ }
+
+ if (m1)
+ sta->m1_received = 1;
+
+ if (attr->config_methods && (!probe_req || !sta->m1_received))
+ sta->config_methods = WPA_GET_BE16(attr->config_methods);
+ if (attr->uuid_e && (!probe_req || !sta->m1_received))
+ os_memcpy(sta->uuid, attr->uuid_e, WPS_UUID_LEN);
+ if (attr->primary_dev_type && (!probe_req || !sta->m1_received))
+ os_memcpy(sta->pri_dev_type, attr->primary_dev_type, 8);
+ if (attr->dev_password_id && (!probe_req || !sta->m1_received))
+ sta->dev_passwd_id = WPA_GET_BE16(attr->dev_password_id);
+
+ if (attr->manufacturer) {
+ os_free(sta->manufacturer);
+ sta->manufacturer = os_malloc(attr->manufacturer_len + 1);
+ if (sta->manufacturer) {
+ os_memcpy(sta->manufacturer, attr->manufacturer,
+ attr->manufacturer_len);
+ sta->manufacturer[attr->manufacturer_len] = '\0';
+ }
+ }
+
+ if (attr->model_name) {
+ os_free(sta->model_name);
+ sta->model_name = os_malloc(attr->model_name_len + 1);
+ if (sta->model_name) {
+ os_memcpy(sta->model_name, attr->model_name,
+ attr->model_name_len);
+ sta->model_name[attr->model_name_len] = '\0';
+ }
+ }
+
+ if (attr->model_number) {
+ os_free(sta->model_number);
+ sta->model_number = os_malloc(attr->model_number_len + 1);
+ if (sta->model_number) {
+ os_memcpy(sta->model_number, attr->model_number,
+ attr->model_number_len);
+ sta->model_number[attr->model_number_len] = '\0';
+ }
+ }
+
+ if (attr->serial_number) {
+ os_free(sta->serial_number);
+ sta->serial_number = os_malloc(attr->serial_number_len + 1);
+ if (sta->serial_number) {
+ os_memcpy(sta->serial_number, attr->serial_number,
+ attr->serial_number_len);
+ sta->serial_number[attr->serial_number_len] = '\0';
+ }
+ }
+
+ if (attr->dev_name) {
+ os_free(sta->dev_name);
+ sta->dev_name = os_malloc(attr->dev_name_len + 1);
+ if (sta->dev_name) {
+ os_memcpy(sta->dev_name, attr->dev_name,
+ attr->dev_name_len);
+ sta->dev_name[attr->dev_name_len] = '\0';
+ }
+ }
+
+ eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
+ eloop_register_timeout(300, 0, wps_er_sta_timeout, sta, NULL);
+
+ if (m1 || new_sta)
+ wps_er_sta_event(ap->er->wps, sta, WPS_EV_ER_ENROLLEE_ADD);
+
+ return sta;
+}
+
+
+static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap,
+ const u8 *addr,
+ struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - Probe Request - from "
+ MACSTR, MAC2STR(addr));
+ wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
+ "(TLVs from Probe Request)", msg);
+
+ if (wps_validate_probe_req(msg, addr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: ER: Ignore invalid proxied "
+ "Probe Request frame from " MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ if (wps_parse_msg(msg, &attr) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
+ "WLANEvent message");
+ return;
+ }
+
+ wps_er_add_sta_data(ap, addr, &attr, 1);
+ wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0);
+}
+
+
+static void wps_er_http_put_wlan_response_cb(void *ctx, struct http_client *c,
+ enum http_client_event event)
+{
+ struct wps_er_sta *sta = ctx;
+
+ switch (event) {
+ case HTTP_CLIENT_OK:
+ wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse OK");
+ break;
+ case HTTP_CLIENT_FAILED:
+ case HTTP_CLIENT_INVALID_REPLY:
+ case HTTP_CLIENT_TIMEOUT:
+ wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse failed");
+ break;
+ }
+ http_client_free(sta->http);
+ sta->http = NULL;
+}
+
+
+static const char *soap_prefix =
+ "<?xml version=\"1.0\"?>\n"
+ "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
+ "<s:Body>\n";
+static const char *soap_postfix =
+ "</s:Body>\n</s:Envelope>\n";
+static const char *urn_wfawlanconfig =
+ "urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
+
+static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg,
+ const char *name, const char *arg_name,
+ const char *path,
+ const struct sockaddr_in *dst,
+ char **len_ptr, char **body_ptr)
+{
+ unsigned char *encoded;
+ size_t encoded_len;
+ struct wpabuf *buf;
+
+ if (msg) {
+ encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg),
+ &encoded_len);
+ if (encoded == NULL)
+ return NULL;
+ } else {
+ encoded = NULL;
+ encoded_len = 0;
+ }
+
+ buf = wpabuf_alloc(1000 + encoded_len);
+ if (buf == NULL) {
+ os_free(encoded);
+ return NULL;
+ }
+
+ wpabuf_printf(buf,
+ "POST %s HTTP/1.1\r\n"
+ "Host: %s:%d\r\n"
+ "Content-Type: text/xml; charset=\"utf-8\"\r\n"
+ "Content-Length: ",
+ path, inet_ntoa(dst->sin_addr), ntohs(dst->sin_port));
+
+ *len_ptr = wpabuf_put(buf, 0);
+ wpabuf_printf(buf,
+ " \r\n"
+ "SOAPACTION: \"%s#%s\"\r\n"
+ "\r\n",
+ urn_wfawlanconfig, name);
+
+ *body_ptr = wpabuf_put(buf, 0);
+
+ wpabuf_put_str(buf, soap_prefix);
+ wpabuf_printf(buf, "<u:%s xmlns:u=\"", name);
+ wpabuf_put_str(buf, urn_wfawlanconfig);
+ wpabuf_put_str(buf, "\">\n");
+ if (encoded) {
+ wpabuf_printf(buf, "<%s>%s</%s>\n",
+ arg_name, (char *) encoded, arg_name);
+ os_free(encoded);
+ }
+
+ return buf;
+}
+
+
+static void wps_er_soap_end(struct wpabuf *buf, const char *name,
+ char *len_ptr, char *body_ptr)
+{
+ char len_buf[10];
+ wpabuf_printf(buf, "</u:%s>\n", name);
+ wpabuf_put_str(buf, soap_postfix);
+ os_snprintf(len_buf, sizeof(len_buf), "%d",
+ (int) ((char *) wpabuf_put(buf, 0) - body_ptr));
+ os_memcpy(len_ptr, len_buf, os_strlen(len_buf));
+}
+
+
+static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+ struct wpabuf *buf;
+ char *len_ptr, *body_ptr;
+ struct sockaddr_in dst;
+ char *url, *path;
+
+ if (sta->http) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - "
+ "ignore new request");
+ wpabuf_free(msg);
+ return;
+ }
+
+ if (sta->ap->control_url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+ wpabuf_free(msg);
+ return;
+ }
+
+ url = http_client_url_parse(sta->ap->control_url, &dst, &path);
+ if (url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+ wpabuf_free(msg);
+ return;
+ }
+
+ buf = wps_er_soap_hdr(msg, "PutWLANResponse", "NewMessage", path, &dst,
+ &len_ptr, &body_ptr);
+ wpabuf_free(msg);
+ os_free(url);
+ if (buf == NULL)
+ return;
+ wpabuf_printf(buf, "<NewWLANEventType>%d</NewWLANEventType>\n",
+ UPNP_WPS_WLANEVENT_TYPE_EAP);
+ wpabuf_printf(buf, "<NewWLANEventMAC>" MACSTR "</NewWLANEventMAC>\n",
+ MAC2STR(sta->addr));
+
+ wps_er_soap_end(buf, "PutWLANResponse", len_ptr, body_ptr);
+
+ sta->http = http_client_addr(&dst, buf, 1000,
+ wps_er_http_put_wlan_response_cb, sta);
+ if (sta->http == NULL)
+ wpabuf_free(buf);
+}
+
+
+static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg,
+ enum wsc_op_code op_code)
+{
+ enum wps_process_res res;
+
+ res = wps_process_msg(sta->wps, op_code, msg);
+ if (res == WPS_CONTINUE) {
+ struct wpabuf *next = wps_get_msg(sta->wps, &op_code);
+ if (next)
+ wps_er_sta_send_msg(sta, next);
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS ER: Protocol run %s with the "
+ "enrollee (res=%d)",
+ res == WPS_DONE ? "succeeded" : "failed", res);
+ wps_deinit(sta->wps);
+ sta->wps = NULL;
+ if (res == WPS_DONE) {
+ /* Remove the STA entry after short timeout */
+ eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
+ eloop_register_timeout(10, 0, wps_er_sta_timeout, sta,
+ NULL);
+ }
+ }
+}
+
+
+static void wps_er_sta_start(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+ struct wps_config cfg;
+
+ if (sta->wps)
+ wps_deinit(sta->wps);
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ cfg.wps = sta->ap->er->wps;
+ cfg.registrar = 1;
+ cfg.peer_addr = sta->addr;
+
+ sta->wps = wps_init(&cfg);
+ if (sta->wps == NULL)
+ return;
+ sta->wps->er = 1;
+ sta->wps->use_cred = sta->ap->ap_settings;
+ if (sta->ap->ap_settings) {
+ os_free(sta->cred);
+ sta->cred = os_malloc(sizeof(*sta->cred));
+ if (sta->cred) {
+ os_memcpy(sta->cred, sta->ap->ap_settings,
+ sizeof(*sta->cred));
+ sta->cred->cred_attr = NULL;
+ os_memcpy(sta->cred->mac_addr, sta->addr, ETH_ALEN);
+ sta->wps->use_cred = sta->cred;
+ }
+ }
+
+ wps_er_sta_process(sta, msg, WSC_MSG);
+}
+
+
+static void wps_er_process_wlanevent_eap(struct wps_er_ap *ap, const u8 *addr,
+ struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+ struct wps_er_sta *sta;
+
+ wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - EAP - from " MACSTR,
+ MAC2STR(addr));
+ wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
+ "(TLVs from EAP-WSC)", msg);
+
+ if (wps_parse_msg(msg, &attr) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
+ "WLANEvent message");
+ return;
+ }
+
+ sta = wps_er_add_sta_data(ap, addr, &attr, 0);
+ if (sta == NULL)
+ return;
+
+ if (attr.msg_type && *attr.msg_type == WPS_M1)
+ wps_er_sta_start(sta, msg);
+ else if (sta->wps) {
+ enum wsc_op_code op_code = WSC_MSG;
+ if (attr.msg_type) {
+ switch (*attr.msg_type) {
+ case WPS_WSC_ACK:
+ op_code = WSC_ACK;
+ break;
+ case WPS_WSC_NACK:
+ op_code = WSC_NACK;
+ break;
+ case WPS_WSC_DONE:
+ op_code = WSC_Done;
+ break;
+ }
+ }
+ wps_er_sta_process(sta, msg, op_code);
+ }
+}
+
+
+static void wps_er_process_wlanevent(struct wps_er_ap *ap,
+ struct wpabuf *event)
+{
+ u8 *data;
+ u8 wlan_event_type;
+ u8 wlan_event_mac[ETH_ALEN];
+ struct wpabuf msg;
+
+ wpa_hexdump(MSG_MSGDUMP, "WPS ER: Received WLANEvent",
+ wpabuf_head(event), wpabuf_len(event));
+ if (wpabuf_len(event) < 1 + 17) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Too short WLANEvent");
+ return;
+ }
+
+ data = wpabuf_mhead(event);
+ wlan_event_type = data[0];
+ if (hwaddr_aton((char *) data + 1, wlan_event_mac) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Invalid WLANEventMAC in "
+ "WLANEvent");
+ return;
+ }
+
+ wpabuf_set(&msg, data + 1 + 17, wpabuf_len(event) - (1 + 17));
+
+ switch (wlan_event_type) {
+ case 1:
+ wps_er_process_wlanevent_probe_req(ap, wlan_event_mac, &msg);
+ break;
+ case 2:
+ wps_er_process_wlanevent_eap(ap, wlan_event_mac, &msg);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS ER: Unknown WLANEventType %d",
+ wlan_event_type);
+ break;
+ }
+}
+
+
+static void wps_er_http_event(struct wps_er *er, struct http_request *req,
+ unsigned int ap_id)
+{
+ struct wps_er_ap *ap = wps_er_ap_get_id(er, ap_id);
+ struct wpabuf *event;
+ enum http_reply_code ret;
+
+ if (ap == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: HTTP event from unknown AP id "
+ "%u", ap_id);
+ wps_er_http_resp_not_found(req);
+ return;
+ }
+ wpa_printf(MSG_MSGDUMP, "WPS ER: HTTP event from AP id %u: %s",
+ ap_id, http_request_get_data(req));
+
+ event = xml_get_base64_item(http_request_get_data(req), "WLANEvent",
+ &ret);
+ if (event == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Could not extract WLANEvent "
+ "from the event notification");
+ /*
+ * Reply with OK anyway to avoid getting unregistered from
+ * events.
+ */
+ wps_er_http_resp_ok(req);
+ return;
+ }
+
+ wps_er_process_wlanevent(ap, event);
+
+ wpabuf_free(event);
+ wps_er_http_resp_ok(req);
+}
+
+
+static void wps_er_http_notify(struct wps_er *er, struct http_request *req)
+{
+ char *uri = http_request_get_uri(req);
+
+ if (os_strncmp(uri, "/event/", 7) == 0) {
+ unsigned int event_id;
+ char *pos;
+ event_id = atoi(uri + 7);
+ if (event_id != er->event_id) {
+ wpa_printf(MSG_DEBUG, "WPS ER: HTTP event for an "
+ "unknown event id %u", event_id);
+ return;
+ }
+ pos = os_strchr(uri + 7, '/');
+ if (pos == NULL)
+ return;
+ pos++;
+ wps_er_http_event(er, req, atoi(pos));
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'",
+ uri);
+ wps_er_http_resp_not_found(req);
+ }
+}
+
+
+static void wps_er_http_req(void *ctx, struct http_request *req)
+{
+ struct wps_er *er = ctx;
+ struct sockaddr_in *cli = http_request_get_cli_addr(req);
+ enum httpread_hdr_type type = http_request_get_type(req);
+ struct wpabuf *buf;
+
+ wpa_printf(MSG_DEBUG, "WPS ER: HTTP request: '%s' (type %d) from "
+ "%s:%d",
+ http_request_get_uri(req), type,
+ inet_ntoa(cli->sin_addr), ntohs(cli->sin_port));
+
+ switch (type) {
+ case HTTPREAD_HDR_TYPE_NOTIFY:
+ wps_er_http_notify(er, req);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS ER: Unsupported HTTP request type "
+ "%d", type);
+ buf = wpabuf_alloc(200);
+ if (buf == NULL) {
+ http_request_deinit(req);
+ return;
+ }
+ wpabuf_put_str(buf,
+ "HTTP/1.1 501 Unimplemented\r\n"
+ "Connection: close\r\n");
+ http_put_date(buf);
+ wpabuf_put_str(buf, "\r\n");
+ http_request_send_and_deinit(req, buf);
+ break;
+ }
+}
+
+
+struct wps_er *
+wps_er_init(struct wps_context *wps, const char *ifname, const char *filter)
+{
+ struct wps_er *er;
+ struct in_addr addr;
+
+ er = os_zalloc(sizeof(*er));
+ if (er == NULL)
+ return NULL;
+ dl_list_init(&er->ap);
+ dl_list_init(&er->ap_unsubscribing);
+ dl_list_init(&er->ap_settings);
+
+ er->multicast_sd = -1;
+ er->ssdp_sd = -1;
+
+ os_strlcpy(er->ifname, ifname, sizeof(er->ifname));
+ er->wps = wps;
+ if (os_get_random((unsigned char *) &er->event_id,
+ sizeof(er->event_id)) < 0) {
+ wps_er_deinit(er, NULL, NULL);
+ return NULL;
+ }
+ /* Limit event_id to < 32 bits to avoid issues with atoi() */
+ er->event_id &= 0x0fffffff;
+
+ if (filter) {
+ if (inet_aton(filter, &er->filter_addr) == 0) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter "
+ "address %s", filter);
+ wps_er_deinit(er, NULL, NULL);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections "
+ "with %s", filter);
+ }
+ if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text,
+ er->mac_addr)) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
+ "for %s. Does it have IP address?", ifname);
+ wps_er_deinit(er, NULL, NULL);
+ return NULL;
+ }
+
+ if (wps_er_ssdp_init(er) < 0) {
+ wpa_printf(MSG_INFO, "WPS UPnP: SSDP initialization failed");
+ wps_er_deinit(er, NULL, NULL);
+ return NULL;
+ }
+
+ addr.s_addr = er->ip_addr;
+ er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er);
+ if (er->http_srv == NULL) {
+ wpa_printf(MSG_INFO, "WPS UPnP: HTTP initialization failed");
+ wps_er_deinit(er, NULL, NULL);
+ return NULL;
+ }
+ er->http_port = http_server_get_port(er->http_srv);
+
+ wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s)",
+ er->ifname, er->ip_addr_text);
+
+ return er;
+}
+
+
+void wps_er_refresh(struct wps_er *er)
+{
+ struct wps_er_ap *ap;
+ struct wps_er_sta *sta;
+
+ dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+ wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_ADD);
+ dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list)
+ wps_er_sta_event(er->wps, sta, WPS_EV_ER_ENROLLEE_ADD);
+ }
+
+ wps_er_send_ssdp_msearch(er);
+}
+
+
+static void wps_er_deinit_finish(void *eloop_data, void *user_ctx)
+{
+ struct wps_er *er = eloop_data;
+ void (*deinit_done_cb)(void *ctx);
+ void *deinit_done_ctx;
+
+ wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit");
+
+ deinit_done_cb = er->deinit_done_cb;
+ deinit_done_ctx = er->deinit_done_ctx;
+ os_free(er->ip_addr_text);
+ os_free(er);
+
+ if (deinit_done_cb)
+ deinit_done_cb(deinit_done_ctx);
+}
+
+
+void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx)
+{
+ if (er == NULL)
+ return;
+ http_server_deinit(er->http_srv);
+ wps_er_ap_remove_all(er);
+ wps_er_ssdp_deinit(er);
+ eloop_register_timeout(dl_list_empty(&er->ap_unsubscribing) ? 0 : 5, 0,
+ wps_er_deinit_finish, er, NULL);
+ wpa_printf(MSG_DEBUG, "WPS ER: Finish deinit from timeout");
+ er->deinitializing = 1;
+ er->deinit_done_cb = cb;
+ er->deinit_done_ctx = ctx;
+}
+
+
+static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c,
+ enum http_client_event event)
+{
+ struct wps_er_ap *ap = ctx;
+ union wps_event_data data;
+
+ os_memset(&data, 0, sizeof(data));
+
+ switch (event) {
+ case HTTP_CLIENT_OK:
+ wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK");
+ data.set_sel_reg.state = WPS_ER_SET_SEL_REG_DONE;
+ data.set_sel_reg.uuid = ap->uuid;
+ break;
+ case HTTP_CLIENT_FAILED:
+ case HTTP_CLIENT_INVALID_REPLY:
+ case HTTP_CLIENT_TIMEOUT:
+ wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed");
+ data.set_sel_reg.state = WPS_ER_SET_SEL_REG_FAILED;
+ data.set_sel_reg.uuid = ap->uuid;
+ break;
+ }
+ http_client_free(ap->http);
+ ap->http = NULL;
+
+ if (data.set_sel_reg.uuid)
+ ap->er->wps->event_cb(ap->er->wps->cb_ctx,
+ WPS_EV_ER_SET_SELECTED_REGISTRAR, &data);
+}
+
+
+static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg)
+{
+ struct wpabuf *buf;
+ char *len_ptr, *body_ptr;
+ struct sockaddr_in dst;
+ char *url, *path;
+
+ if (ap->control_url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+ return;
+ }
+
+ if (ap->http) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for AP - "
+ "ignore new request");
+ return;
+ }
+
+ if (ap->wps) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending WPS operation for AP - "
+ "skip SetSelectedRegistrar");
+ return;
+ }
+
+ url = http_client_url_parse(ap->control_url, &dst, &path);
+ if (url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+ return;
+ }
+
+ buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", "NewMessage", path,
+ &dst, &len_ptr, &body_ptr);
+ os_free(url);
+ if (buf == NULL)
+ return;
+
+ wps_er_soap_end(buf, "SetSelectedRegistrar", len_ptr, body_ptr);
+
+ ap->http = http_client_addr(&dst, buf, 1000,
+ wps_er_http_set_sel_reg_cb, ap);
+ if (ap->http == NULL)
+ wpabuf_free(buf);
+}
+
+
+static int wps_er_build_selected_registrar(struct wpabuf *msg, int sel_reg)
+{
+ wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, !!sel_reg);
+ return 0;
+}
+
+
+static int wps_er_build_dev_password_id(struct wpabuf *msg, u16 dev_passwd_id)
+{
+ wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, dev_passwd_id);
+ return 0;
+}
+
+
+static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg,
+ u16 sel_reg_config_methods)
+{
+ wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, sel_reg_config_methods);
+ return 0;
+}
+
+
+static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r)
+{
+#ifdef CONFIG_WPS2
+ wpabuf_put_be16(msg, ATTR_UUID_R);
+ wpabuf_put_be16(msg, WPS_UUID_LEN);
+ wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN);
+#endif /* CONFIG_WPS2 */
+ return 0;
+}
+
+
+void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods)
+{
+ struct wpabuf *msg;
+ struct wps_er_ap *ap;
+ struct wps_registrar *reg = er->wps->registrar;
+ const u8 *auth_macs;
+#ifdef CONFIG_WPS2
+ u8 bcast[ETH_ALEN];
+#endif /* CONFIG_WPS2 */
+ size_t count;
+ union wps_event_data data;
+
+ if (er->skip_set_sel_reg) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar");
+ return;
+ }
+
+ msg = wpabuf_alloc(500);
+ if (msg == NULL)
+ return;
+
+ auth_macs = wps_authorized_macs(reg, &count);
+#ifdef CONFIG_WPS2
+ if (count == 0) {
+ os_memset(bcast, 0xff, ETH_ALEN);
+ auth_macs = bcast;
+ count = 1;
+ }
+#endif /* CONFIG_WPS2 */
+
+ if (wps_build_version(msg) ||
+ wps_er_build_selected_registrar(msg, sel_reg) ||
+ wps_er_build_dev_password_id(msg, dev_passwd_id) ||
+ wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) ||
+ wps_build_wfa_ext(msg, 0, auth_macs, count) ||
+ wps_er_build_uuid_r(msg, er->wps->uuid)) {
+ wpabuf_free(msg);
+ return;
+ }
+
+ os_memset(&data, 0, sizeof(data));
+ data.set_sel_reg.sel_reg = sel_reg;
+ data.set_sel_reg.dev_passwd_id = dev_passwd_id;
+ data.set_sel_reg.sel_reg_config_methods = sel_reg_config_methods;
+ data.set_sel_reg.state = WPS_ER_SET_SEL_REG_START;
+
+ dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+ if (er->set_sel_reg_uuid_filter &&
+ os_memcmp(ap->uuid, er->set_sel_reg_uuid_filter,
+ WPS_UUID_LEN) != 0)
+ continue;
+ data.set_sel_reg.uuid = ap->uuid;
+ er->wps->event_cb(er->wps->cb_ctx,
+ WPS_EV_ER_SET_SELECTED_REGISTRAR, &data);
+ wps_er_send_set_sel_reg(ap, msg);
+ }
+
+ wpabuf_free(msg);
+}
+
+
+int wps_er_pbc(struct wps_er *er, const u8 *uuid)
+{
+ int res;
+ struct wps_er_ap *ap;
+
+ if (er == NULL || er->wps == NULL)
+ return -1;
+
+ if (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) {
+ wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC "
+ "mode");
+ return -2;
+ }
+
+ ap = wps_er_ap_get(er, NULL, uuid);
+ if (ap == NULL) {
+ struct wps_er_sta *sta = NULL;
+ dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+ sta = wps_er_sta_get(ap, NULL, uuid);
+ if (sta) {
+ uuid = ap->uuid;
+ break;
+ }
+ }
+ if (sta == NULL)
+ return -3; /* Unknown UUID */
+ }
+
+ if (ap->ap_settings == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: AP settings not known");
+ return -4;
+ }
+
+ er->set_sel_reg_uuid_filter = uuid;
+ res = wps_registrar_button_pushed(er->wps->registrar, NULL);
+ er->set_sel_reg_uuid_filter = NULL;
+ if (res)
+ return -1;
+
+ return 0;
+}
+
+
+static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred)
+{
+ struct wps_er_ap *ap = ctx;
+ union wps_event_data data;
+
+ wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received");
+ os_free(ap->ap_settings);
+ ap->ap_settings = os_malloc(sizeof(*cred));
+ if (ap->ap_settings) {
+ os_memcpy(ap->ap_settings, cred, sizeof(*cred));
+ ap->ap_settings->cred_attr = NULL;
+ }
+
+ os_memset(&data, 0, sizeof(data));
+ data.ap_settings.uuid = ap->uuid;
+ data.ap_settings.cred = cred;
+ ap->er->wps->event_cb(ap->er->wps->cb_ctx, WPS_EV_ER_AP_SETTINGS,
+ &data);
+}
+
+
+static void wps_er_http_put_message_cb(void *ctx, struct http_client *c,
+ enum http_client_event event)
+{
+ struct wps_er_ap *ap = ctx;
+ struct wpabuf *reply;
+ char *msg = NULL;
+
+ switch (event) {
+ case HTTP_CLIENT_OK:
+ wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK");
+ reply = http_client_get_body(c);
+ if (reply == NULL)
+ break;
+ msg = os_zalloc(wpabuf_len(reply) + 1);
+ if (msg == NULL)
+ break;
+ os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply));
+ break;
+ case HTTP_CLIENT_FAILED:
+ case HTTP_CLIENT_INVALID_REPLY:
+ case HTTP_CLIENT_TIMEOUT:
+ wpa_printf(MSG_DEBUG, "WPS ER: PutMessage failed");
+ if (ap->wps) {
+ wps_deinit(ap->wps);
+ ap->wps = NULL;
+ }
+ break;
+ }
+ http_client_free(ap->http);
+ ap->http = NULL;
+
+ if (msg) {
+ struct wpabuf *buf;
+ enum http_reply_code ret;
+ buf = xml_get_base64_item(msg, "NewOutMessage", &ret);
+ os_free(msg);
+ if (buf == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Could not extract "
+ "NewOutMessage from PutMessage response");
+ wps_deinit(ap->wps);
+ ap->wps = NULL;
+ return;
+ }
+ wps_er_ap_process(ap, buf);
+ wpabuf_free(buf);
+ }
+}
+
+
+static void wps_er_ap_put_message(struct wps_er_ap *ap,
+ const struct wpabuf *msg)
+{
+ struct wpabuf *buf;
+ char *len_ptr, *body_ptr;
+ struct sockaddr_in dst;
+ char *url, *path;
+
+ if (ap->http) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing "
+ "with the AP - cannot continue learn");
+ return;
+ }
+
+ if (ap->control_url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+ return;
+ }
+
+ url = http_client_url_parse(ap->control_url, &dst, &path);
+ if (url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+ return;
+ }
+
+ buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst,
+ &len_ptr, &body_ptr);
+ os_free(url);
+ if (buf == NULL)
+ return;
+
+ wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr);
+
+ ap->http = http_client_addr(&dst, buf, 10000,
+ wps_er_http_put_message_cb, ap);
+ if (ap->http == NULL)
+ wpabuf_free(buf);
+}
+
+
+static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg)
+{
+ enum wps_process_res res;
+ struct wps_parse_attr attr;
+ enum wsc_op_code op_code;
+
+ op_code = WSC_MSG;
+ if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
+ switch (*attr.msg_type) {
+ case WPS_WSC_ACK:
+ op_code = WSC_ACK;
+ break;
+ case WPS_WSC_NACK:
+ op_code = WSC_NACK;
+ break;
+ case WPS_WSC_DONE:
+ op_code = WSC_Done;
+ break;
+ }
+ }
+
+ res = wps_process_msg(ap->wps, op_code, msg);
+ if (res == WPS_CONTINUE) {
+ struct wpabuf *next = wps_get_msg(ap->wps, &op_code);
+ if (next) {
+ wps_er_ap_put_message(ap, next);
+ wpabuf_free(next);
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to build "
+ "message");
+ wps_deinit(ap->wps);
+ ap->wps = NULL;
+ }
+ } else if (res == WPS_DONE) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Protocol run done");
+ wps_deinit(ap->wps);
+ ap->wps = NULL;
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from "
+ "AP (res=%d)", res);
+ wps_deinit(ap->wps);
+ ap->wps = NULL;
+ }
+}
+
+
+static void wps_er_ap_learn_m1(struct wps_er_ap *ap, struct wpabuf *m1)
+{
+ struct wps_config cfg;
+
+ if (ap->wps) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in "
+ "progress with this AP");
+ return;
+ }
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ cfg.wps = ap->er->wps;
+ cfg.registrar = 1;
+ ap->wps = wps_init(&cfg);
+ if (ap->wps == NULL)
+ return;
+ ap->wps->ap_settings_cb = wps_er_ap_settings_cb;
+ ap->wps->ap_settings_cb_ctx = ap;
+
+ wps_er_ap_process(ap, m1);
+}
+
+
+static void wps_er_ap_learn(struct wps_er_ap *ap, const char *dev_info)
+{
+ struct wpabuf *info;
+ enum http_reply_code ret;
+
+ wpa_printf(MSG_DEBUG, "WPS ER: Received GetDeviceInfo response (M1) "
+ "from the AP");
+ info = xml_get_base64_item(dev_info, "NewDeviceInfo", &ret);
+ if (info == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Could not extract "
+ "NewDeviceInfo from GetDeviceInfo response");
+ return;
+ }
+
+ ap->m1_handler(ap, info);
+ wpabuf_free(info);
+}
+
+
+static void wps_er_http_get_dev_info_cb(void *ctx, struct http_client *c,
+ enum http_client_event event)
+{
+ struct wps_er_ap *ap = ctx;
+ struct wpabuf *reply;
+ char *dev_info = NULL;
+
+ switch (event) {
+ case HTTP_CLIENT_OK:
+ wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo OK");
+ reply = http_client_get_body(c);
+ if (reply == NULL)
+ break;
+ dev_info = os_zalloc(wpabuf_len(reply) + 1);
+ if (dev_info == NULL)
+ break;
+ os_memcpy(dev_info, wpabuf_head(reply), wpabuf_len(reply));
+ break;
+ case HTTP_CLIENT_FAILED:
+ case HTTP_CLIENT_INVALID_REPLY:
+ case HTTP_CLIENT_TIMEOUT:
+ wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo failed");
+ break;
+ }
+ http_client_free(ap->http);
+ ap->http = NULL;
+
+ if (dev_info) {
+ wps_er_ap_learn(ap, dev_info);
+ os_free(dev_info);
+ }
+}
+
+
+static int wps_er_send_get_device_info(struct wps_er_ap *ap,
+ void (*m1_handler)(struct wps_er_ap *ap,
+ struct wpabuf *m1))
+{
+ struct wpabuf *buf;
+ char *len_ptr, *body_ptr;
+ struct sockaddr_in dst;
+ char *url, *path;
+
+ if (ap->http) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing "
+ "with the AP - cannot get device info");
+ return -1;
+ }
+
+ if (ap->control_url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+ return -1;
+ }
+
+ url = http_client_url_parse(ap->control_url, &dst, &path);
+ if (url == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+ return -1;
+ }
+
+ buf = wps_er_soap_hdr(NULL, "GetDeviceInfo", NULL, path, &dst,
+ &len_ptr, &body_ptr);
+ os_free(url);
+ if (buf == NULL)
+ return -1;
+
+ wps_er_soap_end(buf, "GetDeviceInfo", len_ptr, body_ptr);
+
+ ap->http = http_client_addr(&dst, buf, 10000,
+ wps_er_http_get_dev_info_cb, ap);
+ if (ap->http == NULL) {
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ ap->m1_handler = m1_handler;
+
+ return 0;
+}
+
+
+int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
+ size_t pin_len)
+{
+ struct wps_er_ap *ap;
+
+ if (er == NULL)
+ return -1;
+
+ ap = wps_er_ap_get(er, NULL, uuid);
+ if (ap == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn "
+ "request");
+ return -1;
+ }
+ if (ap->wps) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing "
+ "with the AP - cannot start learn");
+ return -1;
+ }
+
+ if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0)
+ return -1;
+
+ er->skip_set_sel_reg = 1;
+ wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0);
+ er->skip_set_sel_reg = 0;
+
+ return 0;
+}
+
+
+int wps_er_set_config(struct wps_er *er, const u8 *uuid,
+ const struct wps_credential *cred)
+{
+ struct wps_er_ap *ap;
+
+ if (er == NULL)
+ return -1;
+
+ ap = wps_er_ap_get(er, NULL, uuid);
+ if (ap == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config "
+ "request");
+ return -1;
+ }
+
+ os_free(ap->ap_settings);
+ ap->ap_settings = os_malloc(sizeof(*cred));
+ if (ap->ap_settings == NULL)
+ return -1;
+ os_memcpy(ap->ap_settings, cred, sizeof(*cred));
+ ap->ap_settings->cred_attr = NULL;
+ wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set "
+ "config request");
+
+ return 0;
+}
+
+
+static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1)
+{
+ struct wps_config cfg;
+
+ if (ap->wps) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in "
+ "progress with this AP");
+ return;
+ }
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ cfg.wps = ap->er->wps;
+ cfg.registrar = 1;
+ cfg.new_ap_settings = ap->ap_settings;
+ ap->wps = wps_init(&cfg);
+ if (ap->wps == NULL)
+ return;
+ ap->wps->ap_settings_cb = NULL;
+ ap->wps->ap_settings_cb_ctx = NULL;
+
+ wps_er_ap_process(ap, m1);
+}
+
+
+int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
+ size_t pin_len, const struct wps_credential *cred)
+{
+ struct wps_er_ap *ap;
+
+ if (er == NULL)
+ return -1;
+
+ ap = wps_er_ap_get(er, NULL, uuid);
+ if (ap == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config "
+ "request");
+ return -1;
+ }
+ if (ap->wps) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing "
+ "with the AP - cannot start config");
+ return -1;
+ }
+
+ os_free(ap->ap_settings);
+ ap->ap_settings = os_malloc(sizeof(*cred));
+ if (ap->ap_settings == NULL)
+ return -1;
+ os_memcpy(ap->ap_settings, cred, sizeof(*cred));
+ ap->ap_settings->cred_attr = NULL;
+
+ if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0)
+ return -1;
+
+ er->skip_set_sel_reg = 1;
+ wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0);
+ er->skip_set_sel_reg = 0;
+
+ return 0;
+}
diff --git a/src/wps/wps_er.h b/src/wps/wps_er.h
new file mode 100644
index 0000000..5388ed1
--- /dev/null
+++ b/src/wps/wps_er.h
@@ -0,0 +1,117 @@
+/*
+ * Wi-Fi Protected Setup - External Registrar
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPS_ER_H
+#define WPS_ER_H
+
+#include "utils/list.h"
+
+struct wps_er_sta {
+ struct dl_list list;
+ struct wps_er_ap *ap;
+ u8 addr[ETH_ALEN];
+ u16 config_methods;
+ u8 uuid[WPS_UUID_LEN];
+ u8 pri_dev_type[8];
+ u16 dev_passwd_id;
+ int m1_received;
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+ char *dev_name;
+ struct wps_data *wps;
+ struct http_client *http;
+ struct wps_credential *cred;
+};
+
+struct wps_er_ap {
+ struct dl_list list;
+ struct wps_er *er;
+ struct dl_list sta; /* list of STAs/Enrollees using this AP */
+ struct in_addr addr;
+ char *location;
+ struct http_client *http;
+ struct wps_data *wps;
+
+ u8 uuid[WPS_UUID_LEN];
+ u8 pri_dev_type[8];
+ u8 wps_state;
+ u8 mac_addr[ETH_ALEN];
+ char *friendly_name;
+ char *manufacturer;
+ char *manufacturer_url;
+ char *model_description;
+ char *model_name;
+ char *model_number;
+ char *model_url;
+ char *serial_number;
+ char *udn;
+ char *upc;
+
+ char *scpd_url;
+ char *control_url;
+ char *event_sub_url;
+
+ int subscribed;
+ u8 sid[WPS_UUID_LEN];
+ unsigned int id;
+
+ struct wps_credential *ap_settings;
+
+ void (*m1_handler)(struct wps_er_ap *ap, struct wpabuf *m1);
+};
+
+struct wps_er_ap_settings {
+ struct dl_list list;
+ u8 uuid[WPS_UUID_LEN];
+ struct wps_credential ap_settings;
+};
+
+struct wps_er {
+ struct wps_context *wps;
+ char ifname[17];
+ u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
+ char *ip_addr_text; /* IP address of network i.f. we use */
+ unsigned ip_addr; /* IP address of network i.f. we use (host order) */
+ int multicast_sd;
+ int ssdp_sd;
+ struct dl_list ap;
+ struct dl_list ap_unsubscribing;
+ struct dl_list ap_settings;
+ struct http_server *http_srv;
+ int http_port;
+ unsigned int next_ap_id;
+ unsigned int event_id;
+ int deinitializing;
+ void (*deinit_done_cb)(void *ctx);
+ void *deinit_done_ctx;
+ struct in_addr filter_addr;
+ int skip_set_sel_reg;
+ const u8 *set_sel_reg_uuid_filter;
+};
+
+
+/* wps_er.c */
+void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
+ const char *location, int max_age);
+void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr);
+int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr);
+
+/* wps_er_ssdp.c */
+int wps_er_ssdp_init(struct wps_er *er);
+void wps_er_ssdp_deinit(struct wps_er *er);
+void wps_er_send_ssdp_msearch(struct wps_er *er);
+
+#endif /* WPS_ER_H */
diff --git a/src/wps/wps_er_ssdp.c b/src/wps/wps_er_ssdp.c
new file mode 100644
index 0000000..83de9ad
--- /dev/null
+++ b/src/wps/wps_er_ssdp.c
@@ -0,0 +1,211 @@
+/*
+ * Wi-Fi Protected Setup - External Registrar (SSDP)
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "uuid.h"
+#include "eloop.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+#include "wps_er.h"
+
+
+static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct wps_er *er = eloop_ctx;
+ struct sockaddr_in addr; /* client address */
+ socklen_t addr_len;
+ int nread;
+ char buf[MULTICAST_MAX_READ], *pos, *pos2, *start;
+ int wfa = 0, byebye = 0;
+ int max_age = -1;
+ char *location = NULL;
+ u8 uuid[WPS_UUID_LEN];
+
+ addr_len = sizeof(addr);
+ nread = recvfrom(sd, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *) &addr, &addr_len);
+ if (nread <= 0)
+ return;
+ buf[nread] = '\0';
+ if (er->filter_addr.s_addr &&
+ er->filter_addr.s_addr != addr.sin_addr.s_addr)
+ return;
+
+ wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s",
+ inet_ntoa(addr.sin_addr));
+ wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Received SSDP contents",
+ (u8 *) buf, nread);
+
+ if (sd == er->multicast_sd) {
+ /* Reply to M-SEARCH */
+ if (os_strncmp(buf, "HTTP/1.1 200 OK", 15) != 0)
+ return; /* unexpected response header */
+ } else {
+ /* Unsolicited message (likely NOTIFY or M-SEARCH) */
+ if (os_strncmp(buf, "NOTIFY ", 7) != 0)
+ return; /* only process notifications */
+ }
+
+ os_memset(uuid, 0, sizeof(uuid));
+
+ for (start = buf; start && *start; start = pos) {
+ pos = os_strchr(start, '\n');
+ if (pos) {
+ if (pos[-1] == '\r')
+ pos[-1] = '\0';
+ *pos++ = '\0';
+ }
+ if (os_strstr(start, "schemas-wifialliance-org:device:"
+ "WFADevice:1"))
+ wfa = 1;
+ if (os_strstr(start, "schemas-wifialliance-org:service:"
+ "WFAWLANConfig:1"))
+ wfa = 1;
+ if (os_strncasecmp(start, "LOCATION:", 9) == 0) {
+ start += 9;
+ while (*start == ' ')
+ start++;
+ location = start;
+ } else if (os_strncasecmp(start, "NTS:", 4) == 0) {
+ if (os_strstr(start, "ssdp:byebye"))
+ byebye = 1;
+ } else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) {
+ start += 9;
+ while (*start == ' ')
+ start++;
+ pos2 = os_strstr(start, "max-age=");
+ if (pos2 == NULL)
+ continue;
+ pos2 += 8;
+ max_age = atoi(pos2);
+ } else if (os_strncasecmp(start, "USN:", 4) == 0) {
+ start += 4;
+ pos2 = os_strstr(start, "uuid:");
+ if (pos2) {
+ pos2 += 5;
+ while (*pos2 == ' ')
+ pos2++;
+ if (uuid_str2bin(pos2, uuid) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS ER: "
+ "Invalid UUID in USN: %s",
+ pos2);
+ return;
+ }
+ }
+ }
+ }
+
+ if (!wfa)
+ return; /* Not WPS advertisement/reply */
+
+ if (byebye) {
+ wps_er_ap_cache_settings(er, &addr.sin_addr);
+ wps_er_ap_remove(er, &addr.sin_addr);
+ return;
+ }
+
+ if (!location)
+ return; /* Unknown location */
+
+ if (max_age < 1)
+ return; /* No max-age reported */
+
+ wpa_printf(MSG_DEBUG, "WPS ER: AP discovered: %s "
+ "(packet source: %s max-age: %d)",
+ location, inet_ntoa(addr.sin_addr), max_age);
+
+ wps_er_ap_add(er, uuid, &addr.sin_addr, location, max_age);
+}
+
+
+void wps_er_send_ssdp_msearch(struct wps_er *er)
+{
+ struct wpabuf *msg;
+ struct sockaddr_in dest;
+
+ msg = wpabuf_alloc(500);
+ if (msg == NULL)
+ return;
+
+ wpabuf_put_str(msg,
+ "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: 239.255.255.250:1900\r\n"
+ "MAN: \"ssdp:discover\"\r\n"
+ "MX: 3\r\n"
+ "ST: urn:schemas-wifialliance-org:device:WFADevice:1"
+ "\r\n"
+ "\r\n");
+
+ os_memset(&dest, 0, sizeof(dest));
+ dest.sin_family = AF_INET;
+ dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+ dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+ if (sendto(er->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+ (struct sockaddr *) &dest, sizeof(dest)) < 0)
+ wpa_printf(MSG_DEBUG, "WPS ER: M-SEARCH sendto failed: "
+ "%d (%s)", errno, strerror(errno));
+
+ wpabuf_free(msg);
+}
+
+
+int wps_er_ssdp_init(struct wps_er *er)
+{
+ if (add_ssdp_network(er->ifname)) {
+ wpa_printf(MSG_INFO, "WPS ER: Failed to add routing entry for "
+ "SSDP");
+ return -1;
+ }
+
+ er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr);
+ if (er->multicast_sd < 0) {
+ wpa_printf(MSG_INFO, "WPS ER: Failed to open multicast socket "
+ "for SSDP");
+ return -1;
+ }
+
+ er->ssdp_sd = ssdp_listener_open();
+ if (er->ssdp_sd < 0) {
+ wpa_printf(MSG_INFO, "WPS ER: Failed to open SSDP listener "
+ "socket");
+ return -1;
+ }
+
+ if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ,
+ wps_er_ssdp_rx, er, NULL) ||
+ eloop_register_sock(er->ssdp_sd, EVENT_TYPE_READ,
+ wps_er_ssdp_rx, er, NULL))
+ return -1;
+
+ wps_er_send_ssdp_msearch(er);
+
+ return 0;
+}
+
+
+void wps_er_ssdp_deinit(struct wps_er *er)
+{
+ if (er->multicast_sd >= 0) {
+ eloop_unregister_sock(er->multicast_sd, EVENT_TYPE_READ);
+ close(er->multicast_sd);
+ }
+ if (er->ssdp_sd >= 0) {
+ eloop_unregister_sock(er->ssdp_sd, EVENT_TYPE_READ);
+ close(er->ssdp_sd);
+ }
+}
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
new file mode 100644
index 0000000..437999b
--- /dev/null
+++ b/src/wps/wps_i.h
@@ -0,0 +1,301 @@
+/*
+ * Wi-Fi Protected Setup - internal definitions
+ * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPS_I_H
+#define WPS_I_H
+
+#include "wps.h"
+
+/**
+ * struct wps_data - WPS registration protocol data
+ *
+ * This data is stored at the EAP-WSC server/peer method and it is kept for a
+ * single registration protocol run.
+ */
+struct wps_data {
+ /**
+ * wps - Pointer to long term WPS context
+ */
+ struct wps_context *wps;
+
+ /**
+ * registrar - Whether this end is a Registrar
+ */
+ int registrar;
+
+ /**
+ * er - Whether the local end is an external registrar
+ */
+ int er;
+
+ enum {
+ /* Enrollee states */
+ SEND_M1, RECV_M2, SEND_M3, RECV_M4, SEND_M5, RECV_M6, SEND_M7,
+ RECV_M8, RECEIVED_M2D, WPS_MSG_DONE, RECV_ACK, WPS_FINISHED,
+ SEND_WSC_NACK,
+
+ /* Registrar states */
+ RECV_M1, SEND_M2, RECV_M3, SEND_M4, RECV_M5, SEND_M6,
+ RECV_M7, SEND_M8, RECV_DONE, SEND_M2D, RECV_M2D_ACK
+ } state;
+
+ u8 uuid_e[WPS_UUID_LEN];
+ u8 uuid_r[WPS_UUID_LEN];
+ u8 mac_addr_e[ETH_ALEN];
+ u8 nonce_e[WPS_NONCE_LEN];
+ u8 nonce_r[WPS_NONCE_LEN];
+ u8 psk1[WPS_PSK_LEN];
+ u8 psk2[WPS_PSK_LEN];
+ u8 snonce[2 * WPS_SECRET_NONCE_LEN];
+ u8 peer_hash1[WPS_HASH_LEN];
+ u8 peer_hash2[WPS_HASH_LEN];
+
+ struct wpabuf *dh_privkey;
+ struct wpabuf *dh_pubkey_e;
+ struct wpabuf *dh_pubkey_r;
+ u8 authkey[WPS_AUTHKEY_LEN];
+ u8 keywrapkey[WPS_KEYWRAPKEY_LEN];
+ u8 emsk[WPS_EMSK_LEN];
+
+ struct wpabuf *last_msg;
+
+ u8 *dev_password;
+ size_t dev_password_len;
+ u16 dev_pw_id;
+ int pbc;
+
+ /**
+ * request_type - Request Type attribute from (Re)AssocReq
+ */
+ u8 request_type;
+
+ /**
+ * encr_type - Available encryption types
+ */
+ u16 encr_type;
+
+ /**
+ * auth_type - Available authentication types
+ */
+ u16 auth_type;
+
+ u8 *new_psk;
+ size_t new_psk_len;
+
+ int wps_pin_revealed;
+ struct wps_credential cred;
+
+ struct wps_device_data peer_dev;
+
+ /**
+ * config_error - Configuration Error value to be used in NACK
+ */
+ u16 config_error;
+ u16 error_indication;
+
+ int ext_reg;
+ int int_reg;
+
+ struct wps_credential *new_ap_settings;
+
+ void *dh_ctx;
+
+ void (*ap_settings_cb)(void *ctx, const struct wps_credential *cred);
+ void *ap_settings_cb_ctx;
+
+ struct wps_credential *use_cred;
+
+ int use_psk_key;
+ u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or
+ * 00:00:00:00:00:00 if not a P2p client */
+};
+
+
+struct wps_parse_attr {
+ /* fixed length fields */
+ const u8 *version; /* 1 octet */
+ const u8 *version2; /* 1 octet */
+ const u8 *msg_type; /* 1 octet */
+ const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
+ const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
+ const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
+ const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
+ const u8 *auth_type_flags; /* 2 octets */
+ const u8 *encr_type_flags; /* 2 octets */
+ const u8 *conn_type_flags; /* 1 octet */
+ const u8 *config_methods; /* 2 octets */
+ const u8 *sel_reg_config_methods; /* 2 octets */
+ const u8 *primary_dev_type; /* 8 octets */
+ const u8 *rf_bands; /* 1 octet */
+ const u8 *assoc_state; /* 2 octets */
+ const u8 *config_error; /* 2 octets */
+ const u8 *dev_password_id; /* 2 octets */
+ const u8 *oob_dev_password; /* WPS_OOB_DEVICE_PASSWORD_ATTR_LEN (54)
+ * octets */
+ const u8 *os_version; /* 4 octets */
+ const u8 *wps_state; /* 1 octet */
+ const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
+ const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
+ const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
+ const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
+ const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
+ const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
+ const u8 *auth_type; /* 2 octets */
+ const u8 *encr_type; /* 2 octets */
+ const u8 *network_idx; /* 1 octet */
+ const u8 *network_key_idx; /* 1 octet */
+ const u8 *mac_addr; /* ETH_ALEN (6) octets */
+ const u8 *key_prov_auto; /* 1 octet (Bool) */
+ const u8 *dot1x_enabled; /* 1 octet (Bool) */
+ const u8 *selected_registrar; /* 1 octet (Bool) */
+ const u8 *request_type; /* 1 octet */
+ const u8 *response_type; /* 1 octet */
+ const u8 *ap_setup_locked; /* 1 octet */
+ const u8 *settings_delay_time; /* 1 octet */
+ const u8 *network_key_shareable; /* 1 octet (Bool) */
+ const u8 *request_to_enroll; /* 1 octet (Bool) */
+
+ /* variable length fields */
+ const u8 *manufacturer;
+ size_t manufacturer_len;
+ const u8 *model_name;
+ size_t model_name_len;
+ const u8 *model_number;
+ size_t model_number_len;
+ const u8 *serial_number;
+ size_t serial_number_len;
+ const u8 *dev_name;
+ size_t dev_name_len;
+ const u8 *public_key;
+ size_t public_key_len;
+ const u8 *encr_settings;
+ size_t encr_settings_len;
+ const u8 *ssid; /* <= 32 octets */
+ size_t ssid_len;
+ const u8 *network_key; /* <= 64 octets */
+ size_t network_key_len;
+ const u8 *eap_type; /* <= 8 octets */
+ size_t eap_type_len;
+ const u8 *eap_identity; /* <= 64 octets */
+ size_t eap_identity_len;
+ const u8 *authorized_macs; /* <= 30 octets */
+ size_t authorized_macs_len;
+ const u8 *sec_dev_type_list; /* <= 128 octets */
+ size_t sec_dev_type_list_len;
+
+ /* attributes that can occur multiple times */
+#define MAX_CRED_COUNT 10
+ const u8 *cred[MAX_CRED_COUNT];
+ size_t cred_len[MAX_CRED_COUNT];
+ size_t num_cred;
+
+#define MAX_REQ_DEV_TYPE_COUNT 10
+ const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
+ size_t num_req_dev_type;
+
+ const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+ size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
+ size_t num_vendor_ext;
+};
+
+/* wps_common.c */
+void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
+ const char *label, u8 *res, size_t res_len);
+int wps_derive_keys(struct wps_data *wps);
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+ size_t dev_passwd_len);
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+ size_t encr_len);
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+ u16 config_error, u16 error_indication);
+void wps_success_event(struct wps_context *wps);
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
+void wps_pbc_overlap_event(struct wps_context *wps);
+void wps_pbc_timeout_event(struct wps_context *wps);
+
+extern struct oob_device_data oob_ufd_device_data;
+extern struct oob_device_data oob_nfc_device_data;
+extern struct oob_nfc_device_data oob_nfc_pn531_device_data;
+
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps);
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps);
+
+/* wps_attr_parse.c */
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
+
+/* wps_attr_build.c */
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type);
+int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type);
+int wps_build_config_methods(struct wpabuf *msg, u16 methods);
+int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid);
+int wps_build_dev_password_id(struct wpabuf *msg, u16 id);
+int wps_build_config_error(struct wpabuf *msg, u16 err);
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+ struct wpabuf *plain);
+int wps_build_version(struct wpabuf *msg);
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+ const u8 *auth_macs, size_t auth_macs_count);
+int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
+int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps);
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
+
+/* wps_attr_process.c */
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+ const struct wpabuf *msg);
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+ const u8 *key_wrap_auth);
+int wps_process_cred(struct wps_parse_attr *attr,
+ struct wps_credential *cred);
+int wps_process_ap_settings(struct wps_parse_attr *attr,
+ struct wps_credential *cred);
+
+/* wps_enrollee.c */
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
+ enum wsc_op_code *op_code);
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
+ enum wsc_op_code op_code,
+ const struct wpabuf *msg);
+
+/* wps_registrar.c */
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
+ enum wsc_op_code *op_code);
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+ enum wsc_op_code op_code,
+ const struct wpabuf *msg);
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg);
+int wps_device_store(struct wps_registrar *reg,
+ struct wps_device_data *dev, const u8 *uuid);
+void wps_registrar_selected_registrar_changed(struct wps_registrar *reg);
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count);
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e);
+
+/* ndef.c */
+struct wpabuf * ndef_parse_wifi(struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi(struct wpabuf *buf);
+
+#endif /* WPS_I_H */
diff --git a/src/wps/wps_nfc.c b/src/wps/wps_nfc.c
new file mode 100644
index 0000000..ff12000
--- /dev/null
+++ b/src/wps/wps_nfc.c
@@ -0,0 +1,117 @@
+/*
+ * NFC routines for Wi-Fi Protected Setup
+ * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include "common.h"
+
+#include "wps/wps.h"
+#include "wps_i.h"
+
+
+struct wps_nfc_data {
+ struct oob_nfc_device_data *oob_nfc_dev;
+};
+
+
+static void * init_nfc(struct wps_context *wps,
+ struct oob_device_data *oob_dev, int registrar)
+{
+ struct oob_nfc_device_data *oob_nfc_dev;
+ struct wps_nfc_data *data;
+
+ oob_nfc_dev = wps_get_oob_nfc_device(oob_dev->device_name);
+ if (oob_nfc_dev == NULL) {
+ wpa_printf(MSG_ERROR, "WPS (NFC): Unknown NFC device (%s)",
+ oob_dev->device_name);
+ return NULL;
+ }
+
+ if (oob_nfc_dev->init_func(oob_dev->device_path) < 0)
+ return NULL;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL) {
+ wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate "
+ "nfc data area");
+ return NULL;
+ }
+ data->oob_nfc_dev = oob_nfc_dev;
+ return data;
+}
+
+
+static struct wpabuf * read_nfc(void *priv)
+{
+ struct wps_nfc_data *data = priv;
+ struct wpabuf *wifi, *buf;
+ char *raw_data;
+ size_t len;
+
+ raw_data = data->oob_nfc_dev->read_func(&len);
+ if (raw_data == NULL)
+ return NULL;
+
+ wifi = wpabuf_alloc_copy(raw_data, len);
+ os_free(raw_data);
+ if (wifi == NULL) {
+ wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate "
+ "nfc read area");
+ return NULL;
+ }
+
+ buf = ndef_parse_wifi(wifi);
+ wpabuf_free(wifi);
+ if (buf == NULL)
+ wpa_printf(MSG_ERROR, "WPS (NFC): Failed to unwrap");
+ return buf;
+}
+
+
+static int write_nfc(void *priv, struct wpabuf *buf)
+{
+ struct wps_nfc_data *data = priv;
+ struct wpabuf *wifi;
+ int ret;
+
+ wifi = ndef_build_wifi(buf);
+ if (wifi == NULL) {
+ wpa_printf(MSG_ERROR, "WPS (NFC): Failed to wrap");
+ return -1;
+ }
+
+ ret = data->oob_nfc_dev->write_func(wpabuf_mhead(wifi),
+ wpabuf_len(wifi));
+ wpabuf_free(wifi);
+ return ret;
+}
+
+
+static void deinit_nfc(void *priv)
+{
+ struct wps_nfc_data *data = priv;
+
+ data->oob_nfc_dev->deinit_func();
+
+ os_free(data);
+}
+
+
+struct oob_device_data oob_nfc_device_data = {
+ .device_name = NULL,
+ .device_path = NULL,
+ .init_func = init_nfc,
+ .read_func = read_nfc,
+ .write_func = write_nfc,
+ .deinit_func = deinit_nfc,
+};
diff --git a/src/wps/wps_nfc_pn531.c b/src/wps/wps_nfc_pn531.c
new file mode 100644
index 0000000..7e05e4d
--- /dev/null
+++ b/src/wps/wps_nfc_pn531.c
@@ -0,0 +1,113 @@
+/*
+ * NFC PN531 routines for Wi-Fi Protected Setup
+ * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include "common.h"
+
+#include "wps/wps.h"
+#include "wps_i.h"
+
+#include "WpsNfcType.h"
+#include "WpsNfc.h"
+
+
+static int init_nfc_pn531(char *path)
+{
+ u32 ret;
+
+ ret = WpsNfcInit();
+ if (ret != WPS_NFCLIB_ERR_SUCCESS) {
+ wpa_printf(MSG_ERROR, "WPS (PN531): Failed to initialize "
+ "NFC Library: 0x%08x", ret);
+ return -1;
+ }
+
+ ret = WpsNfcOpenDevice((int8 *) path);
+ if (ret != WPS_NFCLIB_ERR_SUCCESS) {
+ wpa_printf(MSG_ERROR, "WPS (PN531): Failed to open "
+ "NFC Device(%s): 0x%08x", path, ret);
+ goto fail;
+ }
+
+ ret = WpsNfcTokenDiscovery();
+ if (ret != WPS_NFCLIB_ERR_SUCCESS) {
+ wpa_printf(MSG_ERROR, "WPS (PN531): Failed to discover "
+ "token: 0x%08x", ret);
+ WpsNfcCloseDevice();
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ WpsNfcDeinit();
+ return -1;
+}
+
+
+static void * read_nfc_pn531(size_t *size)
+{
+ uint32 len;
+ u32 ret;
+ int8 *data;
+
+ ret = WpsNfcRawReadToken(&data, &len);
+ if (ret != WPS_NFCLIB_ERR_SUCCESS) {
+ wpa_printf(MSG_ERROR, "WPS (PN531): Failed to read: 0x%08x",
+ ret);
+ return NULL;
+ }
+
+ *size = len;
+ return data;
+}
+
+
+static int write_nfc_pn531(void *data, size_t len)
+{
+ u32 ret;
+
+ ret = WpsNfcRawWriteToken(data, len);
+ if (ret != WPS_NFCLIB_ERR_SUCCESS) {
+ wpa_printf(MSG_ERROR, "WPS (PN531): Failed to write: 0x%08x",
+ ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void deinit_nfc_pn531(void)
+{
+ u32 ret;
+
+ ret = WpsNfcCloseDevice();
+ if (ret != WPS_NFCLIB_ERR_SUCCESS)
+ wpa_printf(MSG_ERROR, "WPS (PN531): Failed to close "
+ "NFC Device: 0x%08x", ret);
+
+ ret = WpsNfcDeinit();
+ if (ret != WPS_NFCLIB_ERR_SUCCESS)
+ wpa_printf(MSG_ERROR, "WPS (PN531): Failed to deinitialize "
+ "NFC Library: 0x%08x", ret);
+}
+
+
+struct oob_nfc_device_data oob_nfc_pn531_device_data = {
+ .init_func = init_nfc_pn531,
+ .read_func = read_nfc_pn531,
+ .write_func = write_nfc_pn531,
+ .deinit_func = deinit_nfc_pn531,
+};
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
new file mode 100644
index 0000000..65ff40d
--- /dev/null
+++ b/src/wps/wps_registrar.c
@@ -0,0 +1,3273 @@
+/*
+ * Wi-Fi Protected Setup - Registrar
+ * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "utils/list.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+#ifndef CONFIG_WPS_STRICT
+#define WPS_WORKAROUNDS
+#endif /* CONFIG_WPS_STRICT */
+
+struct wps_uuid_pin {
+ struct dl_list list;
+ u8 uuid[WPS_UUID_LEN];
+ int wildcard_uuid;
+ u8 *pin;
+ size_t pin_len;
+#define PIN_LOCKED BIT(0)
+#define PIN_EXPIRES BIT(1)
+ int flags;
+ struct os_time expiration;
+ u8 enrollee_addr[ETH_ALEN];
+};
+
+
+static void wps_free_pin(struct wps_uuid_pin *pin)
+{
+ os_free(pin->pin);
+ os_free(pin);
+}
+
+
+static void wps_remove_pin(struct wps_uuid_pin *pin)
+{
+ dl_list_del(&pin->list);
+ wps_free_pin(pin);
+}
+
+
+static void wps_free_pins(struct dl_list *pins)
+{
+ struct wps_uuid_pin *pin, *prev;
+ dl_list_for_each_safe(pin, prev, pins, struct wps_uuid_pin, list)
+ wps_remove_pin(pin);
+}
+
+
+struct wps_pbc_session {
+ struct wps_pbc_session *next;
+ u8 addr[ETH_ALEN];
+ u8 uuid_e[WPS_UUID_LEN];
+ struct os_time timestamp;
+};
+
+
+static void wps_free_pbc_sessions(struct wps_pbc_session *pbc)
+{
+ struct wps_pbc_session *prev;
+
+ while (pbc) {
+ prev = pbc;
+ pbc = pbc->next;
+ os_free(prev);
+ }
+}
+
+
+struct wps_registrar_device {
+ struct wps_registrar_device *next;
+ struct wps_device_data dev;
+ u8 uuid[WPS_UUID_LEN];
+};
+
+
+struct wps_registrar {
+ struct wps_context *wps;
+
+ int pbc;
+ int selected_registrar;
+
+ int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
+ size_t psk_len);
+ int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
+ struct wpabuf *probe_resp_ie);
+ void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+ const struct wps_device_data *dev);
+ void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
+ const u8 *uuid_e);
+ void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
+ u16 sel_reg_config_methods);
+ void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
+ const u8 *pri_dev_type, u16 config_methods,
+ u16 dev_password_id, u8 request_type,
+ const char *dev_name);
+ void *cb_ctx;
+
+ struct dl_list pins;
+ struct wps_pbc_session *pbc_sessions;
+
+ int skip_cred_build;
+ struct wpabuf *extra_cred;
+ int disable_auto_conf;
+ int sel_reg_union;
+ int sel_reg_dev_password_id_override;
+ int sel_reg_config_methods_override;
+ int static_wep_only;
+ int dualband;
+
+ struct wps_registrar_device *devices;
+
+ int force_pbc_overlap;
+
+ u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+ u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+
+ u8 p2p_dev_addr[ETH_ALEN];
+};
+
+
+static int wps_set_ie(struct wps_registrar *reg);
+static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wps_registrar_set_selected_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+
+
+static void wps_registrar_add_authorized_mac(struct wps_registrar *reg,
+ const u8 *addr)
+{
+ int i;
+ wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC " MACSTR,
+ MAC2STR(addr));
+ for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
+ if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was "
+ "already in the list");
+ return; /* already in list */
+ }
+ for (i = WPS_MAX_AUTHORIZED_MACS - 1; i > 0; i--)
+ os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i - 1],
+ ETH_ALEN);
+ os_memcpy(reg->authorized_macs[0], addr, ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
+ (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
+}
+
+
+static void wps_registrar_remove_authorized_mac(struct wps_registrar *reg,
+ const u8 *addr)
+{
+ int i;
+ wpa_printf(MSG_DEBUG, "WPS: Remove authorized MAC " MACSTR,
+ MAC2STR(addr));
+ for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) {
+ if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (i == WPS_MAX_AUTHORIZED_MACS) {
+ wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was not in the "
+ "list");
+ return; /* not in the list */
+ }
+ for (; i + 1 < WPS_MAX_AUTHORIZED_MACS; i++)
+ os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i + 1],
+ ETH_ALEN);
+ os_memset(reg->authorized_macs[WPS_MAX_AUTHORIZED_MACS - 1], 0,
+ ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
+ (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
+}
+
+
+static void wps_free_devices(struct wps_registrar_device *dev)
+{
+ struct wps_registrar_device *prev;
+
+ while (dev) {
+ prev = dev;
+ dev = dev->next;
+ wps_device_data_free(&prev->dev);
+ os_free(prev);
+ }
+}
+
+
+static struct wps_registrar_device * wps_device_get(struct wps_registrar *reg,
+ const u8 *addr)
+{
+ struct wps_registrar_device *dev;
+
+ for (dev = reg->devices; dev; dev = dev->next) {
+ if (os_memcmp(dev->dev.mac_addr, addr, ETH_ALEN) == 0)
+ return dev;
+ }
+ return NULL;
+}
+
+
+static void wps_device_clone_data(struct wps_device_data *dst,
+ struct wps_device_data *src)
+{
+ os_memcpy(dst->mac_addr, src->mac_addr, ETH_ALEN);
+ os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
+
+#define WPS_STRDUP(n) \
+ os_free(dst->n); \
+ dst->n = src->n ? os_strdup(src->n) : NULL
+
+ WPS_STRDUP(device_name);
+ WPS_STRDUP(manufacturer);
+ WPS_STRDUP(model_name);
+ WPS_STRDUP(model_number);
+ WPS_STRDUP(serial_number);
+#undef WPS_STRDUP
+}
+
+
+int wps_device_store(struct wps_registrar *reg,
+ struct wps_device_data *dev, const u8 *uuid)
+{
+ struct wps_registrar_device *d;
+
+ d = wps_device_get(reg, dev->mac_addr);
+ if (d == NULL) {
+ d = os_zalloc(sizeof(*d));
+ if (d == NULL)
+ return -1;
+ d->next = reg->devices;
+ reg->devices = d;
+ }
+
+ wps_device_clone_data(&d->dev, dev);
+ os_memcpy(d->uuid, uuid, WPS_UUID_LEN);
+
+ return 0;
+}
+
+
+static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e)
+{
+ struct wps_pbc_session *pbc, *prev = NULL;
+ struct os_time now;
+
+ os_get_time(&now);
+
+ pbc = reg->pbc_sessions;
+ while (pbc) {
+ if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
+ os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+ if (prev)
+ prev->next = pbc->next;
+ else
+ reg->pbc_sessions = pbc->next;
+ break;
+ }
+ prev = pbc;
+ pbc = pbc->next;
+ }
+
+ if (!pbc) {
+ pbc = os_zalloc(sizeof(*pbc));
+ if (pbc == NULL)
+ return;
+ os_memcpy(pbc->addr, addr, ETH_ALEN);
+ if (uuid_e)
+ os_memcpy(pbc->uuid_e, uuid_e, WPS_UUID_LEN);
+ }
+
+ pbc->next = reg->pbc_sessions;
+ reg->pbc_sessions = pbc;
+ pbc->timestamp = now;
+
+ /* remove entries that have timed out */
+ prev = pbc;
+ pbc = pbc->next;
+
+ while (pbc) {
+ if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+ prev->next = NULL;
+ wps_free_pbc_sessions(pbc);
+ break;
+ }
+ prev = pbc;
+ pbc = pbc->next;
+ }
+}
+
+
+static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
+ const u8 *uuid_e)
+{
+ struct wps_pbc_session *pbc, *prev = NULL, *tmp;
+
+ pbc = reg->pbc_sessions;
+ while (pbc) {
+ if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+ if (prev)
+ prev->next = pbc->next;
+ else
+ reg->pbc_sessions = pbc->next;
+ tmp = pbc;
+ pbc = pbc->next;
+ wpa_printf(MSG_DEBUG, "WPS: Removing PBC session for "
+ "addr=" MACSTR, MAC2STR(tmp->addr));
+ wpa_hexdump(MSG_DEBUG, "WPS: Removed UUID-E",
+ tmp->uuid_e, WPS_UUID_LEN);
+ os_free(tmp);
+ continue;
+ }
+ prev = pbc;
+ pbc = pbc->next;
+ }
+}
+
+
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e)
+{
+ int count = 0;
+ struct wps_pbc_session *pbc;
+ struct wps_pbc_session *first = NULL;
+ struct os_time now;
+
+ os_get_time(&now);
+
+ wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap");
+
+ if (uuid_e) {
+ wpa_printf(MSG_DEBUG, "WPS: Add one for the requested UUID");
+ wpa_hexdump(MSG_DEBUG, "WPS: Requested UUID",
+ uuid_e, WPS_UUID_LEN);
+ count++;
+ }
+
+ for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) {
+ wpa_printf(MSG_DEBUG, "WPS: Consider PBC session with " MACSTR,
+ MAC2STR(pbc->addr));
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E",
+ pbc->uuid_e, WPS_UUID_LEN);
+ if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC walk time has "
+ "expired");
+ break;
+ }
+ if (first &&
+ os_memcmp(pbc->uuid_e, first->uuid_e, WPS_UUID_LEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Same Enrollee");
+ continue; /* same Enrollee */
+ }
+ if (uuid_e == NULL ||
+ os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) {
+ wpa_printf(MSG_DEBUG, "WPS: New Enrollee");
+ count++;
+ }
+ if (first == NULL)
+ first = pbc;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: %u active PBC session(s) found", count);
+
+ return count > 1 ? 1 : 0;
+}
+
+
+static int wps_build_wps_state(struct wps_context *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State (%d)",
+ wps->wps_state);
+ wpabuf_put_be16(msg, ATTR_WPS_STATE);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, wps->wps_state);
+ return 0;
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+static void wps_registrar_free_pending_m2(struct wps_context *wps)
+{
+ struct upnp_pending_message *p, *p2, *prev = NULL;
+ p = wps->upnp_msgs;
+ while (p) {
+ if (p->type == WPS_M2 || p->type == WPS_M2D) {
+ if (prev == NULL)
+ wps->upnp_msgs = p->next;
+ else
+ prev->next = p->next;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Drop pending M2/M2D");
+ p2 = p;
+ p = p->next;
+ wpabuf_free(p2->msg);
+ os_free(p2);
+ continue;
+ }
+ prev = p;
+ p = p->next;
+ }
+}
+#endif /* CONFIG_WPS_UPNP */
+
+
+static int wps_build_ap_setup_locked(struct wps_context *wps,
+ struct wpabuf *msg)
+{
+ if (wps->ap_setup_locked && wps->ap_setup_locked != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: * AP Setup Locked");
+ wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 1);
+ }
+ return 0;
+}
+
+
+static int wps_build_selected_registrar(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ if (!reg->sel_reg_union)
+ return 0;
+ wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar");
+ wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 1);
+ return 0;
+}
+
+
+static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+ if (!reg->sel_reg_union)
+ return 0;
+ if (reg->sel_reg_dev_password_id_override >= 0)
+ id = reg->sel_reg_dev_password_id_override;
+ wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id);
+ wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, id);
+ return 0;
+}
+
+
+static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+ if (!reg->sel_reg_union)
+ return 0;
+ if (reg->sel_reg_dev_password_id_override >= 0)
+ id = reg->sel_reg_dev_password_id_override;
+ if (id != DEV_PW_PUSHBUTTON || !reg->dualband)
+ return 0;
+ return wps_build_uuid_e(msg, reg->wps->uuid);
+}
+
+
+static void wps_set_pushbutton(u16 *methods, u16 conf_methods)
+{
+ *methods |= WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ if (conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON)
+ *methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ if (conf_methods & WPS_CONFIG_PHY_PUSHBUTTON)
+ *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+ if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) !=
+ WPS_CONFIG_VIRT_PUSHBUTTON ||
+ (*methods & WPS_CONFIG_PHY_PUSHBUTTON) !=
+ WPS_CONFIG_PHY_PUSHBUTTON) {
+ /*
+ * Required to include virtual/physical flag, but we were not
+ * configured with push button type, so have to default to one
+ * of them.
+ */
+ *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+ }
+#endif /* CONFIG_WPS2 */
+}
+
+
+static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 methods;
+ if (!reg->sel_reg_union)
+ return 0;
+ methods = reg->wps->config_methods;
+ methods &= ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ if (reg->pbc)
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
+ if (reg->sel_reg_config_methods_override >= 0)
+ methods = reg->sel_reg_config_methods_override;
+ wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar Config Methods (%x)",
+ methods);
+ wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, methods);
+ return 0;
+}
+
+
+static int wps_build_probe_config_methods(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 methods;
+ /*
+ * These are the methods that the AP supports as an Enrollee for adding
+ * external Registrars.
+ */
+ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
+ wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, methods);
+ return 0;
+}
+
+
+static int wps_build_config_methods_r(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 methods;
+ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ if (reg->pbc)
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
+ return wps_build_config_methods(msg, methods);
+}
+
+
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count)
+{
+ *count = 0;
+
+#ifdef CONFIG_WPS2
+ while (*count < WPS_MAX_AUTHORIZED_MACS) {
+ if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
+ break;
+ (*count)++;
+ }
+#endif /* CONFIG_WPS2 */
+
+ return (const u8 *) reg->authorized_macs_union;
+}
+
+
+/**
+ * wps_registrar_init - Initialize WPS Registrar data
+ * @wps: Pointer to longterm WPS context
+ * @cfg: Registrar configuration
+ * Returns: Pointer to allocated Registrar data or %NULL on failure
+ *
+ * This function is used to initialize WPS Registrar functionality. It can be
+ * used for a single Registrar run (e.g., when run in a supplicant) or multiple
+ * runs (e.g., when run as an internal Registrar in an AP). Caller is
+ * responsible for freeing the returned data with wps_registrar_deinit() when
+ * Registrar functionality is not needed anymore.
+ */
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+ const struct wps_registrar_config *cfg)
+{
+ struct wps_registrar *reg = os_zalloc(sizeof(*reg));
+ if (reg == NULL)
+ return NULL;
+
+ dl_list_init(&reg->pins);
+ reg->wps = wps;
+ reg->new_psk_cb = cfg->new_psk_cb;
+ reg->set_ie_cb = cfg->set_ie_cb;
+ reg->pin_needed_cb = cfg->pin_needed_cb;
+ reg->reg_success_cb = cfg->reg_success_cb;
+ reg->set_sel_reg_cb = cfg->set_sel_reg_cb;
+ reg->enrollee_seen_cb = cfg->enrollee_seen_cb;
+ reg->cb_ctx = cfg->cb_ctx;
+ reg->skip_cred_build = cfg->skip_cred_build;
+ if (cfg->extra_cred) {
+ reg->extra_cred = wpabuf_alloc_copy(cfg->extra_cred,
+ cfg->extra_cred_len);
+ if (reg->extra_cred == NULL) {
+ os_free(reg);
+ return NULL;
+ }
+ }
+ reg->disable_auto_conf = cfg->disable_auto_conf;
+ reg->sel_reg_dev_password_id_override = -1;
+ reg->sel_reg_config_methods_override = -1;
+ reg->static_wep_only = cfg->static_wep_only;
+ reg->dualband = cfg->dualband;
+
+ if (wps_set_ie(reg)) {
+ wps_registrar_deinit(reg);
+ return NULL;
+ }
+
+ return reg;
+}
+
+
+/**
+ * wps_registrar_deinit - Deinitialize WPS Registrar data
+ * @reg: Registrar data from wps_registrar_init()
+ */
+void wps_registrar_deinit(struct wps_registrar *reg)
+{
+ if (reg == NULL)
+ return;
+ eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+ eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+ wps_free_pins(&reg->pins);
+ wps_free_pbc_sessions(reg->pbc_sessions);
+ wpabuf_free(reg->extra_cred);
+ wps_free_devices(reg->devices);
+ os_free(reg);
+}
+
+
+/**
+ * wps_registrar_add_pin - Configure a new PIN for Registrar
+ * @reg: Registrar data from wps_registrar_init()
+ * @addr: Enrollee MAC address or %NULL if not known
+ * @uuid: UUID-E or %NULL for wildcard (any UUID)
+ * @pin: PIN (Device Password)
+ * @pin_len: Length of pin in octets
+ * @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout
+ * Returns: 0 on success, -1 on failure
+ */
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
+ const u8 *uuid, const u8 *pin, size_t pin_len,
+ int timeout)
+{
+ struct wps_uuid_pin *p;
+
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ return -1;
+ if (addr)
+ os_memcpy(p->enrollee_addr, addr, ETH_ALEN);
+ if (uuid == NULL)
+ p->wildcard_uuid = 1;
+ else
+ os_memcpy(p->uuid, uuid, WPS_UUID_LEN);
+ p->pin = os_malloc(pin_len);
+ if (p->pin == NULL) {
+ os_free(p);
+ return -1;
+ }
+ os_memcpy(p->pin, pin, pin_len);
+ p->pin_len = pin_len;
+
+ if (timeout) {
+ p->flags |= PIN_EXPIRES;
+ os_get_time(&p->expiration);
+ p->expiration.sec += timeout;
+ }
+
+ dl_list_add(&reg->pins, &p->list);
+
+ wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)",
+ timeout);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len);
+ reg->selected_registrar = 1;
+ reg->pbc = 0;
+ if (addr)
+ wps_registrar_add_authorized_mac(reg, addr);
+ else
+ wps_registrar_add_authorized_mac(
+ reg, (u8 *) "\xff\xff\xff\xff\xff\xff");
+ wps_registrar_selected_registrar_changed(reg);
+ eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+ eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+ wps_registrar_set_selected_timeout,
+ reg, NULL);
+
+ return 0;
+}
+
+
+static void wps_registrar_remove_pin(struct wps_registrar *reg,
+ struct wps_uuid_pin *pin)
+{
+ u8 *addr;
+ u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (is_zero_ether_addr(pin->enrollee_addr))
+ addr = bcast;
+ else
+ addr = pin->enrollee_addr;
+ wps_registrar_remove_authorized_mac(reg, addr);
+ wps_remove_pin(pin);
+ wps_registrar_selected_registrar_changed(reg);
+}
+
+
+static void wps_registrar_expire_pins(struct wps_registrar *reg)
+{
+ struct wps_uuid_pin *pin, *prev;
+ struct os_time now;
+
+ os_get_time(&now);
+ dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+ {
+ if ((pin->flags & PIN_EXPIRES) &&
+ os_time_before(&pin->expiration, &now)) {
+ wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID",
+ pin->uuid, WPS_UUID_LEN);
+ wps_registrar_remove_pin(reg, pin);
+ }
+ }
+}
+
+
+/**
+ * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN
+ * @reg: Registrar data from wps_registrar_init()
+ * Returns: 0 on success, -1 if not wildcard PIN is enabled
+ */
+static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg)
+{
+ struct wps_uuid_pin *pin, *prev;
+
+ dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+ {
+ if (pin->wildcard_uuid) {
+ wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
+ pin->uuid, WPS_UUID_LEN);
+ wps_registrar_remove_pin(reg, pin);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+/**
+ * wps_registrar_invalidate_pin - Invalidate a PIN for a specific UUID-E
+ * @reg: Registrar data from wps_registrar_init()
+ * @uuid: UUID-E
+ * Returns: 0 on success, -1 on failure (e.g., PIN not found)
+ */
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+ struct wps_uuid_pin *pin, *prev;
+
+ dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+ {
+ if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+ wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
+ pin->uuid, WPS_UUID_LEN);
+ wps_registrar_remove_pin(reg, pin);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
+ const u8 *uuid, size_t *pin_len)
+{
+ struct wps_uuid_pin *pin, *found = NULL;
+
+ wps_registrar_expire_pins(reg);
+
+ dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+ if (!pin->wildcard_uuid &&
+ os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+ found = pin;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* Check for wildcard UUIDs since none of the UUID-specific
+ * PINs matched */
+ dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+ if (pin->wildcard_uuid == 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Found a wildcard "
+ "PIN. Assigned it for this UUID-E");
+ pin->wildcard_uuid = 2;
+ os_memcpy(pin->uuid, uuid, WPS_UUID_LEN);
+ found = pin;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ return NULL;
+
+ /*
+ * Lock the PIN to avoid attacks based on concurrent re-use of the PIN
+ * that could otherwise avoid PIN invalidations.
+ */
+ if (found->flags & PIN_LOCKED) {
+ wpa_printf(MSG_DEBUG, "WPS: Selected PIN locked - do not "
+ "allow concurrent re-use");
+ return NULL;
+ }
+ *pin_len = found->pin_len;
+ found->flags |= PIN_LOCKED;
+ return found->pin;
+}
+
+
+/**
+ * wps_registrar_unlock_pin - Unlock a PIN for a specific UUID-E
+ * @reg: Registrar data from wps_registrar_init()
+ * @uuid: UUID-E
+ * Returns: 0 on success, -1 on failure
+ *
+ * PINs are locked to enforce only one concurrent use. This function unlocks a
+ * PIN to allow it to be used again. If the specified PIN was configured using
+ * a wildcard UUID, it will be removed instead of allowing multiple uses.
+ */
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+ struct wps_uuid_pin *pin;
+
+ dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+ if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+ if (pin->wildcard_uuid == 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalidating used "
+ "wildcard PIN");
+ return wps_registrar_invalidate_pin(reg, uuid);
+ }
+ pin->flags &= ~PIN_LOCKED;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static void wps_registrar_stop_pbc(struct wps_registrar *reg)
+{
+ reg->selected_registrar = 0;
+ reg->pbc = 0;
+ os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
+ wps_registrar_remove_authorized_mac(reg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff");
+ wps_registrar_selected_registrar_changed(reg);
+}
+
+
+static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wps_registrar *reg = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "WPS: PBC timed out - disable PBC mode");
+ wps_pbc_timeout_event(reg->wps);
+ wps_registrar_stop_pbc(reg);
+}
+
+
+/**
+ * wps_registrar_button_pushed - Notify Registrar that AP button was pushed
+ * @reg: Registrar data from wps_registrar_init()
+ * @p2p_dev_addr: Limit allowed PBC devices to the specified P2P device, %NULL
+ * indicates no such filtering
+ * Returns: 0 on success, -1 on failure, -2 on session overlap
+ *
+ * This function is called on an AP when a push button is pushed to activate
+ * PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout
+ * or when a PBC registration is completed. If more than one Enrollee in active
+ * PBC mode has been detected during the monitor time (previous 2 minutes), the
+ * PBC mode is not actived and -2 is returned to indicate session overlap. This
+ * is skipped if a specific Enrollee is selected.
+ */
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+ const u8 *p2p_dev_addr)
+{
+ if (p2p_dev_addr == NULL &&
+ wps_registrar_pbc_overlap(reg, NULL, NULL)) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC "
+ "mode");
+ wps_pbc_overlap_event(reg->wps);
+ return -2;
+ }
+ wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started");
+ reg->force_pbc_overlap = 0;
+ reg->selected_registrar = 1;
+ reg->pbc = 1;
+ if (p2p_dev_addr)
+ os_memcpy(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+ else
+ os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
+ wps_registrar_add_authorized_mac(reg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff");
+ wps_registrar_selected_registrar_changed(reg);
+
+ eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+ eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+ eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
+ reg, NULL);
+ return 0;
+}
+
+
+static void wps_registrar_pbc_completed(struct wps_registrar *reg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: PBC completed - stopping PBC mode");
+ eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+ wps_registrar_stop_pbc(reg);
+}
+
+
+static void wps_registrar_pin_completed(struct wps_registrar *reg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: PIN completed using internal Registrar");
+ eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+ reg->selected_registrar = 0;
+ wps_registrar_selected_registrar_changed(reg);
+}
+
+
+int wps_registrar_wps_cancel(struct wps_registrar *reg)
+{
+ if (reg->pbc) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC is set - cancelling it");
+ wps_registrar_pbc_timeout(reg, NULL);
+ return 1;
+ } else if (reg->selected_registrar) {
+ /* PIN Method */
+ wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it");
+ wps_registrar_pin_completed(reg);
+ wps_registrar_invalidate_wildcard_pin(reg);
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ * wps_registrar_probe_req_rx - Notify Registrar of Probe Request
+ * @reg: Registrar data from wps_registrar_init()
+ * @addr: MAC address of the Probe Request sender
+ * @wps_data: WPS IE contents
+ *
+ * This function is called on an AP when a Probe Request with WPS IE is
+ * received. This is used to track PBC mode use and to detect possible overlap
+ * situation with other WPS APs.
+ */
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+ const struct wpabuf *wps_data,
+ int p2p_wildcard)
+{
+ struct wps_parse_attr attr;
+
+ wpa_hexdump_buf(MSG_MSGDUMP,
+ "WPS: Probe Request with WPS data received",
+ wps_data);
+
+ if (wps_parse_msg(wps_data, &attr) < 0)
+ return;
+
+ if (attr.config_methods == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in "
+ "Probe Request");
+ return;
+ }
+
+ if (attr.dev_password_id == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Password Id attribute "
+ "in Probe Request");
+ return;
+ }
+
+ if (reg->enrollee_seen_cb && attr.uuid_e &&
+ attr.primary_dev_type && attr.request_type && !p2p_wildcard) {
+ char *dev_name = NULL;
+ if (attr.dev_name) {
+ dev_name = os_zalloc(attr.dev_name_len + 1);
+ if (dev_name) {
+ os_memcpy(dev_name, attr.dev_name,
+ attr.dev_name_len);
+ }
+ }
+ reg->enrollee_seen_cb(reg->cb_ctx, addr, attr.uuid_e,
+ attr.primary_dev_type,
+ WPA_GET_BE16(attr.config_methods),
+ WPA_GET_BE16(attr.dev_password_id),
+ *attr.request_type, dev_name);
+ os_free(dev_name);
+ }
+
+ if (WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
+ return; /* Not PBC */
+
+ wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from "
+ MACSTR, MAC2STR(addr));
+ if (attr.uuid_e == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Probe Request WPS IE: No "
+ "UUID-E included");
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e,
+ WPS_UUID_LEN);
+
+ wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
+ if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected");
+ reg->force_pbc_overlap = 1;
+ wps_pbc_overlap_event(reg->wps);
+ }
+}
+
+
+static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+ const u8 *psk, size_t psk_len)
+{
+ if (reg->new_psk_cb == NULL)
+ return 0;
+
+ return reg->new_psk_cb(reg->cb_ctx, mac_addr, psk, psk_len);
+}
+
+
+static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e,
+ const struct wps_device_data *dev)
+{
+ if (reg->pin_needed_cb == NULL)
+ return;
+
+ reg->pin_needed_cb(reg->cb_ctx, uuid_e, dev);
+}
+
+
+static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr,
+ const u8 *uuid_e)
+{
+ if (reg->reg_success_cb == NULL)
+ return;
+
+ reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e);
+}
+
+
+static int wps_cb_set_ie(struct wps_registrar *reg, struct wpabuf *beacon_ie,
+ struct wpabuf *probe_resp_ie)
+{
+ return reg->set_ie_cb(reg->cb_ctx, beacon_ie, probe_resp_ie);
+}
+
+
+static void wps_cb_set_sel_reg(struct wps_registrar *reg)
+{
+ u16 methods = 0;
+ if (reg->set_sel_reg_cb == NULL)
+ return;
+
+ if (reg->selected_registrar) {
+ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ if (reg->pbc)
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: wps_cb_set_sel_reg: sel_reg=%d "
+ "config_methods=0x%x pbc=%d methods=0x%x",
+ reg->selected_registrar, reg->wps->config_methods,
+ reg->pbc, methods);
+
+ reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar,
+ reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT,
+ methods);
+}
+
+
+static int wps_set_ie(struct wps_registrar *reg)
+{
+ struct wpabuf *beacon;
+ struct wpabuf *probe;
+ const u8 *auth_macs;
+ size_t count;
+ size_t vendor_len = 0;
+ int i;
+
+ if (reg->set_ie_cb == NULL)
+ return 0;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ if (reg->wps->dev.vendor_ext[i]) {
+ vendor_len += 2 + 2;
+ vendor_len += wpabuf_len(reg->wps->dev.vendor_ext[i]);
+ }
+ }
+
+ beacon = wpabuf_alloc(400 + vendor_len);
+ if (beacon == NULL)
+ return -1;
+ probe = wpabuf_alloc(500 + vendor_len);
+ if (probe == NULL) {
+ wpabuf_free(beacon);
+ return -1;
+ }
+
+ auth_macs = wps_authorized_macs(reg, &count);
+
+ wpa_printf(MSG_DEBUG, "WPS: Build Beacon IEs");
+
+ if (wps_build_version(beacon) ||
+ wps_build_wps_state(reg->wps, beacon) ||
+ wps_build_ap_setup_locked(reg->wps, beacon) ||
+ wps_build_selected_registrar(reg, beacon) ||
+ wps_build_sel_reg_dev_password_id(reg, beacon) ||
+ wps_build_sel_reg_config_methods(reg, beacon) ||
+ wps_build_sel_pbc_reg_uuid_e(reg, beacon) ||
+ (reg->dualband && wps_build_rf_bands(&reg->wps->dev, beacon)) ||
+ wps_build_wfa_ext(beacon, 0, auth_macs, count) ||
+ wps_build_vendor_ext(&reg->wps->dev, beacon)) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+
+#ifdef CONFIG_P2P
+ if (wps_build_dev_name(&reg->wps->dev, beacon) ||
+ wps_build_primary_dev_type(&reg->wps->dev, beacon)) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+#endif /* CONFIG_P2P */
+
+ wpa_printf(MSG_DEBUG, "WPS: Build Probe Response IEs");
+
+ if (wps_build_version(probe) ||
+ wps_build_wps_state(reg->wps, probe) ||
+ wps_build_ap_setup_locked(reg->wps, probe) ||
+ wps_build_selected_registrar(reg, probe) ||
+ wps_build_sel_reg_dev_password_id(reg, probe) ||
+ wps_build_sel_reg_config_methods(reg, probe) ||
+ wps_build_resp_type(probe, reg->wps->ap ? WPS_RESP_AP :
+ WPS_RESP_REGISTRAR) ||
+ wps_build_uuid_e(probe, reg->wps->uuid) ||
+ wps_build_device_attrs(&reg->wps->dev, probe) ||
+ wps_build_probe_config_methods(reg, probe) ||
+ wps_build_rf_bands(&reg->wps->dev, probe) ||
+ wps_build_wfa_ext(probe, 0, auth_macs, count) ||
+ wps_build_vendor_ext(&reg->wps->dev, probe)) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+
+ beacon = wps_ie_encapsulate(beacon);
+ probe = wps_ie_encapsulate(probe);
+
+ if (!beacon || !probe) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+
+ if (reg->static_wep_only) {
+ /*
+ * Windows XP and Vista clients can get confused about
+ * EAP-Identity/Request when they probe the network with
+ * EAPOL-Start. In such a case, they may assume the network is
+ * using IEEE 802.1X and prompt user for a certificate while
+ * the correct (non-WPS) behavior would be to ask for the
+ * static WEP key. As a workaround, use Microsoft Provisioning
+ * IE to advertise that legacy 802.1X is not supported.
+ */
+ const u8 ms_wps[7] = {
+ WLAN_EID_VENDOR_SPECIFIC, 5,
+ /* Microsoft Provisioning IE (00:50:f2:5) */
+ 0x00, 0x50, 0xf2, 5,
+ 0x00 /* no legacy 802.1X or MS WPS */
+ };
+ wpa_printf(MSG_DEBUG, "WPS: Add Microsoft Provisioning IE "
+ "into Beacon/Probe Response frames");
+ wpabuf_put_data(beacon, ms_wps, sizeof(ms_wps));
+ wpabuf_put_data(probe, ms_wps, sizeof(ms_wps));
+ }
+
+ return wps_cb_set_ie(reg, beacon, probe);
+}
+
+
+static int wps_get_dev_password(struct wps_data *wps)
+{
+ const u8 *pin;
+ size_t pin_len = 0;
+
+ os_free(wps->dev_password);
+ wps->dev_password = NULL;
+
+ if (wps->pbc) {
+ wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC");
+ pin = (const u8 *) "00000000";
+ pin_len = 8;
+ } else {
+ pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
+ &pin_len);
+ }
+ if (pin == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
+ "the Enrollee");
+ wps_cb_pin_needed(wps->wps->registrar, wps->uuid_e,
+ &wps->peer_dev);
+ return -1;
+ }
+
+ wps->dev_password = os_malloc(pin_len);
+ if (wps->dev_password == NULL)
+ return -1;
+ os_memcpy(wps->dev_password, pin, pin_len);
+ wps->dev_password_len = pin_len;
+
+ return 0;
+}
+
+
+static int wps_build_uuid_r(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * UUID-R");
+ wpabuf_put_be16(msg, ATTR_UUID_R);
+ wpabuf_put_be16(msg, WPS_UUID_LEN);
+ wpabuf_put_data(msg, wps->uuid_r, WPS_UUID_LEN);
+ return 0;
+}
+
+
+static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+ u8 *hash;
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-S2",
+ wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+ if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
+ "R-Hash derivation");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: * R-Hash1");
+ wpabuf_put_be16(msg, ATTR_R_HASH1);
+ wpabuf_put_be16(msg, SHA256_MAC_LEN);
+ hash = wpabuf_put(msg, SHA256_MAC_LEN);
+ /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+ addr[0] = wps->snonce;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk1;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", hash, SHA256_MAC_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPS: * R-Hash2");
+ wpabuf_put_be16(msg, ATTR_R_HASH2);
+ wpabuf_put_be16(msg, SHA256_MAC_LEN);
+ hash = wpabuf_put(msg, SHA256_MAC_LEN);
+ /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+ addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk2;
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+ wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", hash, SHA256_MAC_LEN);
+
+ return 0;
+}
+
+
+static int wps_build_r_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * R-SNonce1");
+ wpabuf_put_be16(msg, ATTR_R_SNONCE1);
+ wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+ wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+ return 0;
+}
+
+
+static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * R-SNonce2");
+ wpabuf_put_be16(msg, ATTR_R_SNONCE2);
+ wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+ wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+ WPS_SECRET_NONCE_LEN);
+ return 0;
+}
+
+
+static int wps_build_cred_network_idx(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Network Index (1)");
+ wpabuf_put_be16(msg, ATTR_NETWORK_INDEX);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 1);
+ return 0;
+}
+
+
+static int wps_build_cred_ssid(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * SSID");
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID for Credential",
+ cred->ssid, cred->ssid_len);
+ wpabuf_put_be16(msg, ATTR_SSID);
+ wpabuf_put_be16(msg, cred->ssid_len);
+ wpabuf_put_data(msg, cred->ssid, cred->ssid_len);
+ return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)",
+ cred->auth_type);
+ wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, cred->auth_type);
+ return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)",
+ cred->encr_type);
+ wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+ wpabuf_put_be16(msg, 2);
+ wpabuf_put_be16(msg, cred->encr_type);
+ return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%d)",
+ (int) cred->key_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+ cred->key, cred->key_len);
+ wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+ wpabuf_put_be16(msg, cred->key_len);
+ wpabuf_put_data(msg, cred->key, cred->key_len);
+ return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * MAC Address (" MACSTR ")",
+ MAC2STR(cred->mac_addr));
+ wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+ wpabuf_put_be16(msg, ETH_ALEN);
+ wpabuf_put_data(msg, cred->mac_addr, ETH_ALEN);
+ return 0;
+}
+
+
+static int wps_build_credential(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ if (wps_build_cred_network_idx(msg, cred) ||
+ wps_build_cred_ssid(msg, cred) ||
+ wps_build_cred_auth_type(msg, cred) ||
+ wps_build_cred_encr_type(msg, cred) ||
+ wps_build_cred_network_key(msg, cred) ||
+ wps_build_cred_mac_addr(msg, cred))
+ return -1;
+ return 0;
+}
+
+
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
+{
+ struct wpabuf *cred;
+
+ if (wps->wps->registrar->skip_cred_build)
+ goto skip_cred_build;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Credential");
+ if (wps->use_cred) {
+ os_memcpy(&wps->cred, wps->use_cred, sizeof(wps->cred));
+ goto use_provided;
+ }
+ os_memset(&wps->cred, 0, sizeof(wps->cred));
+
+ os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
+ wps->cred.ssid_len = wps->wps->ssid_len;
+
+ /* Select the best authentication and encryption type */
+ if (wps->auth_type & WPS_AUTH_WPA2PSK)
+ wps->auth_type = WPS_AUTH_WPA2PSK;
+ else if (wps->auth_type & WPS_AUTH_WPAPSK)
+ wps->auth_type = WPS_AUTH_WPAPSK;
+ else if (wps->auth_type & WPS_AUTH_OPEN)
+ wps->auth_type = WPS_AUTH_OPEN;
+ else if (wps->auth_type & WPS_AUTH_SHARED)
+ wps->auth_type = WPS_AUTH_SHARED;
+ else {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x",
+ wps->auth_type);
+ return -1;
+ }
+ wps->cred.auth_type = wps->auth_type;
+
+ if (wps->auth_type == WPS_AUTH_WPA2PSK ||
+ wps->auth_type == WPS_AUTH_WPAPSK) {
+ if (wps->encr_type & WPS_ENCR_AES)
+ wps->encr_type = WPS_ENCR_AES;
+ else if (wps->encr_type & WPS_ENCR_TKIP)
+ wps->encr_type = WPS_ENCR_TKIP;
+ else {
+ wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
+ "type for WPA/WPA2");
+ return -1;
+ }
+ } else {
+ if (wps->encr_type & WPS_ENCR_WEP)
+ wps->encr_type = WPS_ENCR_WEP;
+ else if (wps->encr_type & WPS_ENCR_NONE)
+ wps->encr_type = WPS_ENCR_NONE;
+ else {
+ wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
+ "type for non-WPA/WPA2 mode");
+ return -1;
+ }
+ }
+ wps->cred.encr_type = wps->encr_type;
+ /*
+ * Set MAC address in the Credential to be the Enrollee's MAC address
+ */
+ os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
+
+ if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap &&
+ !wps->wps->registrar->disable_auto_conf) {
+ u8 r[16];
+ /* Generate a random passphrase */
+ if (random_get_bytes(r, sizeof(r)) < 0)
+ return -1;
+ os_free(wps->new_psk);
+ wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len);
+ if (wps->new_psk == NULL)
+ return -1;
+ wps->new_psk_len--; /* remove newline */
+ while (wps->new_psk_len &&
+ wps->new_psk[wps->new_psk_len - 1] == '=')
+ wps->new_psk_len--;
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Generated passphrase",
+ wps->new_psk, wps->new_psk_len);
+ os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len);
+ wps->cred.key_len = wps->new_psk_len;
+ } else if (wps->use_psk_key && wps->wps->psk_set) {
+ char hex[65];
+ wpa_printf(MSG_DEBUG, "WPS: Use PSK format for Network Key");
+ wpa_snprintf_hex(hex, sizeof(hex), wps->wps->psk, 32);
+ os_memcpy(wps->cred.key, hex, 32 * 2);
+ wps->cred.key_len = 32 * 2;
+ } else if (wps->wps->network_key) {
+ os_memcpy(wps->cred.key, wps->wps->network_key,
+ wps->wps->network_key_len);
+ wps->cred.key_len = wps->wps->network_key_len;
+ } else if (wps->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+ char hex[65];
+ /* Generate a random per-device PSK */
+ os_free(wps->new_psk);
+ wps->new_psk_len = 32;
+ wps->new_psk = os_malloc(wps->new_psk_len);
+ if (wps->new_psk == NULL)
+ return -1;
+ if (random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) {
+ os_free(wps->new_psk);
+ wps->new_psk = NULL;
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
+ wps->new_psk, wps->new_psk_len);
+ wpa_snprintf_hex(hex, sizeof(hex), wps->new_psk,
+ wps->new_psk_len);
+ os_memcpy(wps->cred.key, hex, wps->new_psk_len * 2);
+ wps->cred.key_len = wps->new_psk_len * 2;
+ }
+
+use_provided:
+#ifdef CONFIG_WPS_TESTING
+ if (wps_testing_dummy_cred)
+ cred = wpabuf_alloc(200);
+ else
+ cred = NULL;
+ if (cred) {
+ struct wps_credential dummy;
+ wpa_printf(MSG_DEBUG, "WPS: Add dummy credential");
+ os_memset(&dummy, 0, sizeof(dummy));
+ os_memcpy(dummy.ssid, "dummy", 5);
+ dummy.ssid_len = 5;
+ dummy.auth_type = WPS_AUTH_WPA2PSK;
+ dummy.encr_type = WPS_ENCR_AES;
+ os_memcpy(dummy.key, "dummy psk", 9);
+ dummy.key_len = 9;
+ os_memcpy(dummy.mac_addr, wps->mac_addr_e, ETH_ALEN);
+ wps_build_credential(cred, &dummy);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Dummy Credential", cred);
+
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(cred));
+ wpabuf_put_buf(msg, cred);
+
+ wpabuf_free(cred);
+ }
+#endif /* CONFIG_WPS_TESTING */
+
+ cred = wpabuf_alloc(200);
+ if (cred == NULL)
+ return -1;
+
+ if (wps_build_credential(cred, &wps->cred)) {
+ wpabuf_free(cred);
+ return -1;
+ }
+
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(cred));
+ wpabuf_put_buf(msg, cred);
+ wpabuf_free(cred);
+
+skip_cred_build:
+ if (wps->wps->registrar->extra_cred) {
+ wpa_printf(MSG_DEBUG, "WPS: * Credential (pre-configured)");
+ wpabuf_put_buf(msg, wps->wps->registrar->extra_cred);
+ }
+
+ return 0;
+}
+
+
+static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: * AP Settings");
+
+ if (wps_build_credential(msg, &wps->cred))
+ return -1;
+
+ return 0;
+}
+
+
+static struct wpabuf * wps_build_m2(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+ wps->nonce_r, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M2");
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M2) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_uuid_r(wps, msg) ||
+ wps_build_public_key(wps, msg) ||
+ wps_derive_keys(wps) ||
+ wps_build_auth_type_flags(wps, msg) ||
+ wps_build_encr_type_flags(wps, msg) ||
+ wps_build_conn_type_flags(wps, msg) ||
+ wps_build_config_methods_r(wps->wps->registrar, msg) ||
+ wps_build_device_attrs(&wps->wps->dev, msg) ||
+ wps_build_rf_bands(&wps->wps->dev, msg) ||
+ wps_build_assoc_state(wps, msg) ||
+ wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
+ wps_build_dev_password_id(msg, wps->dev_pw_id) ||
+ wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->int_reg = 1;
+ wps->state = RECV_M3;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m2d(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+ u16 err = wps->config_error;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M2D");
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps->wps->ap && wps->wps->ap_setup_locked &&
+ err == WPS_CFG_NO_ERROR)
+ err = WPS_CFG_SETUP_LOCKED;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M2D) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_uuid_r(wps, msg) ||
+ wps_build_auth_type_flags(wps, msg) ||
+ wps_build_encr_type_flags(wps, msg) ||
+ wps_build_conn_type_flags(wps, msg) ||
+ wps_build_config_methods_r(wps->wps->registrar, msg) ||
+ wps_build_device_attrs(&wps->wps->dev, msg) ||
+ wps_build_rf_bands(&wps->wps->dev, msg) ||
+ wps_build_assoc_state(wps, msg) ||
+ wps_build_config_error(msg, err) ||
+ wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wps->state = RECV_M2D_ACK;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m4(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M4");
+
+ wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+ plain = wpabuf_alloc(200);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M4) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_r_hash(wps, msg) ||
+ wps_build_r_snonce1(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->state = RECV_M5;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m6(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M6");
+
+ plain = wpabuf_alloc(200);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M6) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_r_snonce2(wps, plain) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->wps_pin_revealed = 1;
+ wps->state = RECV_M7;
+ return msg;
+}
+
+
+static struct wpabuf * wps_build_m8(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message M8");
+
+ plain = wpabuf_alloc(500);
+ if (plain == NULL)
+ return NULL;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_M8) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ ((wps->wps->ap || wps->er) && wps_build_cred(wps, plain)) ||
+ (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) ||
+ wps_build_key_wrap_auth(wps, plain) ||
+ wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_authenticator(wps, msg)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_free(plain);
+
+ wps->state = RECV_DONE;
+ return msg;
+}
+
+
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
+ enum wsc_op_code *op_code)
+{
+ struct wpabuf *msg;
+
+#ifdef CONFIG_WPS_UPNP
+ if (!wps->int_reg && wps->wps->wps_upnp) {
+ struct upnp_pending_message *p, *prev = NULL;
+ if (wps->ext_reg > 1)
+ wps_registrar_free_pending_m2(wps->wps);
+ p = wps->wps->upnp_msgs;
+ /* TODO: check pending message MAC address */
+ while (p && p->next) {
+ prev = p;
+ p = p->next;
+ }
+ if (p) {
+ wpa_printf(MSG_DEBUG, "WPS: Use pending message from "
+ "UPnP");
+ if (prev)
+ prev->next = NULL;
+ else
+ wps->wps->upnp_msgs = NULL;
+ msg = p->msg;
+ switch (p->type) {
+ case WPS_WSC_ACK:
+ *op_code = WSC_ACK;
+ break;
+ case WPS_WSC_NACK:
+ *op_code = WSC_NACK;
+ break;
+ default:
+ *op_code = WSC_MSG;
+ break;
+ }
+ os_free(p);
+ if (wps->ext_reg == 0)
+ wps->ext_reg = 1;
+ return msg;
+ }
+ }
+ if (wps->ext_reg) {
+ wpa_printf(MSG_DEBUG, "WPS: Using external Registrar, but no "
+ "pending message available");
+ return NULL;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ switch (wps->state) {
+ case SEND_M2:
+ if (wps_get_dev_password(wps) < 0)
+ msg = wps_build_m2d(wps);
+ else
+ msg = wps_build_m2(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M2D:
+ msg = wps_build_m2d(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M4:
+ msg = wps_build_m4(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M6:
+ msg = wps_build_m6(wps);
+ *op_code = WSC_MSG;
+ break;
+ case SEND_M8:
+ msg = wps_build_m8(wps);
+ *op_code = WSC_MSG;
+ break;
+ case RECV_DONE:
+ msg = wps_build_wsc_ack(wps);
+ *op_code = WSC_ACK;
+ break;
+ case SEND_WSC_NACK:
+ msg = wps_build_wsc_nack(wps);
+ *op_code = WSC_NACK;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
+ "a message", wps->state);
+ msg = NULL;
+ break;
+ }
+
+ if (*op_code == WSC_MSG && msg) {
+ /* Save a copy of the last message for Authenticator derivation
+ */
+ wpabuf_free(wps->last_msg);
+ wps->last_msg = wpabuf_dup(msg);
+ }
+
+ return msg;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+ if (e_nonce == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
+ return -1;
+ }
+
+ os_memcpy(wps->nonce_e, e_nonce, WPS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+ wps->nonce_e, WPS_NONCE_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+ if (r_nonce == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
+ return -1;
+ }
+
+ if (os_memcmp(wps->nonce_r, r_nonce, WPS_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce received");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_uuid_e(struct wps_data *wps, const u8 *uuid_e)
+{
+ if (uuid_e == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No UUID-E received");
+ return -1;
+ }
+
+ os_memcpy(wps->uuid_e, uuid_e, WPS_UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", wps->uuid_e, WPS_UUID_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_dev_password_id(struct wps_data *wps, const u8 *pw_id)
+{
+ if (pw_id == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Device Password ID received");
+ return -1;
+ }
+
+ wps->dev_pw_id = WPA_GET_BE16(pw_id);
+ wpa_printf(MSG_DEBUG, "WPS: Device Password ID %d", wps->dev_pw_id);
+
+ return 0;
+}
+
+
+static int wps_process_e_hash1(struct wps_data *wps, const u8 *e_hash1)
+{
+ if (e_hash1 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No E-Hash1 received");
+ return -1;
+ }
+
+ os_memcpy(wps->peer_hash1, e_hash1, WPS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_e_hash2(struct wps_data *wps, const u8 *e_hash2)
+{
+ if (e_hash2 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No E-Hash2 received");
+ return -1;
+ }
+
+ os_memcpy(wps->peer_hash2, e_hash2, WPS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+ return 0;
+}
+
+
+static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (e_snonce1 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No E-SNonce1 received");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce1", e_snonce1,
+ WPS_SECRET_NONCE_LEN);
+
+ /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+ addr[0] = e_snonce1;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk1;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+ if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does "
+ "not match with the pre-committed value");
+ wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+ wps_pwd_auth_fail_event(wps->wps, 0, 1);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the first "
+ "half of the device password");
+
+ return 0;
+}
+
+
+static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2)
+{
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+
+ if (e_snonce2 == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No E-SNonce2 received");
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce2", e_snonce2,
+ WPS_SECRET_NONCE_LEN);
+
+ /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+ addr[0] = e_snonce2;
+ len[0] = WPS_SECRET_NONCE_LEN;
+ addr[1] = wps->psk2;
+ len[1] = WPS_PSK_LEN;
+ addr[2] = wpabuf_head(wps->dh_pubkey_e);
+ len[2] = wpabuf_len(wps->dh_pubkey_e);
+ addr[3] = wpabuf_head(wps->dh_pubkey_r);
+ len[3] = wpabuf_len(wps->dh_pubkey_r);
+ hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+ if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: E-Hash2 derived from E-S2 does "
+ "not match with the pre-committed value");
+ wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
+ wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+ wps_pwd_auth_fail_event(wps->wps, 0, 2);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the second "
+ "half of the device password");
+ wps->wps_pin_revealed = 0;
+ wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e);
+
+ return 0;
+}
+
+
+static int wps_process_mac_addr(struct wps_data *wps, const u8 *mac_addr)
+{
+ if (mac_addr == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No MAC Address received");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee MAC Address " MACSTR,
+ MAC2STR(mac_addr));
+ os_memcpy(wps->mac_addr_e, mac_addr, ETH_ALEN);
+ os_memcpy(wps->peer_dev.mac_addr, mac_addr, ETH_ALEN);
+
+ return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+ size_t pk_len)
+{
+ if (pk == NULL || pk_len == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
+ return -1;
+ }
+
+#ifdef CONFIG_WPS_OOB
+ if (wps->wps->oob_conf.pubkey_hash != NULL) {
+ const u8 *addr[1];
+ u8 hash[WPS_HASH_LEN];
+
+ addr[0] = pk;
+ sha256_vector(1, addr, &pk_len, hash);
+ if (os_memcmp(hash,
+ wpabuf_head(wps->wps->oob_conf.pubkey_hash),
+ WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "WPS: Public Key hash error");
+ return -1;
+ }
+ }
+#endif /* CONFIG_WPS_OOB */
+
+ wpabuf_free(wps->dh_pubkey_e);
+ wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
+ if (wps->dh_pubkey_e == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth)
+{
+ u16 auth_types;
+
+ if (auth == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Authentication Type flags "
+ "received");
+ return -1;
+ }
+
+ auth_types = WPA_GET_BE16(auth);
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x",
+ auth_types);
+ wps->auth_type = wps->wps->auth_types & auth_types;
+ if (wps->auth_type == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No match in supported "
+ "authentication types (own 0x%x Enrollee 0x%x)",
+ wps->wps->auth_types, auth_types);
+#ifdef WPS_WORKAROUNDS
+ /*
+ * Some deployed implementations seem to advertise incorrect
+ * information in this attribute. For example, Linksys WRT350N
+ * seems to have a byteorder bug that breaks this negotiation.
+ * In order to interoperate with existing implementations,
+ * assume that the Enrollee supports everything we do.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee "
+ "does not advertise supported authentication types "
+ "correctly");
+ wps->auth_type = wps->wps->auth_types;
+#else /* WPS_WORKAROUNDS */
+ return -1;
+#endif /* WPS_WORKAROUNDS */
+ }
+
+ return 0;
+}
+
+
+static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr)
+{
+ u16 encr_types;
+
+ if (encr == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Encryption Type flags "
+ "received");
+ return -1;
+ }
+
+ encr_types = WPA_GET_BE16(encr);
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Encryption Type flags 0x%x",
+ encr_types);
+ wps->encr_type = wps->wps->encr_types & encr_types;
+ if (wps->encr_type == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No match in supported "
+ "encryption types (own 0x%x Enrollee 0x%x)",
+ wps->wps->encr_types, encr_types);
+#ifdef WPS_WORKAROUNDS
+ /*
+ * Some deployed implementations seem to advertise incorrect
+ * information in this attribute. For example, Linksys WRT350N
+ * seems to have a byteorder bug that breaks this negotiation.
+ * In order to interoperate with existing implementations,
+ * assume that the Enrollee supports everything we do.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee "
+ "does not advertise supported encryption types "
+ "correctly");
+ wps->encr_type = wps->wps->encr_types;
+#else /* WPS_WORKAROUNDS */
+ return -1;
+#endif /* WPS_WORKAROUNDS */
+ }
+
+ return 0;
+}
+
+
+static int wps_process_conn_type_flags(struct wps_data *wps, const u8 *conn)
+{
+ if (conn == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Connection Type flags "
+ "received");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Connection Type flags 0x%x",
+ *conn);
+
+ return 0;
+}
+
+
+static int wps_process_config_methods(struct wps_data *wps, const u8 *methods)
+{
+ u16 m;
+
+ if (methods == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Config Methods received");
+ return -1;
+ }
+
+ m = WPA_GET_BE16(methods);
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Config Methods 0x%x"
+ "%s%s%s%s%s%s%s%s%s", m,
+ m & WPS_CONFIG_USBA ? " [USBA]" : "",
+ m & WPS_CONFIG_ETHERNET ? " [Ethernet]" : "",
+ m & WPS_CONFIG_LABEL ? " [Label]" : "",
+ m & WPS_CONFIG_DISPLAY ? " [Display]" : "",
+ m & WPS_CONFIG_EXT_NFC_TOKEN ? " [Ext NFC Token]" : "",
+ m & WPS_CONFIG_INT_NFC_TOKEN ? " [Int NFC Token]" : "",
+ m & WPS_CONFIG_NFC_INTERFACE ? " [NFC]" : "",
+ m & WPS_CONFIG_PUSHBUTTON ? " [PBC]" : "",
+ m & WPS_CONFIG_KEYPAD ? " [Keypad]" : "");
+
+ if (!(m & WPS_CONFIG_DISPLAY) && !wps->use_psk_key) {
+ /*
+ * The Enrollee does not have a display so it is unlikely to be
+ * able to show the passphrase to a user and as such, could
+ * benefit from receiving PSK to reduce key derivation time.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Prefer PSK format key due to "
+ "Enrollee not supporting display");
+ wps->use_psk_key = 1;
+ }
+
+ return 0;
+}
+
+
+static int wps_process_wps_state(struct wps_data *wps, const u8 *state)
+{
+ if (state == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Wi-Fi Protected Setup State "
+ "received");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Wi-Fi Protected Setup State %d",
+ *state);
+
+ return 0;
+}
+
+
+static int wps_process_assoc_state(struct wps_data *wps, const u8 *assoc)
+{
+ u16 a;
+
+ if (assoc == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Association State received");
+ return -1;
+ }
+
+ a = WPA_GET_BE16(assoc);
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Association State %d", a);
+
+ return 0;
+}
+
+
+static int wps_process_config_error(struct wps_data *wps, const u8 *err)
+{
+ u16 e;
+
+ if (err == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Configuration Error received");
+ return -1;
+ }
+
+ e = WPA_GET_BE16(err);
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee Configuration Error %d", e);
+
+ return 0;
+}
+
+
+static int wps_registrar_p2p_dev_addr_match(struct wps_data *wps)
+{
+#ifdef CONFIG_P2P
+ struct wps_registrar *reg = wps->wps->registrar;
+
+ if (is_zero_ether_addr(reg->p2p_dev_addr))
+ return 1; /* no filtering in use */
+
+ if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No match on P2P Device Address "
+ "filtering for PBC: expected " MACSTR " was "
+ MACSTR " - indicate PBC session overlap",
+ MAC2STR(reg->p2p_dev_addr),
+ MAC2STR(wps->p2p_dev_addr));
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+ return 1;
+}
+
+
+static int wps_registrar_skip_overlap(struct wps_data *wps)
+{
+#ifdef CONFIG_P2P
+ struct wps_registrar *reg = wps->wps->registrar;
+
+ if (is_zero_ether_addr(reg->p2p_dev_addr))
+ return 0; /* no specific Enrollee selected */
+
+ if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Skip PBC overlap due to selected "
+ "Enrollee match");
+ return 1;
+ }
+#endif /* CONFIG_P2P */
+ return 0;
+}
+
+
+static enum wps_process_res wps_process_m1(struct wps_data *wps,
+ struct wps_parse_attr *attr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Received M1");
+
+ if (wps->state != RECV_M1) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M1", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_process_uuid_e(wps, attr->uuid_e) ||
+ wps_process_mac_addr(wps, attr->mac_addr) ||
+ wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+ wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+ wps_process_auth_type_flags(wps, attr->auth_type_flags) ||
+ wps_process_encr_type_flags(wps, attr->encr_type_flags) ||
+ wps_process_conn_type_flags(wps, attr->conn_type_flags) ||
+ wps_process_config_methods(wps, attr->config_methods) ||
+ wps_process_wps_state(wps, attr->wps_state) ||
+ wps_process_device_attrs(&wps->peer_dev, attr) ||
+ wps_process_rf_bands(&wps->peer_dev, attr->rf_bands) ||
+ wps_process_assoc_state(wps, attr->assoc_state) ||
+ wps_process_dev_password_id(wps, attr->dev_password_id) ||
+ wps_process_config_error(wps, attr->config_error) ||
+ wps_process_os_version(&wps->peer_dev, attr->os_version))
+ return WPS_FAILURE;
+
+ if (wps->dev_pw_id < 0x10 &&
+ wps->dev_pw_id != DEV_PW_DEFAULT &&
+ wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
+ wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
+ wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
+ (wps->dev_pw_id != DEV_PW_PUSHBUTTON ||
+ !wps->wps->registrar->pbc)) {
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d",
+ wps->dev_pw_id);
+ wps->state = SEND_M2D;
+ return WPS_CONTINUE;
+ }
+
+#ifdef CONFIG_WPS_OOB
+ if (wps->dev_pw_id >= 0x10 &&
+ wps->dev_pw_id != wps->wps->oob_dev_pw_id) {
+ wpa_printf(MSG_DEBUG, "WPS: OOB Device Password ID "
+ "%d mismatch", wps->dev_pw_id);
+ wps->state = SEND_M2D;
+ return WPS_CONTINUE;
+ }
+#endif /* CONFIG_WPS_OOB */
+
+ if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) {
+ if ((wps->wps->registrar->force_pbc_overlap ||
+ wps_registrar_pbc_overlap(wps->wps->registrar,
+ wps->mac_addr_e, wps->uuid_e) ||
+ !wps_registrar_p2p_dev_addr_match(wps)) &&
+ !wps_registrar_skip_overlap(wps)) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC "
+ "negotiation");
+ wps->state = SEND_M2D;
+ wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+ wps_pbc_overlap_event(wps->wps);
+ wps_fail_event(wps->wps, WPS_M1,
+ WPS_CFG_MULTIPLE_PBC_DETECTED,
+ WPS_EI_NO_ERROR);
+ wps->wps->registrar->force_pbc_overlap = 1;
+ return WPS_CONTINUE;
+ }
+ wps_registrar_add_pbc_session(wps->wps->registrar,
+ wps->mac_addr_e, wps->uuid_e);
+ wps->pbc = 1;
+ }
+
+#ifdef WPS_WORKAROUNDS
+ /*
+ * It looks like Mac OS X 10.6.3 and 10.6.4 do not like Network Key in
+ * passphrase format. To avoid interop issues, force PSK format to be
+ * used.
+ */
+ if (!wps->use_psk_key &&
+ wps->peer_dev.manufacturer &&
+ os_strncmp(wps->peer_dev.manufacturer, "Apple ", 6) == 0 &&
+ wps->peer_dev.model_name &&
+ os_strcmp(wps->peer_dev.model_name, "AirPort") == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - Force Network Key in "
+ "PSK format");
+ wps->use_psk_key = 1;
+ }
+#endif /* WPS_WORKAROUNDS */
+
+ wps->state = SEND_M2;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m3(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Received M3");
+
+ if (wps->state != RECV_M3) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M3", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+ !wps_registrar_skip_overlap(wps)) {
+ wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
+ "session overlap");
+ wps->state = SEND_WSC_NACK;
+ wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg) ||
+ wps_process_e_hash1(wps, attr->e_hash1) ||
+ wps_process_e_hash2(wps, attr->e_hash2)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wps->state = SEND_M4;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m5(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M5");
+
+ if (wps->state != RECV_M5) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M5", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+ !wps_registrar_skip_overlap(wps)) {
+ wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
+ "session overlap");
+ wps->state = SEND_WSC_NACK;
+ wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+ "Settings attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+ wps_process_e_snonce1(wps, eattr.e_snonce1)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+ wpabuf_free(decrypted);
+
+ wps->state = SEND_M6;
+ return WPS_CONTINUE;
+}
+
+
+static void wps_sta_cred_cb(struct wps_data *wps)
+{
+ /*
+ * Update credential to only include a single authentication and
+ * encryption type in case the AP configuration includes more than one
+ * option.
+ */
+ if (wps->cred.auth_type & WPS_AUTH_WPA2PSK)
+ wps->cred.auth_type = WPS_AUTH_WPA2PSK;
+ else if (wps->cred.auth_type & WPS_AUTH_WPAPSK)
+ wps->cred.auth_type = WPS_AUTH_WPAPSK;
+ if (wps->cred.encr_type & WPS_ENCR_AES)
+ wps->cred.encr_type = WPS_ENCR_AES;
+ else if (wps->cred.encr_type & WPS_ENCR_TKIP)
+ wps->cred.encr_type = WPS_ENCR_TKIP;
+ wpa_printf(MSG_DEBUG, "WPS: Update local configuration based on the "
+ "AP configuration");
+ if (wps->wps->cred_cb)
+ wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
+}
+
+
+static void wps_cred_update(struct wps_credential *dst,
+ struct wps_credential *src)
+{
+ os_memcpy(dst->ssid, src->ssid, sizeof(dst->ssid));
+ dst->ssid_len = src->ssid_len;
+ dst->auth_type = src->auth_type;
+ dst->encr_type = src->encr_type;
+ dst->key_idx = src->key_idx;
+ os_memcpy(dst->key, src->key, sizeof(dst->key));
+ dst->key_len = src->key_len;
+}
+
+
+static int wps_process_ap_settings_r(struct wps_data *wps,
+ struct wps_parse_attr *attr)
+{
+ if (wps->wps->ap || wps->er)
+ return 0;
+
+ /* AP Settings Attributes in M7 when Enrollee is an AP */
+ if (wps_process_ap_settings(attr, &wps->cred) < 0)
+ return -1;
+
+ wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP");
+
+ if (wps->new_ap_settings) {
+ wpa_printf(MSG_INFO, "WPS: Update AP configuration based on "
+ "new settings");
+ wps_cred_update(&wps->cred, wps->new_ap_settings);
+ return 0;
+ } else {
+ /*
+ * Use the AP PIN only to receive the current AP settings, not
+ * to reconfigure the AP.
+ */
+
+ /*
+ * Clear selected registrar here since we do not get to
+ * WSC_Done in this protocol run.
+ */
+ wps_registrar_pin_completed(wps->wps->registrar);
+
+ if (wps->ap_settings_cb) {
+ wps->ap_settings_cb(wps->ap_settings_cb_ctx,
+ &wps->cred);
+ return 1;
+ }
+ wps_sta_cred_cb(wps);
+ return 1;
+ }
+}
+
+
+static enum wps_process_res wps_process_m7(struct wps_data *wps,
+ const struct wpabuf *msg,
+ struct wps_parse_attr *attr)
+{
+ struct wpabuf *decrypted;
+ struct wps_parse_attr eattr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received M7");
+
+ if (wps->state != RECV_M7) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving M7", wps->state);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+ !wps_registrar_skip_overlap(wps)) {
+ wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
+ "session overlap");
+ wps->state = SEND_WSC_NACK;
+ wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+ wps_process_authenticator(wps, attr->authenticator, msg)) {
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+ attr->encr_settings_len);
+ if (decrypted == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt Encrypted "
+ "Settings attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er,
+ attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+ "attribute");
+ if (wps_parse_msg(decrypted, &eattr) < 0 ||
+ wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+ wps_process_e_snonce2(wps, eattr.e_snonce2) ||
+ wps_process_ap_settings_r(wps, &eattr)) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ wpabuf_free(decrypted);
+
+ wps->state = SEND_M8;
+ return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+ enum wps_process_res ret = WPS_CONTINUE;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
+ if (*attr.msg_type != WPS_M1 &&
+ (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce,
+ WPS_NONCE_LEN != 0))) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ switch (*attr.msg_type) {
+ case WPS_M1:
+ if (wps_validate_m1(msg) < 0)
+ return WPS_FAILURE;
+#ifdef CONFIG_WPS_UPNP
+ if (wps->wps->wps_upnp && attr.mac_addr) {
+ /* Remove old pending messages when starting new run */
+ wps_free_pending_msgs(wps->wps->upnp_msgs);
+ wps->wps->upnp_msgs = NULL;
+
+ upnp_wps_device_send_wlan_event(
+ wps->wps->wps_upnp, attr.mac_addr,
+ UPNP_WPS_WLANEVENT_TYPE_EAP, msg);
+ }
+#endif /* CONFIG_WPS_UPNP */
+ ret = wps_process_m1(wps, &attr);
+ break;
+ case WPS_M3:
+ if (wps_validate_m3(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m3(wps, msg, &attr);
+ if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+ wps_fail_event(wps->wps, WPS_M3, wps->config_error,
+ wps->error_indication);
+ break;
+ case WPS_M5:
+ if (wps_validate_m5(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m5(wps, msg, &attr);
+ if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+ wps_fail_event(wps->wps, WPS_M5, wps->config_error,
+ wps->error_indication);
+ break;
+ case WPS_M7:
+ if (wps_validate_m7(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_m7(wps, msg, &attr);
+ if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+ wps_fail_event(wps->wps, WPS_M7, wps->config_error,
+ wps->error_indication);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+ if (ret == WPS_CONTINUE) {
+ /* Save a copy of the last message for Authenticator derivation
+ */
+ wpabuf_free(wps->last_msg);
+ wps->last_msg = wpabuf_dup(msg);
+ }
+
+ return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_ACK) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+#ifdef CONFIG_WPS_UPNP
+ if (wps->wps->wps_upnp && wps->ext_reg && wps->state == RECV_M2D_ACK &&
+ upnp_wps_subscribers(wps->wps->wps_upnp)) {
+ if (wps->wps->upnp_msgs)
+ return WPS_CONTINUE;
+ wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
+ "external Registrar");
+ return WPS_PENDING;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ if (wps->state == RECV_M2D_ACK) {
+#ifdef CONFIG_WPS_UPNP
+ if (wps->wps->wps_upnp &&
+ upnp_wps_subscribers(wps->wps->wps_upnp)) {
+ if (wps->wps->upnp_msgs)
+ return WPS_CONTINUE;
+ if (wps->ext_reg == 0)
+ wps->ext_reg = 1;
+ wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
+ "external Registrar");
+ return WPS_PENDING;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ wpa_printf(MSG_DEBUG, "WPS: No more registrars available - "
+ "terminate negotiation");
+ }
+
+ return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+ int old_state;
+ u16 config_error;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
+
+ old_state = wps->state;
+ wps->state = SEND_WSC_NACK;
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_NACK) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+#ifdef CONFIG_WPS_UPNP
+ if (wps->wps->wps_upnp && wps->ext_reg) {
+ wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
+ "Registrar terminated by the Enrollee");
+ return WPS_FAILURE;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.config_error == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
+ "in WSC_NACK");
+ return WPS_FAILURE;
+ }
+
+ config_error = WPA_GET_BE16(attr.config_error);
+ wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
+ "Configuration Error %d", config_error);
+
+ switch (old_state) {
+ case RECV_M3:
+ wps_fail_event(wps->wps, WPS_M2, config_error,
+ wps->error_indication);
+ break;
+ case RECV_M5:
+ wps_fail_event(wps->wps, WPS_M4, config_error,
+ wps->error_indication);
+ break;
+ case RECV_M7:
+ wps_fail_event(wps->wps, WPS_M6, config_error,
+ wps->error_indication);
+ break;
+ case RECV_DONE:
+ wps_fail_event(wps->wps, WPS_M8, config_error,
+ wps->error_indication);
+ break;
+ default:
+ break;
+ }
+
+ return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_printf(MSG_DEBUG, "WPS: Received WSC_Done");
+
+ if (wps->state != RECV_DONE &&
+ (!wps->wps->wps_upnp || !wps->ext_reg)) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+ "receiving WSC_Done", wps->state);
+ return WPS_FAILURE;
+ }
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return WPS_FAILURE;
+
+ if (attr.msg_type == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+ return WPS_FAILURE;
+ }
+
+ if (*attr.msg_type != WPS_WSC_DONE) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+ *attr.msg_type);
+ return WPS_FAILURE;
+ }
+
+#ifdef CONFIG_WPS_UPNP
+ if (wps->wps->wps_upnp && wps->ext_reg) {
+ wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
+ "Registrar completed successfully");
+ wps_device_store(wps->wps->registrar, &wps->peer_dev,
+ wps->uuid_e);
+ return WPS_DONE;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ if (attr.registrar_nonce == NULL ||
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+ return WPS_FAILURE;
+ }
+
+ if (attr.enrollee_nonce == NULL ||
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+ return WPS_FAILURE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully");
+ wps_device_store(wps->wps->registrar, &wps->peer_dev,
+ wps->uuid_e);
+
+ if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->new_psk &&
+ wps->wps->ap && !wps->wps->registrar->disable_auto_conf) {
+ struct wps_credential cred;
+
+ wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based "
+ "on first Enrollee connection");
+
+ os_memset(&cred, 0, sizeof(cred));
+ os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
+ cred.ssid_len = wps->wps->ssid_len;
+ cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
+ cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
+ os_memcpy(cred.key, wps->new_psk, wps->new_psk_len);
+ cred.key_len = wps->new_psk_len;
+
+ wps->wps->wps_state = WPS_STATE_CONFIGURED;
+ wpa_hexdump_ascii_key(MSG_DEBUG,
+ "WPS: Generated random passphrase",
+ wps->new_psk, wps->new_psk_len);
+ if (wps->wps->cred_cb)
+ wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+
+ os_free(wps->new_psk);
+ wps->new_psk = NULL;
+ }
+
+ if (!wps->wps->ap && !wps->er)
+ wps_sta_cred_cb(wps);
+
+ if (wps->new_psk) {
+ if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e,
+ wps->new_psk, wps->new_psk_len)) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to configure the "
+ "new PSK");
+ }
+ os_free(wps->new_psk);
+ wps->new_psk = NULL;
+ }
+
+ wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e);
+
+ if (wps->pbc) {
+ wps_registrar_remove_pbc_session(wps->wps->registrar,
+ wps->uuid_e);
+ wps_registrar_pbc_completed(wps->wps->registrar);
+ } else {
+ wps_registrar_pin_completed(wps->wps->registrar);
+ }
+ /* TODO: maintain AuthorizedMACs somewhere separately for each ER and
+ * merge them into APs own list.. */
+
+ wps_success_event(wps->wps);
+
+ return WPS_DONE;
+}
+
+
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+ enum wsc_op_code op_code,
+ const struct wpabuf *msg)
+{
+ enum wps_process_res ret;
+
+ wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
+ "op_code=%d)",
+ (unsigned long) wpabuf_len(msg), op_code);
+
+#ifdef CONFIG_WPS_UPNP
+ if (wps->wps->wps_upnp && op_code == WSC_MSG && wps->ext_reg == 1) {
+ struct wps_parse_attr attr;
+ if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type &&
+ *attr.msg_type == WPS_M3)
+ wps->ext_reg = 2; /* past M2/M2D phase */
+ }
+ if (wps->ext_reg > 1)
+ wps_registrar_free_pending_m2(wps->wps);
+ if (wps->wps->wps_upnp && wps->ext_reg &&
+ wps->wps->upnp_msgs == NULL &&
+ (op_code == WSC_MSG || op_code == WSC_Done || op_code == WSC_NACK))
+ {
+ struct wps_parse_attr attr;
+ int type;
+ if (wps_parse_msg(msg, &attr) < 0 || attr.msg_type == NULL)
+ type = -1;
+ else
+ type = *attr.msg_type;
+ wpa_printf(MSG_DEBUG, "WPS: Sending received message (type %d)"
+ " to external Registrar for processing", type);
+ upnp_wps_device_send_wlan_event(wps->wps->wps_upnp,
+ wps->mac_addr_e,
+ UPNP_WPS_WLANEVENT_TYPE_EAP,
+ msg);
+ if (op_code == WSC_MSG)
+ return WPS_PENDING;
+ } else if (wps->wps->wps_upnp && wps->ext_reg && op_code == WSC_MSG) {
+ wpa_printf(MSG_DEBUG, "WPS: Skip internal processing - using "
+ "external Registrar");
+ return WPS_CONTINUE;
+ }
+#endif /* CONFIG_WPS_UPNP */
+
+ switch (op_code) {
+ case WSC_MSG:
+ return wps_process_wsc_msg(wps, msg);
+ case WSC_ACK:
+ if (wps_validate_wsc_ack(msg) < 0)
+ return WPS_FAILURE;
+ return wps_process_wsc_ack(wps, msg);
+ case WSC_NACK:
+ if (wps_validate_wsc_nack(msg) < 0)
+ return WPS_FAILURE;
+ return wps_process_wsc_nack(wps, msg);
+ case WSC_Done:
+ if (wps_validate_wsc_done(msg) < 0)
+ return WPS_FAILURE;
+ ret = wps_process_wsc_done(wps, msg);
+ if (ret == WPS_FAILURE) {
+ wps->state = SEND_WSC_NACK;
+ wps_fail_event(wps->wps, WPS_WSC_DONE,
+ wps->config_error,
+ wps->error_indication);
+ }
+ return ret;
+ default:
+ wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
+ return WPS_FAILURE;
+ }
+}
+
+
+int wps_registrar_update_ie(struct wps_registrar *reg)
+{
+ return wps_set_ie(reg);
+}
+
+
+static void wps_registrar_set_selected_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct wps_registrar *reg = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "WPS: Selected Registrar timeout - "
+ "unselect internal Registrar");
+ reg->selected_registrar = 0;
+ reg->pbc = 0;
+ wps_registrar_selected_registrar_changed(reg);
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+static void wps_registrar_sel_reg_add(struct wps_registrar *reg,
+ struct subscription *s)
+{
+ int i, j;
+ wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d "
+ "config_methods=0x%x)",
+ s->dev_password_id, s->config_methods);
+ reg->sel_reg_union = 1;
+ if (reg->sel_reg_dev_password_id_override != DEV_PW_PUSHBUTTON)
+ reg->sel_reg_dev_password_id_override = s->dev_password_id;
+ if (reg->sel_reg_config_methods_override == -1)
+ reg->sel_reg_config_methods_override = 0;
+ reg->sel_reg_config_methods_override |= s->config_methods;
+ for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
+ if (is_zero_ether_addr(reg->authorized_macs_union[i]))
+ break;
+ for (j = 0; i < WPS_MAX_AUTHORIZED_MACS && j < WPS_MAX_AUTHORIZED_MACS;
+ j++) {
+ if (is_zero_ether_addr(s->authorized_macs[j]))
+ break;
+ wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC into union: "
+ MACSTR, MAC2STR(s->authorized_macs[j]));
+ os_memcpy(reg->authorized_macs_union[i],
+ s->authorized_macs[j], ETH_ALEN);
+ i++;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union",
+ (u8 *) reg->authorized_macs_union,
+ sizeof(reg->authorized_macs_union));
+}
+#endif /* CONFIG_WPS_UPNP */
+
+
+static void wps_registrar_sel_reg_union(struct wps_registrar *reg)
+{
+#ifdef CONFIG_WPS_UPNP
+ struct subscription *s;
+
+ if (reg->wps->wps_upnp == NULL)
+ return;
+
+ dl_list_for_each(s, &reg->wps->wps_upnp->subscriptions,
+ struct subscription, list) {
+ struct subscr_addr *sa;
+ sa = dl_list_first(&s->addr_list, struct subscr_addr, list);
+ if (sa) {
+ wpa_printf(MSG_DEBUG, "WPS: External Registrar %s:%d",
+ inet_ntoa(sa->saddr.sin_addr),
+ ntohs(sa->saddr.sin_port));
+ }
+ if (s->selected_registrar)
+ wps_registrar_sel_reg_add(reg, s);
+ else
+ wpa_printf(MSG_DEBUG, "WPS: External Registrar not "
+ "selected");
+ }
+#endif /* CONFIG_WPS_UPNP */
+}
+
+
+/**
+ * wps_registrar_selected_registrar_changed - SetSelectedRegistrar change
+ * @reg: Registrar data from wps_registrar_init()
+ *
+ * This function is called when selected registrar state changes, e.g., when an
+ * AP receives a SetSelectedRegistrar UPnP message.
+ */
+void wps_registrar_selected_registrar_changed(struct wps_registrar *reg)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Selected registrar information changed");
+
+ reg->sel_reg_union = reg->selected_registrar;
+ reg->sel_reg_dev_password_id_override = -1;
+ reg->sel_reg_config_methods_override = -1;
+ os_memcpy(reg->authorized_macs_union, reg->authorized_macs,
+ WPS_MAX_AUTHORIZED_MACS * ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union (start with own)",
+ (u8 *) reg->authorized_macs_union,
+ sizeof(reg->authorized_macs_union));
+ if (reg->selected_registrar) {
+ u16 methods;
+
+ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ if (reg->pbc) {
+ reg->sel_reg_dev_password_id_override =
+ DEV_PW_PUSHBUTTON;
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
+ }
+ wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected "
+ "(pbc=%d)", reg->pbc);
+ reg->sel_reg_config_methods_override = methods;
+ } else
+ wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected");
+
+ wps_registrar_sel_reg_union(reg);
+
+ wps_set_ie(reg);
+ wps_cb_set_sel_reg(reg);
+}
+
+
+int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
+ char *buf, size_t buflen)
+{
+ struct wps_registrar_device *d;
+ int len = 0, ret;
+ char uuid[40];
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+ d = wps_device_get(reg, addr);
+ if (d == NULL)
+ return 0;
+ if (uuid_bin2str(d->uuid, uuid, sizeof(uuid)))
+ return 0;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "wpsUuid=%s\n"
+ "wpsPrimaryDeviceType=%s\n"
+ "wpsDeviceName=%s\n"
+ "wpsManufacturer=%s\n"
+ "wpsModelName=%s\n"
+ "wpsModelNumber=%s\n"
+ "wpsSerialNumber=%s\n",
+ uuid,
+ wps_dev_type_bin2str(d->dev.pri_dev_type, devtype,
+ sizeof(devtype)),
+ d->dev.device_name ? d->dev.device_name : "",
+ d->dev.manufacturer ? d->dev.manufacturer : "",
+ d->dev.model_name ? d->dev.model_name : "",
+ d->dev.model_number ? d->dev.model_number : "",
+ d->dev.serial_number ? d->dev.serial_number : "");
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ return len;
+}
+
+
+int wps_registrar_config_ap(struct wps_registrar *reg,
+ struct wps_credential *cred)
+{
+#ifdef CONFIG_WPS2
+ printf("encr_type=0x%x\n", cred->encr_type);
+ if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP |
+ WPS_ENCR_AES))) {
+ if (cred->encr_type & WPS_ENCR_WEP) {
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
+ "due to WEP configuration");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+ "invalid encr_type 0x%x", cred->encr_type);
+ return -1;
+ }
+
+ if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
+ WPS_ENCR_TKIP) {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
+ "TKIP+AES");
+ cred->encr_type |= WPS_ENCR_AES;
+ }
+
+ if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+ WPS_AUTH_WPAPSK) {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
+ "WPAPSK+WPA2PSK");
+ cred->auth_type |= WPS_AUTH_WPA2PSK;
+ }
+#endif /* CONFIG_WPS2 */
+
+ if (reg->wps->cred_cb)
+ return reg->wps->cred_cb(reg->wps->cb_ctx, cred);
+
+ return -1;
+}
diff --git a/src/wps/wps_ufd.c b/src/wps/wps_ufd.c
new file mode 100644
index 0000000..1a911e1
--- /dev/null
+++ b/src/wps/wps_ufd.c
@@ -0,0 +1,235 @@
+/*
+ * UFD routines for Wi-Fi Protected Setup
+ * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include "common.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include "wps/wps.h"
+#include "wps/wps_i.h"
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#define UFD_DIR1 "%s\\SMRTNTKY"
+#define UFD_DIR2 UFD_DIR1 "\\WFAWSC"
+#define UFD_FILE UFD_DIR2 "\\%s"
+#else /* CONFIG_NATIVE_WINDOWS */
+#define UFD_DIR1 "%s/SMRTNTKY"
+#define UFD_DIR2 UFD_DIR1 "/WFAWSC"
+#define UFD_FILE UFD_DIR2 "/%s"
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+struct wps_ufd_data {
+ int ufd_fd;
+};
+
+
+static int dev_pwd_e_file_filter(const struct dirent *entry)
+{
+ unsigned int prefix;
+ char ext[5];
+
+ if (sscanf(entry->d_name, "%8x.%4s", &prefix, ext) != 2)
+ return 0;
+ if (prefix == 0)
+ return 0;
+ if (os_strcasecmp(ext, "WFA") != 0)
+ return 0;
+
+ return 1;
+}
+
+
+static int wps_get_dev_pwd_e_file_name(char *path, char *file_name)
+{
+ struct dirent **namelist;
+ int i, file_num;
+
+ file_num = scandir(path, &namelist, &dev_pwd_e_file_filter,
+ alphasort);
+ if (file_num < 0) {
+ wpa_printf(MSG_ERROR, "WPS: OOB file not found: %d (%s)",
+ errno, strerror(errno));
+ return -1;
+ }
+ if (file_num == 0) {
+ wpa_printf(MSG_ERROR, "WPS: OOB file not found");
+ os_free(namelist);
+ return -1;
+ }
+ os_strlcpy(file_name, namelist[0]->d_name, 13);
+ for (i = 0; i < file_num; i++)
+ os_free(namelist[i]);
+ os_free(namelist);
+ return 0;
+}
+
+
+static int get_file_name(struct wps_context *wps, int registrar,
+ const char *path, char *file_name)
+{
+ switch (wps->oob_conf.oob_method) {
+ case OOB_METHOD_CRED:
+ os_snprintf(file_name, 13, "00000000.WSC");
+ break;
+ case OOB_METHOD_DEV_PWD_E:
+ if (registrar) {
+ char temp[128];
+ os_snprintf(temp, sizeof(temp), UFD_DIR2, path);
+ if (wps_get_dev_pwd_e_file_name(temp, file_name) < 0)
+ return -1;
+ } else {
+ u8 *mac_addr = wps->dev.mac_addr;
+
+ os_snprintf(file_name, 13, "%02X%02X%02X%02X.WFA",
+ mac_addr[2], mac_addr[3], mac_addr[4],
+ mac_addr[5]);
+ }
+ break;
+ case OOB_METHOD_DEV_PWD_R:
+ os_snprintf(file_name, 13, "00000000.WFA");
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "WPS: Invalid USBA OOB method");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int ufd_mkdir(const char *path)
+{
+ if (mkdir(path, S_IRWXU) < 0 && errno != EEXIST) {
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to create directory "
+ "'%s': %d (%s)", path, errno, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+
+static void * init_ufd(struct wps_context *wps,
+ struct oob_device_data *oob_dev, int registrar)
+{
+ int write_f;
+ char temp[128];
+ char *path = oob_dev->device_path;
+ char filename[13];
+ struct wps_ufd_data *data;
+ int ufd_fd;
+
+ if (path == NULL)
+ return NULL;
+
+ write_f = wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ?
+ !registrar : registrar;
+
+ if (get_file_name(wps, registrar, path, filename) < 0) {
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file name");
+ return NULL;
+ }
+
+ if (write_f) {
+ os_snprintf(temp, sizeof(temp), UFD_DIR1, path);
+ if (ufd_mkdir(temp))
+ return NULL;
+ os_snprintf(temp, sizeof(temp), UFD_DIR2, path);
+ if (ufd_mkdir(temp))
+ return NULL;
+ }
+
+ os_snprintf(temp, sizeof(temp), UFD_FILE, path, filename);
+ if (write_f)
+ ufd_fd = open(temp, O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR);
+ else
+ ufd_fd = open(temp, O_RDONLY);
+ if (ufd_fd < 0) {
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to open %s: %s",
+ temp, strerror(errno));
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->ufd_fd = ufd_fd;
+ return data;
+}
+
+
+static struct wpabuf * read_ufd(void *priv)
+{
+ struct wps_ufd_data *data = priv;
+ struct wpabuf *buf;
+ struct stat s;
+ size_t file_size;
+
+ if (fstat(data->ufd_fd, &s) < 0) {
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file size");
+ return NULL;
+ }
+
+ file_size = s.st_size;
+ buf = wpabuf_alloc(file_size);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to alloc read "
+ "buffer");
+ return NULL;
+ }
+
+ if (read(data->ufd_fd, wpabuf_mhead(buf), file_size) !=
+ (int) file_size) {
+ wpabuf_free(buf);
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to read");
+ return NULL;
+ }
+ wpabuf_put(buf, file_size);
+ return buf;
+}
+
+
+static int write_ufd(void *priv, struct wpabuf *buf)
+{
+ struct wps_ufd_data *data = priv;
+
+ if (write(data->ufd_fd, wpabuf_mhead(buf), wpabuf_len(buf)) !=
+ (int) wpabuf_len(buf)) {
+ wpa_printf(MSG_ERROR, "WPS (UFD): Failed to write");
+ return -1;
+ }
+ return 0;
+}
+
+
+static void deinit_ufd(void *priv)
+{
+ struct wps_ufd_data *data = priv;
+ close(data->ufd_fd);
+ os_free(data);
+}
+
+
+struct oob_device_data oob_ufd_device_data = {
+ .device_name = NULL,
+ .device_path = NULL,
+ .init_func = init_ufd,
+ .read_func = read_ufd,
+ .write_func = write_ufd,
+ .deinit_func = deinit_ufd,
+};
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
new file mode 100644
index 0000000..763dcaf
--- /dev/null
+++ b/src/wps/wps_upnp.c
@@ -0,0 +1,1210 @@
+/*
+ * UPnP WPS Device
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * See below for more details on licensing and code history.
+ */
+
+/*
+ * This has been greatly stripped down from the original file
+ * (upnp_wps_device.c) by Ted Merrill, Atheros Communications
+ * in order to eliminate use of the bulky libupnp library etc.
+ *
+ * History:
+ * upnp_wps_device.c is/was a shim layer between wps_opt_upnp.c and
+ * the libupnp library.
+ * The layering (by Sony) was well done; only a very minor modification
+ * to API of upnp_wps_device.c was required.
+ * libupnp was found to be undesirable because:
+ * -- It consumed too much code and data space
+ * -- It uses multiple threads, making debugging more difficult
+ * and possibly reducing reliability.
+ * -- It uses static variables and only supports one instance.
+ * The shim and libupnp are here replaced by special code written
+ * specifically for the needs of hostapd.
+ * Various shortcuts can and are taken to keep the code size small.
+ * Generally, execution time is not as crucial.
+ *
+ * BUGS:
+ * -- UPnP requires that we be able to resolve domain names.
+ * While uncommon, if we have to do it then it will stall the entire
+ * hostapd program, which is bad.
+ * This is because we use the standard linux getaddrinfo() function
+ * which is syncronous.
+ * An asyncronous solution would be to use the free "ares" library.
+ * -- Does not have a robust output buffering scheme. Uses a single
+ * fixed size output buffer per TCP/HTTP connection, with possible (although
+ * unlikely) possibility of overflow and likely excessive use of RAM.
+ * A better solution would be to write the HTTP output as a buffered stream,
+ * using chunking: (handle header specially, then) generate data with
+ * a printf-like function into a buffer, catching buffer full condition,
+ * then send it out surrounded by http chunking.
+ * -- There is some code that could be separated out into the common
+ * library to be shared with wpa_supplicant.
+ * -- Needs renaming with module prefix to avoid polluting the debugger
+ * namespace and causing possible collisions with other static fncs
+ * and structure declarations when using the debugger.
+ * -- The http error code generation is pretty bogus, hopefully noone cares.
+ *
+ * Author: Ted Merrill, Atheros Communications, based upon earlier work
+ * as explained above and below.
+ *
+ * Copyright:
+ * Copyright 2008 Atheros Communications.
+ *
+ * The original header (of upnp_wps_device.c) reads:
+ *
+ * Copyright (c) 2006-2007 Sony Corporation. All Rights Reserved.
+ *
+ * File Name: upnp_wps_device.c
+ * Description: EAP-WPS UPnP device source
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Sony Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions from Intel libupnp files, e.g. genlib/net/http/httpreadwrite.c
+ * typical header:
+ *
+ * Copyright (c) 2000-2003 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * Overview of WPS over UPnP:
+ *
+ * UPnP is a protocol that allows devices to discover each other and control
+ * each other. In UPnP terminology, a device is either a "device" (a server
+ * that provides information about itself and allows itself to be controlled)
+ * or a "control point" (a client that controls "devices") or possibly both.
+ * This file implements a UPnP "device".
+ *
+ * For us, we use mostly basic UPnP discovery, but the control part of interest
+ * is WPS carried via UPnP messages. There is quite a bit of basic UPnP
+ * discovery to do before we can get to WPS, however.
+ *
+ * UPnP discovery begins with "devices" send out multicast UDP packets to a
+ * certain fixed multicast IP address and port, and "control points" sending
+ * out other such UDP packets.
+ *
+ * The packets sent by devices are NOTIFY packets (not to be confused with TCP
+ * NOTIFY packets that are used later) and those sent by control points are
+ * M-SEARCH packets. These packets contain a simple HTTP style header. The
+ * packets are sent redundantly to get around packet loss. Devices respond to
+ * M-SEARCH packets with HTTP-like UDP packets containing HTTP/1.1 200 OK
+ * messages, which give similar information as the UDP NOTIFY packets.
+ *
+ * The above UDP packets advertise the (arbitrary) TCP ports that the
+ * respective parties will listen to. The control point can then do a HTTP
+ * SUBSCRIBE (something like an HTTP PUT) after which the device can do a
+ * separate HTTP NOTIFY (also like an HTTP PUT) to do event messaging.
+ *
+ * The control point will also do HTTP GET of the "device file" listed in the
+ * original UDP information from the device (see UPNP_WPS_DEVICE_XML_FILE
+ * data), and based on this will do additional GETs... HTTP POSTs are done to
+ * cause an action.
+ *
+ * Beyond some basic information in HTTP headers, additional information is in
+ * the HTTP bodies, in a format set by the SOAP and XML standards, a markup
+ * language related to HTML used for web pages. This language is intended to
+ * provide the ultimate in self-documentation by providing a universal
+ * namespace based on pseudo-URLs called URIs. Note that although a URI looks
+ * like a URL (a web address), they are never accessed as such but are used
+ * only as identifiers.
+ *
+ * The POST of a GetDeviceInfo gets information similar to what might be
+ * obtained from a probe request or response on Wi-Fi. WPS messages M1-M8
+ * are passed via a POST of a PutMessage; the M1-M8 WPS messages are converted
+ * to a bin64 ascii representation for encapsulation. When proxying messages,
+ * WLANEvent and PutWLANResponse are used.
+ *
+ * This of course glosses over a lot of details.
+ */
+
+#include "includes.h"
+
+#include <assert.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "uuid.h"
+#include "base64.h"
+#include "wps.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+
+/*
+ * UPnP allows a client ("control point") to send a server like us ("device")
+ * a domain name for registration, and we are supposed to resolve it. This is
+ * bad because, using the standard Linux library, we will stall the entire
+ * hostapd waiting for resolution.
+ *
+ * The "correct" solution would be to use an event driven library for domain
+ * name resolution such as "ares". However, this would increase code size
+ * further. Since it is unlikely that we'll actually see such domain names, we
+ * can just refuse to accept them.
+ */
+#define NO_DOMAIN_NAME_RESOLUTION 1 /* 1 to allow only dotted ip addresses */
+
+
+/*
+ * UPnP does not scale well. If we were in a room with thousands of control
+ * points then potentially we could be expected to handle subscriptions for
+ * each of them, which would exhaust our memory. So we must set a limit. In
+ * practice we are unlikely to see more than one or two.
+ */
+#define MAX_SUBSCRIPTIONS 4 /* how many subscribing clients we handle */
+#define MAX_ADDR_PER_SUBSCRIPTION 8
+
+/* Maximum number of Probe Request events per second */
+#define MAX_EVENTS_PER_SEC 5
+
+
+static struct upnp_wps_device_sm *shared_upnp_device = NULL;
+
+
+/* Write the current date/time per RFC */
+void format_date(struct wpabuf *buf)
+{
+ const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
+ const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
+ "Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
+ struct tm *date;
+ time_t t;
+
+ t = time(NULL);
+ date = gmtime(&t);
+ wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT",
+ &weekday_str[date->tm_wday * 4], date->tm_mday,
+ &month_str[date->tm_mon * 4], date->tm_year + 1900,
+ date->tm_hour, date->tm_min, date->tm_sec);
+}
+
+
+/***************************************************************************
+ * UUIDs (unique identifiers)
+ *
+ * These are supposed to be unique in all the world.
+ * Sometimes permanent ones are used, sometimes temporary ones
+ * based on random numbers... there are different rules for valid content
+ * of different types.
+ * Each uuid is 16 bytes long.
+ **************************************************************************/
+
+/* uuid_make -- construct a random UUID
+ * The UPnP documents don't seem to offer any guidelines as to which method to
+ * use for constructing UUIDs for subscriptions. Presumably any method from
+ * rfc4122 is good enough; I've chosen random number method.
+ */
+static void uuid_make(u8 uuid[UUID_LEN])
+{
+ os_get_random(uuid, UUID_LEN);
+
+ /* Replace certain bits as specified in rfc4122 or X.667 */
+ uuid[6] &= 0x0f; uuid[6] |= (4 << 4); /* version 4 == random gen */
+ uuid[8] &= 0x3f; uuid[8] |= 0x80;
+}
+
+
+/*
+ * Subscriber address handling.
+ * Since a subscriber may have an arbitrary number of addresses, we have to
+ * add a bunch of code to handle them.
+ *
+ * Addresses are passed in text, and MAY be domain names instead of the (usual
+ * and expected) dotted IP addresses. Resolving domain names consumes a lot of
+ * resources. Worse, we are currently using the standard Linux getaddrinfo()
+ * which will block the entire program until complete or timeout! The proper
+ * solution would be to use the "ares" library or similar with more state
+ * machine steps etc. or just disable domain name resolution by setting
+ * NO_DOMAIN_NAME_RESOLUTION to 1 at top of this file.
+ */
+
+/* subscr_addr_delete -- delete single unlinked subscriber address
+ * (be sure to unlink first if need be)
+ */
+void subscr_addr_delete(struct subscr_addr *a)
+{
+ /*
+ * Note: do NOT free domain_and_port or path because they point to
+ * memory within the allocation of "a".
+ */
+ os_free(a);
+}
+
+
+/* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */
+static void subscr_addr_free_all(struct subscription *s)
+{
+ struct subscr_addr *a, *tmp;
+ dl_list_for_each_safe(a, tmp, &s->addr_list, struct subscr_addr, list)
+ {
+ dl_list_del(&a->list);
+ subscr_addr_delete(a);
+ }
+}
+
+
+/* subscr_addr_add_url -- add address(es) for one url to subscription */
+static void subscr_addr_add_url(struct subscription *s, const char *url,
+ size_t url_len)
+{
+ int alloc_len;
+ char *scratch_mem = NULL;
+ char *mem;
+ char *domain_and_port;
+ char *delim;
+ char *path;
+ char *domain;
+ int port = 80; /* port to send to (default is port 80) */
+ struct addrinfo hints;
+ struct addrinfo *result = NULL;
+ struct addrinfo *rp;
+ int rerr;
+
+ /* url MUST begin with http: */
+ if (url_len < 7 || os_strncasecmp(url, "http://", 7))
+ goto fail;
+ url += 7;
+ url_len -= 7;
+
+ /* allocate memory for the extra stuff we need */
+ alloc_len = 2 * (url_len + 1);
+ scratch_mem = os_zalloc(alloc_len);
+ if (scratch_mem == NULL)
+ goto fail;
+ mem = scratch_mem;
+ os_strncpy(mem, url, url_len);
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", mem);
+ domain_and_port = mem;
+ mem += 1 + os_strlen(mem);
+ delim = os_strchr(domain_and_port, '/');
+ if (delim) {
+ *delim++ = 0; /* null terminate domain and port */
+ path = delim;
+ } else {
+ path = domain_and_port + os_strlen(domain_and_port);
+ }
+ domain = mem;
+ strcpy(domain, domain_and_port);
+ delim = os_strchr(domain, ':');
+ if (delim) {
+ *delim++ = 0; /* null terminate domain */
+ if (isdigit(*delim))
+ port = atol(delim);
+ }
+
+ /*
+ * getaddrinfo does the right thing with dotted decimal notations, or
+ * will resolve domain names. Resolving domain names will unfortunately
+ * hang the entire program until it is resolved or it times out
+ * internal to getaddrinfo; fortunately we think that the use of actual
+ * domain names (vs. dotted decimal notations) should be uncommon.
+ */
+ os_memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_INET; /* IPv4 */
+ hints.ai_socktype = SOCK_STREAM;
+#if NO_DOMAIN_NAME_RESOLUTION
+ /* Suppress domain name resolutions that would halt
+ * the program for periods of time
+ */
+ hints.ai_flags = AI_NUMERICHOST;
+#else
+ /* Allow domain name resolution. */
+ hints.ai_flags = 0;
+#endif
+ hints.ai_protocol = 0; /* Any protocol? */
+ rerr = getaddrinfo(domain, NULL /* fill in port ourselves */,
+ &hints, &result);
+ if (rerr) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
+ rerr, gai_strerror(rerr), domain);
+ goto fail;
+ }
+ for (rp = result; rp; rp = rp->ai_next) {
+ struct subscr_addr *a;
+
+ /* Limit no. of address to avoid denial of service attack */
+ if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
+ wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
+ "Ignoring excessive addresses");
+ break;
+ }
+
+ a = os_zalloc(sizeof(*a) + alloc_len);
+ if (a == NULL)
+ continue;
+ mem = (void *) (a + 1);
+ a->domain_and_port = mem;
+ strcpy(mem, domain_and_port);
+ mem += 1 + strlen(mem);
+ a->path = mem;
+ if (path[0] != '/')
+ *mem++ = '/';
+ strcpy(mem, path);
+ mem += 1 + os_strlen(mem);
+ os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
+ a->saddr.sin_port = htons(port);
+
+ dl_list_add(&s->addr_list, &a->list);
+ }
+
+fail:
+ if (result)
+ freeaddrinfo(result);
+ os_free(scratch_mem);
+}
+
+
+/* subscr_addr_list_create -- create list from urls in string.
+ * Each url is enclosed by angle brackets.
+ */
+static void subscr_addr_list_create(struct subscription *s,
+ const char *url_list)
+{
+ const char *end;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list);
+ for (;;) {
+ while (*url_list == ' ' || *url_list == '\t')
+ url_list++;
+ if (*url_list != '<')
+ break;
+ url_list++;
+ end = os_strchr(url_list, '>');
+ if (end == NULL)
+ break;
+ subscr_addr_add_url(s, url_list, end - url_list);
+ url_list = end + 1;
+ }
+}
+
+
+int send_wpabuf(int fd, struct wpabuf *buf)
+{
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message",
+ (unsigned long) wpabuf_len(buf));
+ errno = 0;
+ if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) !=
+ (int) wpabuf_len(buf)) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: "
+ "errno=%d (%s)",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void wpabuf_put_property(struct wpabuf *buf, const char *name,
+ const char *value)
+{
+ wpabuf_put_str(buf, "<e:property>");
+ wpabuf_printf(buf, "<%s>", name);
+ if (value)
+ wpabuf_put_str(buf, value);
+ wpabuf_printf(buf, "</%s>", name);
+ wpabuf_put_str(buf, "</e:property>\n");
+}
+
+
+/**
+ * upnp_wps_device_send_event - Queue event messages for subscribers
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ *
+ * This function queues the last WLANEvent to be sent for all currently
+ * subscribed UPnP control points. sm->wlanevent must have been set with the
+ * encoded data before calling this function.
+ */
+static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
+{
+ /* Enqueue event message for all subscribers */
+ struct wpabuf *buf; /* holds event message */
+ int buf_size = 0;
+ struct subscription *s, *tmp;
+ /* Actually, utf-8 is the default, but it doesn't hurt to specify it */
+ const char *format_head =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
+ const char *format_tail = "</e:propertyset>\n";
+ struct os_time now;
+
+ if (dl_list_empty(&sm->subscriptions)) {
+ /* optimize */
+ return;
+ }
+
+ if (os_get_time(&now) == 0) {
+ if (now.sec != sm->last_event_sec) {
+ sm->last_event_sec = now.sec;
+ sm->num_events_in_sec = 1;
+ } else {
+ sm->num_events_in_sec++;
+ /*
+ * In theory, this should apply to all WLANEvent
+ * notifications, but EAP messages are of much higher
+ * priority and Probe Request notifications should not
+ * be allowed to drop EAP messages, so only throttle
+ * Probe Request notifications.
+ */
+ if (sm->num_events_in_sec > MAX_EVENTS_PER_SEC &&
+ sm->wlanevent_type ==
+ UPNP_WPS_WLANEVENT_TYPE_PROBE) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Throttle "
+ "event notifications (%u seen "
+ "during one second)",
+ sm->num_events_in_sec);
+ return;
+ }
+ }
+ }
+
+ /* Determine buffer size needed first */
+ buf_size += os_strlen(format_head);
+ buf_size += 50 + 2 * os_strlen("WLANEvent");
+ if (sm->wlanevent)
+ buf_size += os_strlen(sm->wlanevent);
+ buf_size += os_strlen(format_tail);
+
+ buf = wpabuf_alloc(buf_size);
+ if (buf == NULL)
+ return;
+ wpabuf_put_str(buf, format_head);
+ wpabuf_put_property(buf, "WLANEvent", sm->wlanevent);
+ wpabuf_put_str(buf, format_tail);
+
+ wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s",
+ (char *) wpabuf_head(buf));
+
+ dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
+ list) {
+ event_add(s, buf,
+ sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
+ }
+
+ wpabuf_free(buf);
+}
+
+
+/*
+ * Event subscription (subscriber machines register with us to receive event
+ * messages).
+ * This is the result of an incoming HTTP over TCP SUBSCRIBE request.
+ */
+
+/* subscription_destroy -- destroy an unlinked subscription
+ * Be sure to unlink first if necessary.
+ */
+void subscription_destroy(struct subscription *s)
+{
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
+ subscr_addr_free_all(s);
+ event_delete_all(s);
+ upnp_er_remove_notification(s);
+ os_free(s);
+}
+
+
+/* subscription_list_age -- remove expired subscriptions */
+static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now)
+{
+ struct subscription *s, *tmp;
+ dl_list_for_each_safe(s, tmp, &sm->subscriptions,
+ struct subscription, list) {
+ if (s->timeout_time > now)
+ break;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription");
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ }
+}
+
+
+/* subscription_find -- return existing subscription matching uuid, if any
+ * returns NULL if not found
+ */
+struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
+ const u8 uuid[UUID_LEN])
+{
+ struct subscription *s;
+ dl_list_for_each(s, &sm->subscriptions, struct subscription, list) {
+ if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0)
+ return s; /* Found match */
+ }
+ return NULL;
+}
+
+
+static struct wpabuf * build_fake_wsc_ack(void)
+{
+ struct wpabuf *msg = wpabuf_alloc(100);
+ if (msg == NULL)
+ return NULL;
+ wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP);
+ wpabuf_put_str(msg, "00:00:00:00:00:00");
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_ACK)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ /* Enrollee Nonce */
+ wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+ wpabuf_put_be16(msg, WPS_NONCE_LEN);
+ wpabuf_put(msg, WPS_NONCE_LEN);
+ /* Registrar Nonce */
+ wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
+ wpabuf_put_be16(msg, WPS_NONCE_LEN);
+ wpabuf_put(msg, WPS_NONCE_LEN);
+ wps_build_wfa_ext(msg, 0, NULL, 0);
+ return msg;
+}
+
+
+/* subscription_first_event -- send format/queue event that is automatically
+ * sent on a new subscription.
+ */
+static int subscription_first_event(struct subscription *s)
+{
+ /*
+ * Actually, utf-8 is the default, but it doesn't hurt to specify it.
+ *
+ * APStatus is apparently a bit set,
+ * 0x1 = configuration change (but is always set?)
+ * 0x10 = ap is locked
+ *
+ * Per UPnP spec, we send out the last value of each variable, even
+ * for WLANEvent, whatever it was.
+ */
+ char *wlan_event;
+ struct wpabuf *buf;
+ int ap_status = 1; /* TODO: add 0x10 if access point is locked */
+ const char *head =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
+ const char *tail = "</e:propertyset>\n";
+ char txt[10];
+ int ret;
+
+ if (s->sm->wlanevent == NULL) {
+ /*
+ * There has been no events before the subscription. However,
+ * UPnP device architecture specification requires all the
+ * evented variables to be included, so generate a dummy event
+ * for this particular case using a WSC_ACK and all-zeros
+ * nonces. The ER (UPnP control point) will ignore this, but at
+ * least it will learn that WLANEvent variable will be used in
+ * event notifications in the future.
+ */
+ struct wpabuf *msg;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the "
+ "initial WLANEvent");
+ msg = build_fake_wsc_ack();
+ if (msg) {
+ s->sm->wlanevent = (char *)
+ base64_encode(wpabuf_head(msg),
+ wpabuf_len(msg), NULL);
+ wpabuf_free(msg);
+ }
+ }
+
+ wlan_event = s->sm->wlanevent;
+ if (wlan_event == NULL || *wlan_event == '\0') {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for "
+ "initial event message");
+ wlan_event = "";
+ }
+ buf = wpabuf_alloc(500 + os_strlen(wlan_event));
+ if (buf == NULL)
+ return -1;
+
+ wpabuf_put_str(buf, head);
+ wpabuf_put_property(buf, "STAStatus", "1");
+ os_snprintf(txt, sizeof(txt), "%d", ap_status);
+ wpabuf_put_property(buf, "APStatus", txt);
+ if (*wlan_event)
+ wpabuf_put_property(buf, "WLANEvent", wlan_event);
+ wpabuf_put_str(buf, tail);
+
+ ret = event_add(s, buf, 0);
+ if (ret) {
+ wpabuf_free(buf);
+ return ret;
+ }
+ wpabuf_free(buf);
+
+ return 0;
+}
+
+
+/**
+ * subscription_start - Remember a UPnP control point to send events to.
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @callback_urls: Callback URLs
+ * Returns: %NULL on error, or pointer to new subscription structure.
+ */
+struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
+ const char *callback_urls)
+{
+ struct subscription *s;
+ time_t now = time(NULL);
+ time_t expire = now + UPNP_SUBSCRIBE_SEC;
+
+ /* Get rid of expired subscriptions so we have room */
+ subscription_list_age(sm, now);
+
+ /* If too many subscriptions, remove oldest */
+ if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) {
+ s = dl_list_first(&sm->subscriptions, struct subscription,
+ list);
+ wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, "
+ "trashing oldest");
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ }
+
+ s = os_zalloc(sizeof(*s));
+ if (s == NULL)
+ return NULL;
+ dl_list_init(&s->addr_list);
+ dl_list_init(&s->event_queue);
+
+ s->sm = sm;
+ s->timeout_time = expire;
+ uuid_make(s->uuid);
+ subscr_addr_list_create(s, callback_urls);
+ if (dl_list_empty(&s->addr_list)) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in "
+ "'%s' - drop subscription", callback_urls);
+ subscription_destroy(s);
+ return NULL;
+ }
+
+ /* Add to end of list, since it has the highest expiration time */
+ dl_list_add_tail(&sm->subscriptions, &s->list);
+ /* Queue up immediate event message (our last event)
+ * as required by UPnP spec.
+ */
+ if (subscription_first_event(s)) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to "
+ "event backlog");
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s",
+ s, callback_urls);
+ /* Schedule sending this */
+ event_send_all_later(sm);
+ return s;
+}
+
+
+/* subscription_renew -- find subscription and reset timeout */
+struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
+ const u8 uuid[UUID_LEN])
+{
+ time_t now = time(NULL);
+ time_t expire = now + UPNP_SUBSCRIBE_SEC;
+ struct subscription *s = subscription_find(sm, uuid);
+ if (s == NULL)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed");
+ dl_list_del(&s->list);
+ s->timeout_time = expire;
+ /* add back to end of list, since it now has highest expiry */
+ dl_list_add_tail(&sm->subscriptions, &s->list);
+ return s;
+}
+
+
+/**
+ * upnp_wps_device_send_wlan_event - Event notification
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @from_mac_addr: Source (Enrollee) MAC address for the event
+ * @ev_type: Event type
+ * @msg: Event data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Tell external Registrars (UPnP control points) that something happened. In
+ * particular, events include WPS messages from clients that are proxied to
+ * external Registrars.
+ */
+int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
+ const u8 from_mac_addr[ETH_ALEN],
+ enum upnp_wps_wlanevent_type ev_type,
+ const struct wpabuf *msg)
+{
+ int ret = -1;
+ char type[2];
+ const u8 *mac = from_mac_addr;
+ char mac_text[18];
+ u8 *raw = NULL;
+ size_t raw_len;
+ char *val;
+ size_t val_len;
+ int pos = 0;
+
+ if (!sm)
+ goto fail;
+
+ os_snprintf(type, sizeof(type), "%1u", ev_type);
+
+ raw_len = 1 + 17 + (msg ? wpabuf_len(msg) : 0);
+ raw = os_zalloc(raw_len);
+ if (!raw)
+ goto fail;
+
+ *(raw + pos) = (u8) ev_type;
+ pos += 1;
+ os_snprintf(mac_text, sizeof(mac_text), MACSTR, MAC2STR(mac));
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Proxying WLANEvent from %s",
+ mac_text);
+ os_memcpy(raw + pos, mac_text, 17);
+ pos += 17;
+ if (msg) {
+ os_memcpy(raw + pos, wpabuf_head(msg), wpabuf_len(msg));
+ pos += wpabuf_len(msg);
+ }
+ raw_len = pos;
+
+ val = (char *) base64_encode(raw, raw_len, &val_len);
+ if (val == NULL)
+ goto fail;
+
+ os_free(sm->wlanevent);
+ sm->wlanevent = val;
+ sm->wlanevent_type = ev_type;
+ upnp_wps_device_send_event(sm);
+
+ ret = 0;
+
+fail:
+ os_free(raw);
+
+ return ret;
+}
+
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <sys/sysctl.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+static int eth_get(const char *device, u8 ea[ETH_ALEN])
+{
+ struct if_msghdr *ifm;
+ struct sockaddr_dl *sdl;
+ u_char *p, *buf;
+ size_t len;
+ int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return -1;
+ if ((buf = os_malloc(len)) == NULL)
+ return -1;
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ os_free(buf);
+ return -1;
+ }
+ for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)p;
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (ifm->ifm_type != RTM_IFINFO ||
+ (ifm->ifm_addrs & RTA_IFP) == 0)
+ continue;
+ if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
+ os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
+ continue;
+ os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
+ break;
+ }
+ os_free(buf);
+
+ if (p >= buf + len) {
+ errno = ESRCH;
+ return -1;
+ }
+ return 0;
+}
+#endif /* __FreeBSD__ */
+
+
+/**
+ * get_netif_info - Get hw and IP addresses for network device
+ * @net_if: Selected network interface name
+ * @ip_addr: Buffer for returning IP address in network byte order
+ * @ip_addr_text: Buffer for returning a pointer to allocated IP address text
+ * @mac: Buffer for returning MAC address
+ * Returns: 0 on success, -1 on failure
+ */
+int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
+ u8 mac[ETH_ALEN])
+{
+ struct ifreq req;
+ int sock = -1;
+ struct sockaddr_in *addr;
+ struct in_addr in_addr;
+
+ *ip_addr_text = os_zalloc(16);
+ if (*ip_addr_text == NULL)
+ goto fail;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ goto fail;
+
+ os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
+ if (ioctl(sock, SIOCGIFADDR, &req) < 0) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFADDR failed: %d (%s)",
+ errno, strerror(errno));
+ goto fail;
+ }
+ addr = (void *) &req.ifr_addr;
+ *ip_addr = addr->sin_addr.s_addr;
+ in_addr.s_addr = *ip_addr;
+ os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr));
+
+#ifdef __linux__
+ os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
+ if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: "
+ "%d (%s)", errno, strerror(errno));
+ goto fail;
+ }
+ os_memcpy(mac, req.ifr_addr.sa_data, 6);
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ if (eth_get(net_if, mac) < 0) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address");
+ goto fail;
+ }
+#else
+#error MAC address fetch not implemented
+#endif
+
+ close(sock);
+ return 0;
+
+fail:
+ if (sock >= 0)
+ close(sock);
+ os_free(*ip_addr_text);
+ *ip_addr_text = NULL;
+ return -1;
+}
+
+
+static void upnp_wps_free_msearchreply(struct dl_list *head)
+{
+ struct advertisement_state_machine *a, *tmp;
+ dl_list_for_each_safe(a, tmp, head, struct advertisement_state_machine,
+ list)
+ msearchreply_state_machine_stop(a);
+}
+
+
+static void upnp_wps_free_subscriptions(struct dl_list *head,
+ struct wps_registrar *reg)
+{
+ struct subscription *s, *tmp;
+ dl_list_for_each_safe(s, tmp, head, struct subscription, list) {
+ if (reg && s->reg != reg)
+ continue;
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ }
+}
+
+
+/**
+ * upnp_wps_device_stop - Stop WPS UPnP operations on an interface
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ */
+static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
+{
+ if (!sm || !sm->started)
+ return;
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
+ web_listener_stop(sm);
+ upnp_wps_free_msearchreply(&sm->msearch_replies);
+ upnp_wps_free_subscriptions(&sm->subscriptions, NULL);
+
+ advertisement_state_machine_stop(sm, 1);
+
+ event_send_stop_all(sm);
+ os_free(sm->wlanevent);
+ sm->wlanevent = NULL;
+ os_free(sm->ip_addr_text);
+ sm->ip_addr_text = NULL;
+ if (sm->multicast_sd >= 0)
+ close(sm->multicast_sd);
+ sm->multicast_sd = -1;
+ ssdp_listener_stop(sm);
+
+ sm->started = 0;
+}
+
+
+/**
+ * upnp_wps_device_start - Start WPS UPnP operations on an interface
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @net_if: Selected network interface name
+ * Returns: 0 on success, -1 on failure
+ */
+static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
+{
+ if (!sm || !net_if)
+ return -1;
+
+ if (sm->started)
+ upnp_wps_device_stop(sm);
+
+ sm->multicast_sd = -1;
+ sm->ssdp_sd = -1;
+ sm->started = 1;
+ sm->advertise_count = 0;
+
+ /* Fix up linux multicast handling */
+ if (add_ssdp_network(net_if))
+ goto fail;
+
+ /* Determine which IP and mac address we're using */
+ if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text,
+ sm->mac_addr)) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
+ "for %s. Does it have IP address?", net_if);
+ goto fail;
+ }
+
+ /* Listen for incoming TCP connections so that others
+ * can fetch our "xml files" from us.
+ */
+ if (web_listener_start(sm))
+ goto fail;
+
+ /* Set up for receiving discovery (UDP) packets */
+ if (ssdp_listener_start(sm))
+ goto fail;
+
+ /* Set up for sending multicast */
+ if (ssdp_open_multicast(sm) < 0)
+ goto fail;
+
+ /*
+ * Broadcast NOTIFY messages to let the world know we exist.
+ * This is done via a state machine since the messages should not be
+ * all sent out at once.
+ */
+ if (advertisement_state_machine_start(sm))
+ goto fail;
+
+ return 0;
+
+fail:
+ upnp_wps_device_stop(sm);
+ return -1;
+}
+
+
+static struct upnp_wps_device_interface *
+upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv)
+{
+ struct upnp_wps_device_interface *iface;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (iface->priv == priv)
+ return iface;
+ }
+ return NULL;
+}
+
+
+/**
+ * upnp_wps_device_deinit - Deinitialize WPS UPnP
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @priv: External context data that was used in upnp_wps_device_init() call
+ */
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
+{
+ struct upnp_wps_device_interface *iface;
+
+ if (!sm)
+ return;
+
+ iface = upnp_wps_get_iface(sm, priv);
+ if (iface == NULL) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface "
+ "instance to deinit");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface);
+ if (dl_list_len(&sm->interfaces) == 1) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance "
+ "- free global device instance");
+ upnp_wps_device_stop(sm);
+ } else
+ upnp_wps_free_subscriptions(&sm->subscriptions,
+ iface->wps->registrar);
+ dl_list_del(&iface->list);
+
+ if (iface->peer.wps)
+ wps_deinit(iface->peer.wps);
+ os_free(iface->ctx->ap_pin);
+ os_free(iface->ctx);
+ os_free(iface);
+
+ if (dl_list_empty(&sm->interfaces)) {
+ os_free(sm->root_dir);
+ os_free(sm->desc_url);
+ os_free(sm);
+ shared_upnp_device = NULL;
+ }
+}
+
+
+/**
+ * upnp_wps_device_init - Initialize WPS UPnP
+ * @ctx: callback table; we must eventually free it
+ * @wps: Pointer to longterm WPS context
+ * @priv: External context data that will be used in callbacks
+ * @net_if: Selected network interface name
+ * Returns: WPS UPnP state or %NULL on failure
+ */
+struct upnp_wps_device_sm *
+upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
+ void *priv, char *net_if)
+{
+ struct upnp_wps_device_sm *sm;
+ struct upnp_wps_device_interface *iface;
+ int start = 0;
+
+ iface = os_zalloc(sizeof(*iface));
+ if (iface == NULL) {
+ os_free(ctx->ap_pin);
+ os_free(ctx);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface);
+
+ iface->ctx = ctx;
+ iface->wps = wps;
+ iface->priv = priv;
+
+ if (shared_upnp_device) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device "
+ "context");
+ sm = shared_upnp_device;
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context");
+ sm = os_zalloc(sizeof(*sm));
+ if (!sm) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init "
+ "failed");
+ os_free(iface);
+ os_free(ctx->ap_pin);
+ os_free(ctx);
+ return NULL;
+ }
+ shared_upnp_device = sm;
+
+ dl_list_init(&sm->msearch_replies);
+ dl_list_init(&sm->subscriptions);
+ dl_list_init(&sm->interfaces);
+ start = 1;
+ }
+
+ dl_list_add(&sm->interfaces, &iface->list);
+
+ if (start && upnp_wps_device_start(sm, net_if)) {
+ upnp_wps_device_deinit(sm, priv);
+ return NULL;
+ }
+
+
+ return sm;
+}
+
+
+/**
+ * upnp_wps_subscribers - Check whether there are any event subscribers
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 if no subscribers, 1 if subscribers
+ */
+int upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
+{
+ return !dl_list_empty(&sm->subscriptions);
+}
+
+
+int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
+{
+ struct upnp_wps_device_interface *iface;
+ if (sm == NULL)
+ return 0;
+
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ os_free(iface->ctx->ap_pin);
+ if (ap_pin) {
+ iface->ctx->ap_pin = os_strdup(ap_pin);
+ if (iface->ctx->ap_pin == NULL)
+ return -1;
+ } else
+ iface->ctx->ap_pin = NULL;
+ }
+
+ return 0;
+}
diff --git a/src/wps/wps_upnp.h b/src/wps/wps_upnp.h
new file mode 100644
index 0000000..87b7ab1
--- /dev/null
+++ b/src/wps/wps_upnp.h
@@ -0,0 +1,48 @@
+/*
+ * UPnP WPS Device
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef WPS_UPNP_H
+#define WPS_UPNP_H
+
+struct upnp_wps_device_sm;
+struct wps_context;
+struct wps_data;
+
+struct upnp_wps_peer {
+ struct wps_data *wps;
+};
+
+enum upnp_wps_wlanevent_type {
+ UPNP_WPS_WLANEVENT_TYPE_PROBE = 1,
+ UPNP_WPS_WLANEVENT_TYPE_EAP = 2
+};
+
+struct upnp_wps_device_ctx {
+ int (*rx_req_put_wlan_response)(
+ void *priv, enum upnp_wps_wlanevent_type ev_type,
+ const u8 *mac_addr, const struct wpabuf *msg,
+ enum wps_msg_type msg_type);
+
+ char *ap_pin;
+};
+
+struct upnp_wps_device_sm *
+upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
+ void *priv, char *net_if);
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv);
+
+int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
+ const u8 from_mac_addr[ETH_ALEN],
+ enum upnp_wps_wlanevent_type ev_type,
+ const struct wpabuf *msg);
+int upnp_wps_subscribers(struct upnp_wps_device_sm *sm);
+int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin);
+
+#endif /* WPS_UPNP_H */
diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c
new file mode 100644
index 0000000..501ecbc
--- /dev/null
+++ b/src/wps/wps_upnp_ap.c
@@ -0,0 +1,91 @@
+/*
+ * Wi-Fi Protected Setup - UPnP AP functionality
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "uuid.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+
+static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct subscription *s = eloop_ctx;
+ wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out");
+ s->selected_registrar = 0;
+ wps_registrar_selected_registrar_changed(s->reg);
+}
+
+
+int upnp_er_set_selected_registrar(struct wps_registrar *reg,
+ struct subscription *s,
+ const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
+ msg);
+ if (wps_validate_upnp_set_selected_registrar(msg) < 0)
+ return -1;
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return -1;
+
+ s->reg = reg;
+ eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL);
+
+ os_memset(s->authorized_macs, 0, sizeof(s->authorized_macs));
+ if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable "
+ "Selected Registrar");
+ s->selected_registrar = 0;
+ } else {
+ s->selected_registrar = 1;
+ s->dev_password_id = attr.dev_password_id ?
+ WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT;
+ s->config_methods = attr.sel_reg_config_methods ?
+ WPA_GET_BE16(attr.sel_reg_config_methods) : -1;
+ if (attr.authorized_macs) {
+ int count = attr.authorized_macs_len / ETH_ALEN;
+ if (count > WPS_MAX_AUTHORIZED_MACS)
+ count = WPS_MAX_AUTHORIZED_MACS;
+ os_memcpy(s->authorized_macs, attr.authorized_macs,
+ count * ETH_ALEN);
+ } else if (!attr.version2) {
+#ifdef CONFIG_WPS2
+ wpa_printf(MSG_DEBUG, "WPS: Add broadcast "
+ "AuthorizedMACs for WPS 1.0 ER");
+ os_memset(s->authorized_macs, 0xff, ETH_ALEN);
+#endif /* CONFIG_WPS2 */
+ }
+ eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+ upnp_er_set_selected_timeout, s, NULL);
+ }
+
+ wps_registrar_selected_registrar_changed(reg);
+
+ return 0;
+}
+
+
+void upnp_er_remove_notification(struct subscription *s)
+{
+ s->selected_registrar = 0;
+ eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL);
+ if (s->reg)
+ wps_registrar_selected_registrar_changed(s->reg);
+}
diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c
new file mode 100644
index 0000000..2c8ed4f
--- /dev/null
+++ b/src/wps/wps_upnp_event.c
@@ -0,0 +1,423 @@
+/*
+ * UPnP WPS Device - Event processing
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+#include <assert.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "uuid.h"
+#include "http_client.h"
+#include "wps_defs.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+/*
+ * Event message generation (to subscribers)
+ *
+ * We make a separate copy for each message for each subscriber. This memory
+ * wasted could be limited (adding code complexity) by sharing copies, keeping
+ * a usage count and freeing when zero.
+ *
+ * Sending a message requires using a HTTP over TCP NOTIFY
+ * (like a PUT) which requires a number of states..
+ */
+
+#define MAX_EVENTS_QUEUED 20 /* How far behind queued events */
+#define MAX_FAILURES 10 /* Drop subscription after this many failures */
+
+/* How long to wait before sending event */
+#define EVENT_DELAY_SECONDS 0
+#define EVENT_DELAY_MSEC 0
+
+/*
+ * Event information that we send to each subscriber is remembered in this
+ * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
+ * over TCP transaction which requires various states.. It may also need to be
+ * retried at a different address (if more than one is available).
+ *
+ * TODO: As an optimization we could share data between subscribers.
+ */
+struct wps_event_ {
+ struct dl_list list;
+ struct subscription *s; /* parent */
+ unsigned subscriber_sequence; /* which event for this subscription*/
+ unsigned int retry; /* which retry */
+ struct subscr_addr *addr; /* address to connect to */
+ struct wpabuf *data; /* event data to send */
+ struct http_client *http_event;
+};
+
+
+/* event_clean -- clean sockets etc. of event
+ * Leaves data, retry count etc. alone.
+ */
+static void event_clean(struct wps_event_ *e)
+{
+ if (e->s->current_event == e)
+ e->s->current_event = NULL;
+ http_client_free(e->http_event);
+ e->http_event = NULL;
+}
+
+
+/* event_delete -- delete single unqueued event
+ * (be sure to dequeue first if need be)
+ */
+static void event_delete(struct wps_event_ *e)
+{
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e);
+ event_clean(e);
+ wpabuf_free(e->data);
+ os_free(e);
+}
+
+
+/* event_dequeue -- get next event from the queue
+ * Returns NULL if empty.
+ */
+static struct wps_event_ *event_dequeue(struct subscription *s)
+{
+ struct wps_event_ *e;
+ e = dl_list_first(&s->event_queue, struct wps_event_, list);
+ if (e) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
+ "subscription %p", e, s);
+ dl_list_del(&e->list);
+ }
+ return e;
+}
+
+
+/* event_delete_all -- delete entire event queue and current event */
+void event_delete_all(struct subscription *s)
+{
+ struct wps_event_ *e;
+ while ((e = event_dequeue(s)) != NULL)
+ event_delete(e);
+ if (s->current_event) {
+ event_delete(s->current_event);
+ /* will set: s->current_event = NULL; */
+ }
+}
+
+
+/**
+ * event_retry - Called when we had a failure delivering event msg
+ * @e: Event
+ * @do_next_address: skip address e.g. on connect fail
+ */
+static void event_retry(struct wps_event_ *e, int do_next_address)
+{
+ struct subscription *s = e->s;
+ struct upnp_wps_device_sm *sm = s->sm;
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
+ e, s);
+ event_clean(e);
+ /* will set: s->current_event = NULL; */
+
+ if (do_next_address) {
+ e->retry++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
+ }
+ if (e->retry >= dl_list_len(&s->addr_list)) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
+ "for %s", e->addr->domain_and_port);
+ event_delete(e);
+ s->last_event_failed = 1;
+ if (!dl_list_empty(&s->event_queue))
+ event_send_all_later(s->sm);
+ return;
+ }
+ dl_list_add(&s->event_queue, &e->list);
+ event_send_all_later(sm);
+}
+
+
+static struct wpabuf * event_build_message(struct wps_event_ *e)
+{
+ struct wpabuf *buf;
+ char *b;
+
+ buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
+ if (buf == NULL)
+ return NULL;
+ wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
+ wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
+ wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
+ wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
+ "NT: upnp:event\r\n"
+ "NTS: upnp:propchange\r\n");
+ wpabuf_put_str(buf, "SID: uuid:");
+ b = wpabuf_put(buf, 0);
+ uuid_bin2str(e->s->uuid, b, 80);
+ wpabuf_put(buf, os_strlen(b));
+ wpabuf_put_str(buf, "\r\n");
+ wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
+ wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
+ (int) wpabuf_len(e->data));
+ wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
+ wpabuf_put_buf(buf, e->data);
+ return buf;
+}
+
+
+static void event_addr_failure(struct wps_event_ *e)
+{
+ struct subscription *s = e->s;
+
+ e->addr->num_failures++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
+ "(num_failures=%u)",
+ e, e->addr->domain_and_port, e->addr->num_failures);
+
+ if (e->addr->num_failures < MAX_FAILURES) {
+ /* Try other addresses, if available */
+ event_retry(e, 1);
+ return;
+ }
+
+ /*
+ * If other side doesn't like what we say, forget about them.
+ * (There is no way to tell other side that we are dropping them...).
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p "
+ "address %s due to errors", s, e->addr->domain_and_port);
+ dl_list_del(&e->addr->list);
+ subscr_addr_delete(e->addr);
+ e->addr = NULL;
+
+ if (dl_list_empty(&s->addr_list)) {
+ /* if we've given up on all addresses */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p "
+ "with no addresses", s);
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ return;
+ }
+
+ /* Try other addresses, if available */
+ event_retry(e, 0);
+}
+
+
+static void event_http_cb(void *ctx, struct http_client *c,
+ enum http_client_event event)
+{
+ struct wps_event_ *e = ctx;
+ struct subscription *s = e->s;
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
+ "event=%d", e, c, event);
+ switch (event) {
+ case HTTP_CLIENT_OK:
+ wpa_printf(MSG_DEBUG,
+ "WPS UPnP: Got event %p reply OK from %s",
+ e, e->addr->domain_and_port);
+ e->addr->num_failures = 0;
+ s->last_event_failed = 0;
+ event_delete(e);
+
+ /* Schedule sending more if there is more to send */
+ if (!dl_list_empty(&s->event_queue))
+ event_send_all_later(s->sm);
+ break;
+ case HTTP_CLIENT_FAILED:
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
+ event_addr_failure(e);
+ break;
+ case HTTP_CLIENT_INVALID_REPLY:
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply");
+ event_addr_failure(e);
+ break;
+ case HTTP_CLIENT_TIMEOUT:
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
+ event_addr_failure(e);
+ break;
+ }
+}
+
+
+/* event_send_start -- prepare to send a event message to subscriber
+ *
+ * This gets complicated because:
+ * -- The message is sent via TCP and we have to keep the stream open
+ * for 30 seconds to get a response... then close it.
+ * -- But we might have other event happen in the meantime...
+ * we have to queue them, if we lose them then the subscriber will
+ * be forced to unsubscribe and subscribe again.
+ * -- If multiple URLs are provided then we are supposed to try successive
+ * ones after 30 second timeout.
+ * -- The URLs might use domain names instead of dotted decimal addresses,
+ * and resolution of those may cause unwanted sleeping.
+ * -- Doing the initial TCP connect can take a while, so we have to come
+ * back after connection and then send the data.
+ *
+ * Returns nonzero on error;
+ *
+ * Prerequisite: No current event send (s->current_event == NULL)
+ * and non-empty queue.
+ */
+static int event_send_start(struct subscription *s)
+{
+ struct wps_event_ *e;
+ unsigned int itry;
+ struct wpabuf *buf;
+
+ /*
+ * Assume we are called ONLY with no current event and ONLY with
+ * nonempty event queue and ONLY with at least one address to send to.
+ */
+ if (dl_list_empty(&s->addr_list))
+ return -1;
+ if (s->current_event)
+ return -1;
+ if (dl_list_empty(&s->event_queue))
+ return -1;
+
+ s->current_event = e = event_dequeue(s);
+
+ /* Use address according to number of retries */
+ itry = 0;
+ dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
+ if (itry++ == e->retry)
+ break;
+ if (itry < e->retry)
+ return -1;
+
+ buf = event_build_message(e);
+ if (buf == NULL) {
+ event_retry(e, 0);
+ return -1;
+ }
+
+ e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
+ event_http_cb, e);
+ if (e->http_event == NULL) {
+ wpabuf_free(buf);
+ event_retry(e, 0);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* event_send_all_later_handler -- actually send events as needed */
+static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
+{
+ struct upnp_wps_device_sm *sm = user_ctx;
+ struct subscription *s, *tmp;
+ int nerrors = 0;
+
+ sm->event_send_all_queued = 0;
+ dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
+ list) {
+ if (s->current_event == NULL /* not busy */ &&
+ !dl_list_empty(&s->event_queue) /* more to do */) {
+ if (event_send_start(s))
+ nerrors++;
+ }
+ }
+
+ if (nerrors) {
+ /* Try again later */
+ event_send_all_later(sm);
+ }
+}
+
+
+/* event_send_all_later -- schedule sending events to all subscribers
+ * that need it.
+ * This avoids two problems:
+ * -- After getting a subscription, we should not send the first event
+ * until after our reply is fully queued to be sent back,
+ * -- Possible stack depth or infinite recursion issues.
+ */
+void event_send_all_later(struct upnp_wps_device_sm *sm)
+{
+ /*
+ * The exact time in the future isn't too important. Waiting a bit
+ * might let us do several together.
+ */
+ if (sm->event_send_all_queued)
+ return;
+ sm->event_send_all_queued = 1;
+ eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
+ event_send_all_later_handler, NULL, sm);
+}
+
+
+/* event_send_stop_all -- cleanup */
+void event_send_stop_all(struct upnp_wps_device_sm *sm)
+{
+ if (sm->event_send_all_queued)
+ eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
+ sm->event_send_all_queued = 0;
+}
+
+
+/**
+ * event_add - Add a new event to a queue
+ * @s: Subscription
+ * @data: Event data (is copied; caller retains ownership)
+ * @probereq: Whether this is a Probe Request event
+ * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
+ */
+int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
+{
+ struct wps_event_ *e;
+ unsigned int len;
+
+ len = dl_list_len(&s->event_queue);
+ if (len >= MAX_EVENTS_QUEUED) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
+ "subscriber %p", s);
+ if (probereq)
+ return 1;
+
+ /* Drop oldest entry to allow EAP event to be stored. */
+ e = event_dequeue(s);
+ if (!e)
+ return 1;
+ event_delete(e);
+ }
+
+ if (s->last_event_failed && probereq && len > 0) {
+ /*
+ * Avoid queuing frames for subscribers that may have left
+ * without unsubscribing.
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe "
+ "Request frames for subscription %p since last "
+ "delivery failed", s);
+ return -1;
+ }
+
+ e = os_zalloc(sizeof(*e));
+ if (e == NULL)
+ return -1;
+ dl_list_init(&e->list);
+ e->s = s;
+ e->data = wpabuf_dup(data);
+ if (e->data == NULL) {
+ os_free(e);
+ return -1;
+ }
+ e->subscriber_sequence = s->next_subscriber_sequence++;
+ if (s->next_subscriber_sequence == 0)
+ s->next_subscriber_sequence++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
+ "(queue len %u)", e, s, len + 1);
+ dl_list_add_tail(&s->event_queue, &e->list);
+ event_send_all_later(s->sm);
+ return 0;
+}
diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h
new file mode 100644
index 0000000..3ecf05d
--- /dev/null
+++ b/src/wps/wps_upnp_i.h
@@ -0,0 +1,193 @@
+/*
+ * UPnP for WPS / internal definitions
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef WPS_UPNP_I_H
+#define WPS_UPNP_I_H
+
+#include "utils/list.h"
+#include "http.h"
+
+#define UPNP_MULTICAST_ADDRESS "239.255.255.250" /* for UPnP multicasting */
+#define UPNP_MULTICAST_PORT 1900 /* UDP port to monitor for UPnP */
+
+/* min subscribe time per UPnP standard */
+#define UPNP_SUBSCRIBE_SEC_MIN 1800
+/* subscribe time we use */
+#define UPNP_SUBSCRIBE_SEC (UPNP_SUBSCRIBE_SEC_MIN + 1)
+
+/* "filenames" used in URLs that we service via our "web server": */
+#define UPNP_WPS_DEVICE_XML_FILE "wps_device.xml"
+#define UPNP_WPS_SCPD_XML_FILE "wps_scpd.xml"
+#define UPNP_WPS_DEVICE_CONTROL_FILE "wps_control"
+#define UPNP_WPS_DEVICE_EVENT_FILE "wps_event"
+
+#define MULTICAST_MAX_READ 1600 /* max bytes we'll read for UPD request */
+
+
+struct upnp_wps_device_sm;
+struct wps_registrar;
+
+
+enum advertisement_type_enum {
+ ADVERTISE_UP = 0,
+ ADVERTISE_DOWN = 1,
+ MSEARCH_REPLY = 2
+};
+
+/*
+ * Advertisements are broadcast via UDP NOTIFYs, and are also the essence of
+ * the reply to UDP M-SEARCH requests. This struct handles both cases.
+ *
+ * A state machine is needed because a number of variant forms must be sent in
+ * separate packets and spread out in time to avoid congestion.
+ */
+struct advertisement_state_machine {
+ struct dl_list list;
+ enum advertisement_type_enum type;
+ int state;
+ int nerrors;
+ struct sockaddr_in client; /* for M-SEARCH replies */
+};
+
+
+/*
+ * An address of a subscriber (who may have multiple addresses). We are
+ * supposed to send (via TCP) updates to each subscriber, trying each address
+ * for a subscriber until we find one that seems to work.
+ */
+struct subscr_addr {
+ struct dl_list list;
+ char *domain_and_port; /* domain and port part of url */
+ char *path; /* "filepath" part of url (from "mem") */
+ struct sockaddr_in saddr; /* address for doing connect */
+ unsigned num_failures;
+};
+
+
+/*
+ * Subscribers to our events are recorded in this struct. This includes a max
+ * of one outgoing connection (sending an "event message") per subscriber. We
+ * also have to age out subscribers unless they renew.
+ */
+struct subscription {
+ struct dl_list list;
+ struct upnp_wps_device_sm *sm; /* parent */
+ time_t timeout_time; /* when to age out the subscription */
+ unsigned next_subscriber_sequence; /* number our messages */
+ /*
+ * This uuid identifies the subscription and is randomly generated by
+ * us and given to the subscriber when the subscription is accepted;
+ * and is then included with each event sent to the subscriber.
+ */
+ u8 uuid[UUID_LEN];
+ /* Linked list of address alternatives (rotate through on failure) */
+ struct dl_list addr_list;
+ struct dl_list event_queue; /* Queued event messages. */
+ struct wps_event_ *current_event; /* non-NULL if being sent (not in q)
+ */
+ int last_event_failed; /* Whether delivery of last event failed */
+
+ /* Information from SetSelectedRegistrar action */
+ u8 selected_registrar;
+ u16 dev_password_id;
+ u16 config_methods;
+ u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+ struct wps_registrar *reg;
+};
+
+
+struct upnp_wps_device_interface {
+ struct dl_list list;
+ struct upnp_wps_device_ctx *ctx; /* callback table */
+ struct wps_context *wps;
+ void *priv;
+
+ /* FIX: maintain separate structures for each UPnP peer */
+ struct upnp_wps_peer peer;
+};
+
+/*
+ * Our instance data corresponding to the AP device. Note that there may be
+ * multiple wireless interfaces sharing the same UPnP device instance. Each
+ * such interface is stored in the list of struct upnp_wps_device_interface
+ * instances.
+ *
+ * This is known as an opaque struct declaration to users of the WPS UPnP code.
+ */
+struct upnp_wps_device_sm {
+ struct dl_list interfaces; /* struct upnp_wps_device_interface */
+ char *root_dir;
+ char *desc_url;
+ int started; /* nonzero if we are active */
+ u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
+ char *ip_addr_text; /* IP address of network i.f. we use */
+ unsigned ip_addr; /* IP address of network i.f. we use (host order) */
+ int multicast_sd; /* send multicast messages over this socket */
+ int ssdp_sd; /* receive discovery UPD packets on socket */
+ int ssdp_sd_registered; /* nonzero if we must unregister */
+ unsigned advertise_count; /* how many advertisements done */
+ struct advertisement_state_machine advertisement;
+ struct dl_list msearch_replies;
+ int web_port; /* our port that others get xml files from */
+ struct http_server *web_srv;
+ /* Note: subscriptions are kept in expiry order */
+ struct dl_list subscriptions;
+ int event_send_all_queued; /* if we are scheduled to send events soon
+ */
+
+ char *wlanevent; /* the last WLANEvent data */
+ enum upnp_wps_wlanevent_type wlanevent_type;
+ os_time_t last_event_sec;
+ unsigned int num_events_in_sec;
+};
+
+/* wps_upnp.c */
+void format_date(struct wpabuf *buf);
+struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
+ const char *callback_urls);
+struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
+ const u8 uuid[UUID_LEN]);
+void subscription_destroy(struct subscription *s);
+struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
+ const u8 uuid[UUID_LEN]);
+void subscr_addr_delete(struct subscr_addr *a);
+int send_wpabuf(int fd, struct wpabuf *buf);
+int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
+ u8 mac[ETH_ALEN]);
+
+/* wps_upnp_ssdp.c */
+void msearchreply_state_machine_stop(struct advertisement_state_machine *a);
+int advertisement_state_machine_start(struct upnp_wps_device_sm *sm);
+void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm,
+ int send_byebye);
+void ssdp_listener_stop(struct upnp_wps_device_sm *sm);
+int ssdp_listener_start(struct upnp_wps_device_sm *sm);
+int ssdp_listener_open(void);
+int add_ssdp_network(const char *net_if);
+int ssdp_open_multicast_sock(u32 ip_addr);
+int ssdp_open_multicast(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_web.c */
+int web_listener_start(struct upnp_wps_device_sm *sm);
+void web_listener_stop(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_event.c */
+int event_add(struct subscription *s, const struct wpabuf *data, int probereq);
+void event_delete_all(struct subscription *s);
+void event_send_all_later(struct upnp_wps_device_sm *sm);
+void event_send_stop_all(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_ap.c */
+int upnp_er_set_selected_registrar(struct wps_registrar *reg,
+ struct subscription *s,
+ const struct wpabuf *msg);
+void upnp_er_remove_notification(struct subscription *s);
+
+#endif /* WPS_UPNP_I_H */
diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c
new file mode 100644
index 0000000..c7fb158
--- /dev/null
+++ b/src/wps/wps_upnp_ssdp.c
@@ -0,0 +1,938 @@
+/*
+ * UPnP SSDP for WPS
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <net/route.h>
+
+#include "common.h"
+#include "uuid.h"
+#include "eloop.h"
+#include "wps.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+#define UPNP_CACHE_SEC (UPNP_CACHE_SEC_MIN + 1) /* cache time we use */
+#define UPNP_CACHE_SEC_MIN 1800 /* min cachable time per UPnP standard */
+#define UPNP_ADVERTISE_REPEAT 2 /* no more than 3 */
+#define MAX_MSEARCH 20 /* max simultaneous M-SEARCH replies ongoing */
+#define SSDP_TARGET "239.0.0.0"
+#define SSDP_NETMASK "255.0.0.0"
+
+
+/* Check tokens for equality, where tokens consist of letters, digits,
+ * underscore and hyphen, and are matched case insensitive.
+ */
+static int token_eq(const char *s1, const char *s2)
+{
+ int c1;
+ int c2;
+ int end1 = 0;
+ int end2 = 0;
+ for (;;) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (isalpha(c1) && isupper(c1))
+ c1 = tolower(c1);
+ if (isalpha(c2) && isupper(c2))
+ c2 = tolower(c2);
+ end1 = !(isalnum(c1) || c1 == '_' || c1 == '-');
+ end2 = !(isalnum(c2) || c2 == '_' || c2 == '-');
+ if (end1 || end2 || c1 != c2)
+ break;
+ }
+ return end1 && end2; /* reached end of both words? */
+}
+
+
+/* Return length of token (see above for definition of token) */
+static int token_length(const char *s)
+{
+ const char *begin = s;
+ for (;; s++) {
+ int c = *s;
+ int end = !(isalnum(c) || c == '_' || c == '-');
+ if (end)
+ break;
+ }
+ return s - begin;
+}
+
+
+/* return length of interword separation.
+ * This accepts only spaces/tabs and thus will not traverse a line
+ * or buffer ending.
+ */
+static int word_separation_length(const char *s)
+{
+ const char *begin = s;
+ for (;; s++) {
+ int c = *s;
+ if (c == ' ' || c == '\t')
+ continue;
+ break;
+ }
+ return s - begin;
+}
+
+
+/* No. of chars through (including) end of line */
+static int line_length(const char *l)
+{
+ const char *lp = l;
+ while (*lp && *lp != '\n')
+ lp++;
+ if (*lp == '\n')
+ lp++;
+ return lp - l;
+}
+
+
+/* No. of chars excluding trailing whitespace */
+static int line_length_stripped(const char *l)
+{
+ const char *lp = l + line_length(l);
+ while (lp > l && !isgraph(lp[-1]))
+ lp--;
+ return lp - l;
+}
+
+
+static int str_starts(const char *str, const char *start)
+{
+ return os_strncmp(str, start, os_strlen(start)) == 0;
+}
+
+
+/***************************************************************************
+ * Advertisements.
+ * These are multicast to the world to tell them we are here.
+ * The individual packets are spread out in time to limit loss,
+ * and then after a much longer period of time the whole sequence
+ * is repeated again (for NOTIFYs only).
+ **************************************************************************/
+
+/**
+ * next_advertisement - Build next message and advance the state machine
+ * @a: Advertisement state
+ * @islast: Buffer for indicating whether this is the last message (= 1)
+ * Returns: The new message (caller is responsible for freeing this)
+ *
+ * Note: next_advertisement is shared code with msearchreply_* functions
+ */
+static struct wpabuf *
+next_advertisement(struct upnp_wps_device_sm *sm,
+ struct advertisement_state_machine *a, int *islast)
+{
+ struct wpabuf *msg;
+ char *NTString = "";
+ char uuid_string[80];
+ struct upnp_wps_device_interface *iface;
+
+ *islast = 0;
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+ uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
+ msg = wpabuf_alloc(800); /* more than big enough */
+ if (msg == NULL)
+ goto fail;
+ switch (a->type) {
+ case ADVERTISE_UP:
+ case ADVERTISE_DOWN:
+ NTString = "NT";
+ wpabuf_put_str(msg, "NOTIFY * HTTP/1.1\r\n");
+ wpabuf_printf(msg, "HOST: %s:%d\r\n",
+ UPNP_MULTICAST_ADDRESS, UPNP_MULTICAST_PORT);
+ wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
+ UPNP_CACHE_SEC);
+ wpabuf_printf(msg, "NTS: %s\r\n",
+ (a->type == ADVERTISE_UP ?
+ "ssdp:alive" : "ssdp:byebye"));
+ break;
+ case MSEARCH_REPLY:
+ NTString = "ST";
+ wpabuf_put_str(msg, "HTTP/1.1 200 OK\r\n");
+ wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
+ UPNP_CACHE_SEC);
+
+ wpabuf_put_str(msg, "DATE: ");
+ format_date(msg);
+ wpabuf_put_str(msg, "\r\n");
+
+ wpabuf_put_str(msg, "EXT:\r\n");
+ break;
+ }
+
+ if (a->type != ADVERTISE_DOWN) {
+ /* Where others may get our XML files from */
+ wpabuf_printf(msg, "LOCATION: http://%s:%d/%s\r\n",
+ sm->ip_addr_text, sm->web_port,
+ UPNP_WPS_DEVICE_XML_FILE);
+ }
+
+ /* The SERVER line has three comma-separated fields:
+ * operating system / version
+ * upnp version
+ * software package / version
+ * However, only the UPnP version is really required, the
+ * others can be place holders... for security reasons
+ * it is better to NOT provide extra information.
+ */
+ wpabuf_put_str(msg, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
+
+ switch (a->state / UPNP_ADVERTISE_REPEAT) {
+ case 0:
+ wpabuf_printf(msg, "%s: upnp:rootdevice\r\n", NTString);
+ wpabuf_printf(msg, "USN: uuid:%s::upnp:rootdevice\r\n",
+ uuid_string);
+ break;
+ case 1:
+ wpabuf_printf(msg, "%s: uuid:%s\r\n", NTString, uuid_string);
+ wpabuf_printf(msg, "USN: uuid:%s\r\n", uuid_string);
+ break;
+ case 2:
+ wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:device:"
+ "WFADevice:1\r\n", NTString);
+ wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
+ "org:device:WFADevice:1\r\n", uuid_string);
+ break;
+ case 3:
+ wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:service:"
+ "WFAWLANConfig:1\r\n", NTString);
+ wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
+ "org:service:WFAWLANConfig:1\r\n", uuid_string);
+ break;
+ }
+ wpabuf_put_str(msg, "\r\n");
+
+ if (a->state + 1 >= 4 * UPNP_ADVERTISE_REPEAT)
+ *islast = 1;
+
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
+static void advertisement_state_machine_handler(void *eloop_data,
+ void *user_ctx);
+
+
+/**
+ * advertisement_state_machine_stop - Stop SSDP advertisements
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @send_byebye: Send byebye advertisement messages immediately
+ */
+void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm,
+ int send_byebye)
+{
+ struct advertisement_state_machine *a = &sm->advertisement;
+ int islast = 0;
+ struct wpabuf *msg;
+ struct sockaddr_in dest;
+
+ eloop_cancel_timeout(advertisement_state_machine_handler, NULL, sm);
+ if (!send_byebye || sm->multicast_sd < 0)
+ return;
+
+ a->type = ADVERTISE_DOWN;
+ a->state = 0;
+
+ os_memset(&dest, 0, sizeof(dest));
+ dest.sin_family = AF_INET;
+ dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+ dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+ while (!islast) {
+ msg = next_advertisement(sm, a, &islast);
+ if (msg == NULL)
+ break;
+ if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg),
+ 0, (struct sockaddr *) &dest, sizeof(dest)) < 0) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Advertisement sendto "
+ "failed: %d (%s)", errno, strerror(errno));
+ }
+ wpabuf_free(msg);
+ a->state++;
+ }
+}
+
+
+static void advertisement_state_machine_handler(void *eloop_data,
+ void *user_ctx)
+{
+ struct upnp_wps_device_sm *sm = user_ctx;
+ struct advertisement_state_machine *a = &sm->advertisement;
+ struct wpabuf *msg;
+ int next_timeout_msec = 100;
+ int next_timeout_sec = 0;
+ struct sockaddr_in dest;
+ int islast = 0;
+
+ /*
+ * Each is sent twice (in case lost) w/ 100 msec delay between;
+ * spec says no more than 3 times.
+ * One pair for rootdevice, one pair for uuid, and a pair each for
+ * each of the two urns.
+ * The entire sequence must be repeated before cache control timeout
+ * (which is min 1800 seconds),
+ * recommend random portion of half of the advertised cache control age
+ * to ensure against loss... perhaps 1800/4 + rand*1800/4 ?
+ * Delay random interval < 100 msec prior to initial sending.
+ * TTL of 4
+ */
+
+ wpa_printf(MSG_MSGDUMP, "WPS UPnP: Advertisement state=%d", a->state);
+ msg = next_advertisement(sm, a, &islast);
+ if (msg == NULL)
+ return;
+
+ os_memset(&dest, 0, sizeof(dest));
+ dest.sin_family = AF_INET;
+ dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+ dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+ if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+ (struct sockaddr *) &dest, sizeof(dest)) == -1) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: Advertisement sendto failed:"
+ "%d (%s)", errno, strerror(errno));
+ next_timeout_msec = 0;
+ next_timeout_sec = 10; /* ... later */
+ } else if (islast) {
+ a->state = 0; /* wrap around */
+ if (a->type == ADVERTISE_DOWN) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_DOWN->UP");
+ a->type = ADVERTISE_UP;
+ /* do it all over again right away */
+ } else {
+ u16 r;
+ /*
+ * Start over again after a long timeout
+ * (see notes above)
+ */
+ next_timeout_msec = 0;
+ os_get_random((void *) &r, sizeof(r));
+ next_timeout_sec = UPNP_CACHE_SEC / 4 +
+ (((UPNP_CACHE_SEC / 4) * r) >> 16);
+ sm->advertise_count++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_UP (#%u); "
+ "next in %d sec",
+ sm->advertise_count, next_timeout_sec);
+ }
+ } else {
+ a->state++;
+ }
+
+ wpabuf_free(msg);
+
+ eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+ advertisement_state_machine_handler, NULL, sm);
+}
+
+
+/**
+ * advertisement_state_machine_start - Start SSDP advertisements
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int advertisement_state_machine_start(struct upnp_wps_device_sm *sm)
+{
+ struct advertisement_state_machine *a = &sm->advertisement;
+ int next_timeout_msec;
+
+ advertisement_state_machine_stop(sm, 0);
+
+ /*
+ * Start out advertising down, this automatically switches
+ * to advertising up which signals our restart.
+ */
+ a->type = ADVERTISE_DOWN;
+ a->state = 0;
+ /* (other fields not used here) */
+
+ /* First timeout should be random interval < 100 msec */
+ next_timeout_msec = (100 * (os_random() & 0xFF)) >> 8;
+ return eloop_register_timeout(0, next_timeout_msec,
+ advertisement_state_machine_handler,
+ NULL, sm);
+}
+
+
+/***************************************************************************
+ * M-SEARCH replies
+ * These are very similar to the multicast advertisements, with some
+ * small changes in data content; and they are sent (UDP) to a specific
+ * unicast address instead of multicast.
+ * They are sent in response to a UDP M-SEARCH packet.
+ **************************************************************************/
+
+/**
+ * msearchreply_state_machine_stop - Stop M-SEARCH reply state machine
+ * @a: Selected advertisement/reply state
+ */
+void msearchreply_state_machine_stop(struct advertisement_state_machine *a)
+{
+ wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH stop");
+ dl_list_del(&a->list);
+ os_free(a);
+}
+
+
+static void msearchreply_state_machine_handler(void *eloop_data,
+ void *user_ctx)
+{
+ struct advertisement_state_machine *a = user_ctx;
+ struct upnp_wps_device_sm *sm = eloop_data;
+ struct wpabuf *msg;
+ int next_timeout_msec = 100;
+ int next_timeout_sec = 0;
+ int islast = 0;
+
+ /*
+ * Each response is sent twice (in case lost) w/ 100 msec delay
+ * between; spec says no more than 3 times.
+ * One pair for rootdevice, one pair for uuid, and a pair each for
+ * each of the two urns.
+ */
+
+ /* TODO: should only send the requested response types */
+
+ wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply state=%d (%s:%d)",
+ a->state, inet_ntoa(a->client.sin_addr),
+ ntohs(a->client.sin_port));
+ msg = next_advertisement(sm, a, &islast);
+ if (msg == NULL)
+ return;
+
+ /*
+ * Send it on the multicast socket to avoid having to set up another
+ * socket.
+ */
+ if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+ (struct sockaddr *) &a->client, sizeof(a->client)) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply sendto "
+ "errno %d (%s) for %s:%d",
+ errno, strerror(errno),
+ inet_ntoa(a->client.sin_addr),
+ ntohs(a->client.sin_port));
+ /* Ignore error and hope for the best */
+ }
+ wpabuf_free(msg);
+ if (islast) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply done");
+ msearchreply_state_machine_stop(a);
+ return;
+ }
+ a->state++;
+
+ wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply in %d.%03d sec",
+ next_timeout_sec, next_timeout_msec);
+ eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+ msearchreply_state_machine_handler, sm, a);
+}
+
+
+/**
+ * msearchreply_state_machine_start - Reply to M-SEARCH discovery request
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @client: Client address
+ * @mx: Maximum delay in seconds
+ *
+ * Use TTL of 4 (this was done when socket set up).
+ * A response should be given in randomized portion of min(MX,120) seconds
+ *
+ * UPnP-arch-DeviceArchitecture, 1.2.3:
+ * To be found, a device must send a UDP response to the source IP address and
+ * port that sent the request to the multicast channel. Devices respond if the
+ * ST header of the M-SEARCH request is "ssdp:all", "upnp:rootdevice", "uuid:"
+ * followed by a UUID that exactly matches one advertised by the device.
+ */
+static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm,
+ struct sockaddr_in *client,
+ int mx)
+{
+ struct advertisement_state_machine *a;
+ int next_timeout_sec;
+ int next_timeout_msec;
+ int replies;
+
+ replies = dl_list_len(&sm->msearch_replies);
+ wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply start (%d "
+ "outstanding)", replies);
+ if (replies >= MAX_MSEARCH) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Too many outstanding "
+ "M-SEARCH replies");
+ return;
+ }
+
+ a = os_zalloc(sizeof(*a));
+ if (a == NULL)
+ return;
+ a->type = MSEARCH_REPLY;
+ a->state = 0;
+ os_memcpy(&a->client, client, sizeof(*client));
+ /* Wait time depending on MX value */
+ next_timeout_msec = (1000 * mx * (os_random() & 0xFF)) >> 8;
+ next_timeout_sec = next_timeout_msec / 1000;
+ next_timeout_msec = next_timeout_msec % 1000;
+ if (eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+ msearchreply_state_machine_handler, sm,
+ a)) {
+ /* No way to recover (from malloc failure) */
+ goto fail;
+ }
+ /* Remember for future cleanup */
+ dl_list_add(&sm->msearch_replies, &a->list);
+ return;
+
+fail:
+ wpa_printf(MSG_INFO, "WPS UPnP: M-SEARCH reply failure!");
+ eloop_cancel_timeout(msearchreply_state_machine_handler, sm, a);
+ os_free(a);
+}
+
+
+/**
+ * ssdp_parse_msearch - Process a received M-SEARCH
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @client: Client address
+ * @data: NULL terminated M-SEARCH message
+ *
+ * Given that we have received a header w/ M-SEARCH, act upon it
+ *
+ * Format of M-SEARCH (case insensitive!):
+ *
+ * First line must be:
+ * M-SEARCH * HTTP/1.1
+ * Other lines in arbitrary order:
+ * HOST:239.255.255.250:1900
+ * ST:<varies -- must match>
+ * MAN:"ssdp:discover"
+ * MX:<varies>
+ *
+ * It should be noted that when Microsoft Vista is still learning its IP
+ * address, it sends out host lines like: HOST:[FF02::C]:1900
+ */
+static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
+ struct sockaddr_in *client, const char *data)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ const char *start = data;
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+ const char *end;
+ int got_host = 0;
+ int got_st = 0, st_match = 0;
+ int got_man = 0;
+ int got_mx = 0;
+ int mx = 0;
+
+ /*
+ * Skip first line M-SEARCH * HTTP/1.1
+ * (perhaps we should check remainder of the line for syntax)
+ */
+ data += line_length(data);
+
+ /* Parse remaining lines */
+ for (; *data != '\0'; data += line_length(data)) {
+ end = data + line_length_stripped(data);
+ if (token_eq(data, "host")) {
+ /* The host line indicates who the packet
+ * is addressed to... but do we really care?
+ * Note that Microsoft sometimes does funny
+ * stuff with the HOST: line.
+ */
+#if 0 /* could be */
+ data += token_length(data);
+ data += word_separation_length(data);
+ if (*data != ':')
+ goto bad;
+ data++;
+ data += word_separation_length(data);
+ /* UPNP_MULTICAST_ADDRESS */
+ if (!str_starts(data, "239.255.255.250"))
+ goto bad;
+ data += os_strlen("239.255.255.250");
+ if (*data == ':') {
+ if (!str_starts(data, ":1900"))
+ goto bad;
+ }
+#endif /* could be */
+ got_host = 1;
+ continue;
+ } else if (token_eq(data, "st")) {
+ /* There are a number of forms; we look
+ * for one that matches our case.
+ */
+ got_st = 1;
+ data += token_length(data);
+ data += word_separation_length(data);
+ if (*data != ':')
+ continue;
+ data++;
+ data += word_separation_length(data);
+ if (str_starts(data, "ssdp:all")) {
+ st_match = 1;
+ continue;
+ }
+ if (str_starts(data, "upnp:rootdevice")) {
+ st_match = 1;
+ continue;
+ }
+ if (str_starts(data, "uuid:")) {
+ char uuid_string[80];
+ struct upnp_wps_device_interface *iface;
+ iface = dl_list_first(
+ &sm->interfaces,
+ struct upnp_wps_device_interface,
+ list);
+ data += os_strlen("uuid:");
+ uuid_bin2str(iface->wps->uuid, uuid_string,
+ sizeof(uuid_string));
+ if (str_starts(data, uuid_string))
+ st_match = 1;
+ continue;
+ }
+#if 0
+ /* FIX: should we really reply to IGD string? */
+ if (str_starts(data, "urn:schemas-upnp-org:device:"
+ "InternetGatewayDevice:1")) {
+ st_match = 1;
+ continue;
+ }
+#endif
+ if (str_starts(data, "urn:schemas-wifialliance-org:"
+ "service:WFAWLANConfig:1")) {
+ st_match = 1;
+ continue;
+ }
+ if (str_starts(data, "urn:schemas-wifialliance-org:"
+ "device:WFADevice:1")) {
+ st_match = 1;
+ continue;
+ }
+ continue;
+ } else if (token_eq(data, "man")) {
+ data += token_length(data);
+ data += word_separation_length(data);
+ if (*data != ':')
+ continue;
+ data++;
+ data += word_separation_length(data);
+ if (!str_starts(data, "\"ssdp:discover\"")) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Unexpected "
+ "M-SEARCH man-field");
+ goto bad;
+ }
+ got_man = 1;
+ continue;
+ } else if (token_eq(data, "mx")) {
+ data += token_length(data);
+ data += word_separation_length(data);
+ if (*data != ':')
+ continue;
+ data++;
+ data += word_separation_length(data);
+ mx = atol(data);
+ got_mx = 1;
+ continue;
+ }
+ /* ignore anything else */
+ }
+ if (!got_host || !got_st || !got_man || !got_mx || mx < 0) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid M-SEARCH: %d %d %d "
+ "%d mx=%d", got_host, got_st, got_man, got_mx, mx);
+ goto bad;
+ }
+ if (!st_match) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored M-SEARCH (no ST "
+ "match)");
+ return;
+ }
+ if (mx > 120)
+ mx = 120; /* UPnP-arch-DeviceArchitecture, 1.2.3 */
+ msearchreply_state_machine_start(sm, client, mx);
+ return;
+
+bad:
+ wpa_printf(MSG_INFO, "WPS UPnP: Failed to parse M-SEARCH");
+ wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH data:\n%s", start);
+}
+
+
+/* Listening for (UDP) discovery (M-SEARCH) packets */
+
+/**
+ * ssdp_listener_stop - Stop SSDP listered
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ *
+ * This function stops the SSDP listener that was started by calling
+ * ssdp_listener_start().
+ */
+void ssdp_listener_stop(struct upnp_wps_device_sm *sm)
+{
+ if (sm->ssdp_sd_registered) {
+ eloop_unregister_sock(sm->ssdp_sd, EVENT_TYPE_READ);
+ sm->ssdp_sd_registered = 0;
+ }
+
+ if (sm->ssdp_sd != -1) {
+ close(sm->ssdp_sd);
+ sm->ssdp_sd = -1;
+ }
+
+ eloop_cancel_timeout(msearchreply_state_machine_handler, sm,
+ ELOOP_ALL_CTX);
+}
+
+
+static void ssdp_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct upnp_wps_device_sm *sm = sock_ctx;
+ struct sockaddr_in addr; /* client address */
+ socklen_t addr_len;
+ int nread;
+ char buf[MULTICAST_MAX_READ], *pos;
+
+ addr_len = sizeof(addr);
+ nread = recvfrom(sm->ssdp_sd, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *) &addr, &addr_len);
+ if (nread <= 0)
+ return;
+ buf[nread] = '\0'; /* need null termination for algorithm */
+
+ if (str_starts(buf, "NOTIFY ")) {
+ /*
+ * Silently ignore NOTIFYs to avoid filling debug log with
+ * unwanted messages.
+ */
+ return;
+ }
+
+ pos = os_strchr(buf, '\n');
+ if (pos)
+ *pos = '\0';
+ wpa_printf(MSG_MSGDUMP, "WPS UPnP: Received SSDP packet from %s:%d: "
+ "%s", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), buf);
+ if (pos)
+ *pos = '\n';
+
+ /* Parse first line */
+ if (os_strncasecmp(buf, "M-SEARCH", os_strlen("M-SEARCH")) == 0 &&
+ !isgraph(buf[strlen("M-SEARCH")])) {
+ ssdp_parse_msearch(sm, &addr, buf);
+ return;
+ }
+
+ /* Ignore anything else */
+}
+
+
+int ssdp_listener_open(void)
+{
+ struct sockaddr_in addr;
+ struct ip_mreq mcast_addr;
+ int on = 1;
+ /* per UPnP spec, keep IP packet time to live (TTL) small */
+ unsigned char ttl = 4;
+ int sd;
+
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0)
+ goto fail;
+ if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+ goto fail;
+ if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
+ goto fail;
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(UPNP_MULTICAST_PORT);
+ if (bind(sd, (struct sockaddr *) &addr, sizeof(addr)))
+ goto fail;
+ os_memset(&mcast_addr, 0, sizeof(mcast_addr));
+ mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY);
+ mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+ if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *) &mcast_addr, sizeof(mcast_addr)))
+ goto fail;
+ if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+ &ttl, sizeof(ttl)))
+ goto fail;
+
+ return sd;
+
+fail:
+ if (sd >= 0)
+ close(sd);
+ return -1;
+}
+
+
+/**
+ * ssdp_listener_start - Set up for receiving discovery (UDP) packets
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * The SSDP listener is stopped by calling ssdp_listener_stop().
+ */
+int ssdp_listener_start(struct upnp_wps_device_sm *sm)
+{
+ sm->ssdp_sd = ssdp_listener_open();
+
+ if (eloop_register_sock(sm->ssdp_sd, EVENT_TYPE_READ,
+ ssdp_listener_handler, NULL, sm))
+ goto fail;
+ sm->ssdp_sd_registered = 1;
+ return 0;
+
+fail:
+ /* Error */
+ wpa_printf(MSG_ERROR, "WPS UPnP: ssdp_listener_start failed");
+ ssdp_listener_stop(sm);
+ return -1;
+}
+
+
+/**
+ * add_ssdp_network - Add routing entry for SSDP
+ * @net_if: Selected network interface name
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function assures that the multicast address will be properly
+ * handled by Linux networking code (by a modification to routing tables).
+ * This must be done per network interface. It really only needs to be done
+ * once after booting up, but it does not hurt to call this more frequently
+ * "to be safe".
+ */
+int add_ssdp_network(const char *net_if)
+{
+#ifdef __linux__
+ int ret = -1;
+ int sock = -1;
+ struct rtentry rt;
+ struct sockaddr_in *sin;
+
+ if (!net_if)
+ goto fail;
+
+ os_memset(&rt, 0, sizeof(rt));
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ goto fail;
+
+ rt.rt_dev = (char *) net_if;
+ sin = aliasing_hide_typecast(&rt.rt_dst, struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_port = 0;
+ sin->sin_addr.s_addr = inet_addr(SSDP_TARGET);
+ sin = aliasing_hide_typecast(&rt.rt_genmask, struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_port = 0;
+ sin->sin_addr.s_addr = inet_addr(SSDP_NETMASK);
+ rt.rt_flags = RTF_UP;
+ if (ioctl(sock, SIOCADDRT, &rt) < 0) {
+ if (errno == EPERM) {
+ wpa_printf(MSG_DEBUG, "add_ssdp_network: No "
+ "permissions to add routing table entry");
+ /* Continue to allow testing as non-root */
+ } else if (errno != EEXIST) {
+ wpa_printf(MSG_INFO, "add_ssdp_network() ioctl errno "
+ "%d (%s)", errno, strerror(errno));
+ goto fail;
+ }
+ }
+
+ ret = 0;
+
+fail:
+ if (sock >= 0)
+ close(sock);
+
+ return ret;
+#else /* __linux__ */
+ return 0;
+#endif /* __linux__ */
+}
+
+
+int ssdp_open_multicast_sock(u32 ip_addr)
+{
+ int sd;
+ /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet
+ * time to live (TTL) small */
+ unsigned char ttl = 4;
+
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0)
+ return -1;
+
+#if 0 /* maybe ok if we sometimes block on writes */
+ if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+ return -1;
+#endif
+
+ if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
+ &ip_addr, sizeof(ip_addr))) {
+ wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: "
+ "%d (%s)", ip_addr, errno, strerror(errno));
+ return -1;
+ }
+ if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+ &ttl, sizeof(ttl))) {
+ wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): "
+ "%d (%s)", errno, strerror(errno));
+ return -1;
+ }
+
+#if 0 /* not needed, because we don't receive using multicast_sd */
+ {
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+ mreq.imr_interface.s_addr = ip_addr;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Multicast addr 0x%x if addr "
+ "0x%x",
+ mreq.imr_multiaddr.s_addr,
+ mreq.imr_interface.s_addr);
+ if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+ sizeof(mreq))) {
+ wpa_printf(MSG_ERROR,
+ "WPS UPnP: setsockopt "
+ "IP_ADD_MEMBERSHIP errno %d (%s)",
+ errno, strerror(errno));
+ return -1;
+ }
+ }
+#endif /* not needed */
+
+ /*
+ * TODO: What about IP_MULTICAST_LOOP? It seems to be on by default?
+ * which aids debugging I suppose but isn't really necessary?
+ */
+
+ return sd;
+}
+
+
+/**
+ * ssdp_open_multicast - Open socket for sending multicast SSDP messages
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
+{
+ sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr);
+ if (sm->multicast_sd < 0)
+ return -1;
+ return 0;
+}
diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c
new file mode 100644
index 0000000..917f60b
--- /dev/null
+++ b/src/wps/wps_upnp_web.c
@@ -0,0 +1,1324 @@
+/*
+ * UPnP WPS Device - Web connections
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "uuid.h"
+#include "httpread.h"
+#include "http_server.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+#include "upnp_xml.h"
+
+/***************************************************************************
+ * Web connections (we serve pages of info about ourselves, handle
+ * requests, etc. etc.).
+ **************************************************************************/
+
+#define WEB_CONNECTION_TIMEOUT_SEC 30 /* Drop web connection after t.o. */
+#define WEB_CONNECTION_MAX_READ 8000 /* Max we'll read for TCP request */
+#define MAX_WEB_CONNECTIONS 10 /* max simultaneous web connects */
+
+
+static const char *urn_wfawlanconfig =
+ "urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
+static const char *http_server_hdr =
+ "Server: unspecified, UPnP/1.0, unspecified\r\n";
+static const char *http_connection_close =
+ "Connection: close\r\n";
+
+/*
+ * "Files" that we serve via HTTP. The format of these files is given by
+ * WFA WPS specifications. Extra white space has been removed to save space.
+ */
+
+static const char wps_scpd_xml[] =
+"<?xml version=\"1.0\"?>\n"
+"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
+"<specVersion><major>1</major><minor>0</minor></specVersion>\n"
+"<actionList>\n"
+"<action>\n"
+"<name>GetDeviceInfo</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewDeviceInfo</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>PutMessage</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewInMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>InMessage</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewOutMessage</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>OutMessage</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>PutWLANResponse</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewWLANEventType</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewWLANEventMAC</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>SetSelectedRegistrar</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"</actionList>\n"
+"<serviceStateTable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>Message</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>InMessage</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>OutMessage</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>DeviceInfo</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>APStatus</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>STAStatus</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>WLANEvent</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANEventType</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANEventMAC</name>\n"
+"<dataType>string</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANResponse</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"</serviceStateTable>\n"
+"</scpd>\n"
+;
+
+
+static const char *wps_device_xml_prefix =
+ "<?xml version=\"1.0\"?>\n"
+ "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
+ "<specVersion>\n"
+ "<major>1</major>\n"
+ "<minor>0</minor>\n"
+ "</specVersion>\n"
+ "<device>\n"
+ "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
+ "</deviceType>\n";
+
+static const char *wps_device_xml_postfix =
+ "<serviceList>\n"
+ "<service>\n"
+ "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
+ "</serviceType>\n"
+ "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
+ "\n"
+ "<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
+ "<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
+ "<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
+ "</service>\n"
+ "</serviceList>\n"
+ "</device>\n"
+ "</root>\n";
+
+
+/* format_wps_device_xml -- produce content of "file" wps_device.xml
+ * (UPNP_WPS_DEVICE_XML_FILE)
+ */
+static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
+ struct wpabuf *buf)
+{
+ const char *s;
+ char uuid_string[80];
+ struct upnp_wps_device_interface *iface;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+
+ wpabuf_put_str(buf, wps_device_xml_prefix);
+
+ /*
+ * Add required fields with default values if not configured. Add
+ * optional and recommended fields only if configured.
+ */
+ s = iface->wps->friendly_name;
+ s = ((s && *s) ? s : "WPS Access Point");
+ xml_add_tagged_data(buf, "friendlyName", s);
+
+ s = iface->wps->dev.manufacturer;
+ s = ((s && *s) ? s : "");
+ xml_add_tagged_data(buf, "manufacturer", s);
+
+ if (iface->wps->manufacturer_url)
+ xml_add_tagged_data(buf, "manufacturerURL",
+ iface->wps->manufacturer_url);
+
+ if (iface->wps->model_description)
+ xml_add_tagged_data(buf, "modelDescription",
+ iface->wps->model_description);
+
+ s = iface->wps->dev.model_name;
+ s = ((s && *s) ? s : "");
+ xml_add_tagged_data(buf, "modelName", s);
+
+ if (iface->wps->dev.model_number)
+ xml_add_tagged_data(buf, "modelNumber",
+ iface->wps->dev.model_number);
+
+ if (iface->wps->model_url)
+ xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
+
+ if (iface->wps->dev.serial_number)
+ xml_add_tagged_data(buf, "serialNumber",
+ iface->wps->dev.serial_number);
+
+ uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
+ s = uuid_string;
+ /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
+ * easily...
+ */
+ wpabuf_put_str(buf, "<UDN>uuid:");
+ xml_data_encode(buf, s, os_strlen(s));
+ wpabuf_put_str(buf, "</UDN>\n");
+
+ if (iface->wps->upc)
+ xml_add_tagged_data(buf, "UPC", iface->wps->upc);
+
+ wpabuf_put_str(buf, wps_device_xml_postfix);
+}
+
+
+static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
+{
+ wpabuf_put_str(buf, "HTTP/1.1 ");
+ switch (code) {
+ case HTTP_OK:
+ wpabuf_put_str(buf, "200 OK\r\n");
+ break;
+ case HTTP_BAD_REQUEST:
+ wpabuf_put_str(buf, "400 Bad request\r\n");
+ break;
+ case HTTP_PRECONDITION_FAILED:
+ wpabuf_put_str(buf, "412 Precondition failed\r\n");
+ break;
+ case HTTP_UNIMPLEMENTED:
+ wpabuf_put_str(buf, "501 Unimplemented\r\n");
+ break;
+ case HTTP_INTERNAL_SERVER_ERROR:
+ default:
+ wpabuf_put_str(buf, "500 Internal server error\r\n");
+ break;
+ }
+}
+
+
+static void http_put_date(struct wpabuf *buf)
+{
+ wpabuf_put_str(buf, "Date: ");
+ format_date(buf);
+ wpabuf_put_str(buf, "\r\n");
+}
+
+
+static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
+{
+ http_put_reply_code(buf, code);
+ wpabuf_put_str(buf, http_server_hdr);
+ wpabuf_put_str(buf, http_connection_close);
+ wpabuf_put_str(buf, "Content-Length: 0\r\n"
+ "\r\n");
+}
+
+
+/* Given that we have received a header w/ GET, act upon it
+ *
+ * Format of GET (case-insensitive):
+ *
+ * First line must be:
+ * GET /<file> HTTP/1.1
+ * Since we don't do anything fancy we just ignore other lines.
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Connection: close
+ * Content-Type: text/xml
+ * Date: <rfc1123-date>
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
+ struct http_request *hreq, char *filename)
+{
+ struct wpabuf *buf; /* output buffer, allocated */
+ char *put_length_here;
+ char *body_start;
+ enum {
+ GET_DEVICE_XML_FILE,
+ GET_SCPD_XML_FILE
+ } req;
+ size_t extra_len = 0;
+ int body_length;
+ char len_buf[10];
+ struct upnp_wps_device_interface *iface;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+
+ /*
+ * It is not required that filenames be case insensitive but it is
+ * allowed and cannot hurt here.
+ */
+ if (filename == NULL)
+ filename = "(null)"; /* just in case */
+ if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
+ req = GET_DEVICE_XML_FILE;
+ extra_len = 3000;
+ if (iface->wps->friendly_name)
+ extra_len += os_strlen(iface->wps->friendly_name);
+ if (iface->wps->manufacturer_url)
+ extra_len += os_strlen(iface->wps->manufacturer_url);
+ if (iface->wps->model_description)
+ extra_len += os_strlen(iface->wps->model_description);
+ if (iface->wps->model_url)
+ extra_len += os_strlen(iface->wps->model_url);
+ if (iface->wps->upc)
+ extra_len += os_strlen(iface->wps->upc);
+ } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
+ req = GET_SCPD_XML_FILE;
+ extra_len = os_strlen(wps_scpd_xml);
+ } else {
+ /* File not found */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
+ filename);
+ buf = wpabuf_alloc(200);
+ if (buf == NULL) {
+ http_request_deinit(hreq);
+ return;
+ }
+ wpabuf_put_str(buf,
+ "HTTP/1.1 404 Not Found\r\n"
+ "Connection: close\r\n");
+
+ http_put_date(buf);
+
+ /* terminating empty line */
+ wpabuf_put_str(buf, "\r\n");
+
+ goto send_buf;
+ }
+
+ buf = wpabuf_alloc(1000 + extra_len);
+ if (buf == NULL) {
+ http_request_deinit(hreq);
+ return;
+ }
+
+ wpabuf_put_str(buf,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/xml; charset=\"utf-8\"\r\n");
+ wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
+ wpabuf_put_str(buf, "Connection: close\r\n");
+ wpabuf_put_str(buf, "Content-Length: ");
+ /*
+ * We will paste the length in later, leaving some extra whitespace.
+ * HTTP code is supposed to be tolerant of extra whitespace.
+ */
+ put_length_here = wpabuf_put(buf, 0);
+ wpabuf_put_str(buf, " \r\n");
+
+ http_put_date(buf);
+
+ /* terminating empty line */
+ wpabuf_put_str(buf, "\r\n");
+
+ body_start = wpabuf_put(buf, 0);
+
+ switch (req) {
+ case GET_DEVICE_XML_FILE:
+ format_wps_device_xml(sm, buf);
+ break;
+ case GET_SCPD_XML_FILE:
+ wpabuf_put_str(buf, wps_scpd_xml);
+ break;
+ }
+
+ /* Now patch in the content length at the end */
+ body_length = (char *) wpabuf_put(buf, 0) - body_start;
+ os_snprintf(len_buf, 10, "%d", body_length);
+ os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
+
+send_buf:
+ http_request_send_and_deinit(hreq, buf);
+}
+
+
+static enum http_reply_code
+web_process_get_device_info(struct upnp_wps_device_sm *sm,
+ struct wpabuf **reply, const char **replyname)
+{
+ static const char *name = "NewDeviceInfo";
+ struct wps_config cfg;
+ struct upnp_wps_device_interface *iface;
+ struct upnp_wps_peer *peer;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+ peer = &iface->peer;
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
+
+ if (iface->ctx->ap_pin == NULL)
+ return HTTP_INTERNAL_SERVER_ERROR;
+
+ /*
+ * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
+ * registration over UPnP with the AP acting as an Enrollee. It should
+ * be noted that this is frequently used just to get the device data,
+ * i.e., there may not be any intent to actually complete the
+ * registration.
+ */
+
+ if (peer->wps)
+ wps_deinit(peer->wps);
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ cfg.wps = iface->wps;
+ cfg.pin = (u8 *) iface->ctx->ap_pin;
+ cfg.pin_len = os_strlen(iface->ctx->ap_pin);
+ peer->wps = wps_init(&cfg);
+ if (peer->wps) {
+ enum wsc_op_code op_code;
+ *reply = wps_get_msg(peer->wps, &op_code);
+ if (*reply == NULL) {
+ wps_deinit(peer->wps);
+ peer->wps = NULL;
+ }
+ } else
+ *reply = NULL;
+ if (*reply == NULL) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ *replyname = name;
+ return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
+ struct wpabuf **reply, const char **replyname)
+{
+ struct wpabuf *msg;
+ static const char *name = "NewOutMessage";
+ enum http_reply_code ret;
+ enum wps_process_res res;
+ enum wsc_op_code op_code;
+ struct upnp_wps_device_interface *iface;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+
+ /*
+ * PutMessage is used by external UPnP-based Registrar to perform WPS
+ * operation with the access point itself; as compared with
+ * PutWLANResponse which is for proxying.
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
+ msg = xml_get_base64_item(data, "NewInMessage", &ret);
+ if (msg == NULL)
+ return ret;
+ res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
+ if (res == WPS_FAILURE)
+ *reply = NULL;
+ else
+ *reply = wps_get_msg(iface->peer.wps, &op_code);
+ wpabuf_free(msg);
+ if (*reply == NULL)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ *replyname = name;
+ return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
+ struct wpabuf **reply, const char **replyname)
+{
+ struct wpabuf *msg;
+ enum http_reply_code ret;
+ u8 macaddr[ETH_ALEN];
+ int ev_type;
+ int type;
+ char *val;
+ struct upnp_wps_device_interface *iface;
+ int ok = 0;
+
+ /*
+ * External UPnP-based Registrar is passing us a message to be proxied
+ * over to a Wi-Fi -based client of ours.
+ */
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
+ msg = xml_get_base64_item(data, "NewMessage", &ret);
+ if (msg == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
+ "from PutWLANResponse");
+ return ret;
+ }
+ val = xml_get_first_item(data, "NewWLANEventType");
+ if (val == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in "
+ "PutWLANResponse");
+ wpabuf_free(msg);
+ return UPNP_ARG_VALUE_INVALID;
+ }
+ ev_type = atol(val);
+ os_free(val);
+ val = xml_get_first_item(data, "NewWLANEventMAC");
+ if (val == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in "
+ "PutWLANResponse");
+ wpabuf_free(msg);
+ return UPNP_ARG_VALUE_INVALID;
+ }
+ if (hwaddr_aton(val, macaddr)) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
+ "PutWLANResponse: '%s'", val);
+#ifdef CONFIG_WPS_STRICT
+ {
+ struct wps_parse_attr attr;
+ if (wps_parse_msg(msg, &attr) < 0 || attr.version2) {
+ wpabuf_free(msg);
+ os_free(val);
+ return UPNP_ARG_VALUE_INVALID;
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
+ if (hwaddr_aton2(val, macaddr) > 0) {
+ /*
+ * At least some versions of Intel PROset seem to be
+ * using dot-deliminated MAC address format here.
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
+ "incorrect MAC address format in "
+ "NewWLANEventMAC: %s -> " MACSTR,
+ val, MAC2STR(macaddr));
+ } else {
+ wpabuf_free(msg);
+ os_free(val);
+ return UPNP_ARG_VALUE_INVALID;
+ }
+ }
+ os_free(val);
+ if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
+ struct wps_parse_attr attr;
+ if (wps_parse_msg(msg, &attr) < 0 ||
+ attr.msg_type == NULL)
+ type = -1;
+ else
+ type = *attr.msg_type;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
+ } else
+ type = -1;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (iface->ctx->rx_req_put_wlan_response &&
+ iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
+ macaddr, msg, type)
+ == 0)
+ ok = 1;
+ }
+
+ if (!ok) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
+ "rx_req_put_wlan_response");
+ wpabuf_free(msg);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ wpabuf_free(msg);
+ *replyname = NULL;
+ *reply = NULL;
+ return HTTP_OK;
+}
+
+
+static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
+{
+ struct subscr_addr *a;
+
+ dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
+ if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
+ return 1;
+ }
+ return 0;
+}
+
+
+static struct subscription * find_er(struct upnp_wps_device_sm *sm,
+ struct sockaddr_in *cli)
+{
+ struct subscription *s;
+ dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
+ if (find_er_addr(s, cli))
+ return s;
+ return NULL;
+}
+
+
+static enum http_reply_code
+web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
+ struct sockaddr_in *cli, char *data,
+ struct wpabuf **reply,
+ const char **replyname)
+{
+ struct wpabuf *msg;
+ enum http_reply_code ret;
+ struct subscription *s;
+ struct upnp_wps_device_interface *iface;
+ int err = 0;
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
+ s = find_er(sm, cli);
+ if (s == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
+ "from unknown ER");
+ return UPNP_ACTION_FAILED;
+ }
+ msg = xml_get_base64_item(data, "NewMessage", &ret);
+ if (msg == NULL)
+ return ret;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
+ msg))
+ err = 1;
+ }
+ wpabuf_free(msg);
+ if (err)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ *replyname = NULL;
+ *reply = NULL;
+ return HTTP_OK;
+}
+
+
+static const char *soap_prefix =
+ "<?xml version=\"1.0\"?>\n"
+ "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
+ "<s:Body>\n";
+static const char *soap_postfix =
+ "</s:Body>\n</s:Envelope>\n";
+
+static const char *soap_error_prefix =
+ "<s:Fault>\n"
+ "<faultcode>s:Client</faultcode>\n"
+ "<faultstring>UPnPError</faultstring>\n"
+ "<detail>\n"
+ "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
+static const char *soap_error_postfix =
+ "<errorDescription>Error</errorDescription>\n"
+ "</UPnPError>\n"
+ "</detail>\n"
+ "</s:Fault>\n";
+
+static void web_connection_send_reply(struct http_request *req,
+ enum http_reply_code ret,
+ const char *action, int action_len,
+ const struct wpabuf *reply,
+ const char *replyname)
+{
+ struct wpabuf *buf;
+ char *replydata;
+ char *put_length_here = NULL;
+ char *body_start = NULL;
+
+ if (reply) {
+ size_t len;
+ replydata = (char *) base64_encode(wpabuf_head(reply),
+ wpabuf_len(reply), &len);
+ } else
+ replydata = NULL;
+
+ /* Parameters of the response:
+ * action(action_len) -- action we are responding to
+ * replyname -- a name we need for the reply
+ * replydata -- NULL or null-terminated string
+ */
+ buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
+ (action_len > 0 ? action_len * 2 : 0));
+ if (buf == NULL) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
+ "POST");
+ os_free(replydata);
+ http_request_deinit(req);
+ return;
+ }
+
+ /*
+ * Assuming we will be successful, put in the output header first.
+ * Note: we do not keep connections alive (and httpread does
+ * not support it)... therefore we must have Connection: close.
+ */
+ if (ret == HTTP_OK) {
+ wpabuf_put_str(buf,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/xml; "
+ "charset=\"utf-8\"\r\n");
+ } else {
+ wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
+ }
+ wpabuf_put_str(buf, http_connection_close);
+
+ wpabuf_put_str(buf, "Content-Length: ");
+ /*
+ * We will paste the length in later, leaving some extra whitespace.
+ * HTTP code is supposed to be tolerant of extra whitespace.
+ */
+ put_length_here = wpabuf_put(buf, 0);
+ wpabuf_put_str(buf, " \r\n");
+
+ http_put_date(buf);
+
+ /* terminating empty line */
+ wpabuf_put_str(buf, "\r\n");
+
+ body_start = wpabuf_put(buf, 0);
+
+ if (ret == HTTP_OK) {
+ wpabuf_put_str(buf, soap_prefix);
+ wpabuf_put_str(buf, "<u:");
+ wpabuf_put_data(buf, action, action_len);
+ wpabuf_put_str(buf, "Response xmlns:u=\"");
+ wpabuf_put_str(buf, urn_wfawlanconfig);
+ wpabuf_put_str(buf, "\">\n");
+ if (replydata && replyname) {
+ /* TODO: might possibly need to escape part of reply
+ * data? ...
+ * probably not, unlikely to have ampersand(&) or left
+ * angle bracket (<) in it...
+ */
+ wpabuf_printf(buf, "<%s>", replyname);
+ wpabuf_put_str(buf, replydata);
+ wpabuf_printf(buf, "</%s>\n", replyname);
+ }
+ wpabuf_put_str(buf, "</u:");
+ wpabuf_put_data(buf, action, action_len);
+ wpabuf_put_str(buf, "Response>\n");
+ wpabuf_put_str(buf, soap_postfix);
+ } else {
+ /* Error case */
+ wpabuf_put_str(buf, soap_prefix);
+ wpabuf_put_str(buf, soap_error_prefix);
+ wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
+ wpabuf_put_str(buf, soap_error_postfix);
+ wpabuf_put_str(buf, soap_postfix);
+ }
+ os_free(replydata);
+
+ /* Now patch in the content length at the end */
+ if (body_start && put_length_here) {
+ int body_length = (char *) wpabuf_put(buf, 0) - body_start;
+ char len_buf[10];
+ os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
+ os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
+ }
+
+ http_request_send_and_deinit(req, buf);
+}
+
+
+static const char * web_get_action(struct http_request *req,
+ size_t *action_len)
+{
+ const char *match;
+ int match_len;
+ char *b;
+ char *action;
+
+ *action_len = 0;
+ /* The SOAPAction line of the header tells us what we want to do */
+ b = http_request_get_hdr_line(req, "SOAPAction:");
+ if (b == NULL)
+ return NULL;
+ if (*b == '"')
+ b++;
+ else
+ return NULL;
+ match = urn_wfawlanconfig;
+ match_len = os_strlen(urn_wfawlanconfig) - 1;
+ if (os_strncasecmp(b, match, match_len))
+ return NULL;
+ b += match_len;
+ /* skip over version */
+ while (isgraph(*b) && *b != '#')
+ b++;
+ if (*b != '#')
+ return NULL;
+ b++;
+ /* Following the sharp(#) should be the action and a double quote */
+ action = b;
+ while (isgraph(*b) && *b != '"')
+ b++;
+ if (*b != '"')
+ return NULL;
+ *action_len = b - action;
+ return action;
+}
+
+
+/* Given that we have received a header w/ POST, act upon it
+ *
+ * Format of POST (case-insensitive):
+ *
+ * First line must be:
+ * POST /<file> HTTP/1.1
+ * Since we don't do anything fancy we just ignore other lines.
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Connection: close
+ * Content-Type: text/xml
+ * Date: <rfc1123-date>
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_post(struct upnp_wps_device_sm *sm,
+ struct sockaddr_in *cli,
+ struct http_request *req,
+ const char *filename)
+{
+ enum http_reply_code ret;
+ char *data = http_request_get_data(req); /* body of http msg */
+ const char *action = NULL;
+ size_t action_len = 0;
+ const char *replyname = NULL; /* argument name for the reply */
+ struct wpabuf *reply = NULL; /* data for the reply */
+
+ if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
+ filename);
+ ret = HTTP_NOT_FOUND;
+ goto bad;
+ }
+
+ ret = UPNP_INVALID_ACTION;
+ action = web_get_action(req, &action_len);
+ if (action == NULL)
+ goto bad;
+
+ if (!os_strncasecmp("GetDeviceInfo", action, action_len))
+ ret = web_process_get_device_info(sm, &reply, &replyname);
+ else if (!os_strncasecmp("PutMessage", action, action_len))
+ ret = web_process_put_message(sm, data, &reply, &replyname);
+ else if (!os_strncasecmp("PutWLANResponse", action, action_len))
+ ret = web_process_put_wlan_response(sm, data, &reply,
+ &replyname);
+ else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
+ ret = web_process_set_selected_registrar(sm, cli, data, &reply,
+ &replyname);
+ else
+ wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
+
+bad:
+ if (ret != HTTP_OK)
+ wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
+ web_connection_send_reply(req, ret, action, action_len, reply,
+ replyname);
+ wpabuf_free(reply);
+}
+
+
+/* Given that we have received a header w/ SUBSCRIBE, act upon it
+ *
+ * Format of SUBSCRIBE (case-insensitive):
+ *
+ * First line must be:
+ * SUBSCRIBE /wps_event HTTP/1.1
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Server: xx, UPnP/1.0, xx
+ * SID: uuid:xxxxxxxxx
+ * Timeout: Second-<n>
+ * Content-Length: 0
+ * Date: xxxx
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
+ struct http_request *req,
+ const char *filename)
+{
+ struct wpabuf *buf;
+ char *b;
+ char *hdr = http_request_get_hdr(req);
+ char *h;
+ char *match;
+ int match_len;
+ char *end;
+ int len;
+ int got_nt = 0;
+ u8 uuid[UUID_LEN];
+ int got_uuid = 0;
+ char *callback_urls = NULL;
+ struct subscription *s = NULL;
+ enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
+
+ buf = wpabuf_alloc(1000);
+ if (buf == NULL) {
+ http_request_deinit(req);
+ return;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
+ (u8 *) hdr, os_strlen(hdr));
+
+ /* Parse/validate headers */
+ h = hdr;
+ /* First line: SUBSCRIBE /wps_event HTTP/1.1
+ * has already been parsed.
+ */
+ if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
+ ret = HTTP_PRECONDITION_FAILED;
+ goto error;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
+ end = os_strchr(h, '\n');
+
+ for (; end != NULL; h = end + 1) {
+ /* Option line by option line */
+ h = end + 1;
+ end = os_strchr(h, '\n');
+ if (end == NULL)
+ break; /* no unterminated lines allowed */
+
+ /* NT assures that it is our type of subscription;
+ * not used for a renewl.
+ **/
+ match = "NT:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ match = "upnp:event";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) != 0) {
+ ret = HTTP_BAD_REQUEST;
+ goto error;
+ }
+ got_nt = 1;
+ continue;
+ }
+ /* HOST should refer to us */
+#if 0
+ match = "HOST:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ .....
+ }
+#endif
+ /* CALLBACK gives one or more URLs for NOTIFYs
+ * to be sent as a result of the subscription.
+ * Each URL is enclosed in angle brackets.
+ */
+ match = "CALLBACK:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ len = end - h;
+ os_free(callback_urls);
+ callback_urls = os_malloc(len + 1);
+ if (callback_urls == NULL) {
+ ret = HTTP_INTERNAL_SERVER_ERROR;
+ goto error;
+ }
+ os_memcpy(callback_urls, h, len);
+ callback_urls[len] = 0;
+ continue;
+ }
+ /* SID is only for renewal */
+ match = "SID:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ match = "uuid:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) != 0) {
+ ret = HTTP_BAD_REQUEST;
+ goto error;
+ }
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ if (uuid_str2bin(h, uuid)) {
+ ret = HTTP_BAD_REQUEST;
+ goto error;
+ }
+ got_uuid = 1;
+ continue;
+ }
+ /* TIMEOUT is requested timeout, but apparently we can
+ * just ignore this.
+ */
+ }
+
+ if (got_uuid) {
+ /* renewal */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
+ if (callback_urls) {
+ ret = HTTP_BAD_REQUEST;
+ goto error;
+ }
+ s = subscription_renew(sm, uuid);
+ if (s == NULL) {
+ char str[80];
+ uuid_bin2str(uuid, str, sizeof(str));
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
+ "SID %s", str);
+ ret = HTTP_PRECONDITION_FAILED;
+ goto error;
+ }
+ } else if (callback_urls) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
+ if (!got_nt) {
+ ret = HTTP_PRECONDITION_FAILED;
+ goto error;
+ }
+ s = subscription_start(sm, callback_urls);
+ if (s == NULL) {
+ ret = HTTP_INTERNAL_SERVER_ERROR;
+ goto error;
+ }
+ } else {
+ ret = HTTP_PRECONDITION_FAILED;
+ goto error;
+ }
+
+ /* success */
+ http_put_reply_code(buf, HTTP_OK);
+ wpabuf_put_str(buf, http_server_hdr);
+ wpabuf_put_str(buf, http_connection_close);
+ wpabuf_put_str(buf, "Content-Length: 0\r\n");
+ wpabuf_put_str(buf, "SID: uuid:");
+ /* subscription id */
+ b = wpabuf_put(buf, 0);
+ uuid_bin2str(s->uuid, b, 80);
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
+ wpabuf_put(buf, os_strlen(b));
+ wpabuf_put_str(buf, "\r\n");
+ wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
+ http_put_date(buf);
+ /* And empty line to terminate header: */
+ wpabuf_put_str(buf, "\r\n");
+
+ os_free(callback_urls);
+ http_request_send_and_deinit(req, buf);
+ return;
+
+error:
+ /* Per UPnP spec:
+ * Errors
+ * Incompatible headers
+ * 400 Bad Request. If SID header and one of NT or CALLBACK headers
+ * are present, the publisher must respond with HTTP error
+ * 400 Bad Request.
+ * Missing or invalid CALLBACK
+ * 412 Precondition Failed. If CALLBACK header is missing or does not
+ * contain a valid HTTP URL, the publisher must respond with HTTP
+ * error 412 Precondition Failed.
+ * Invalid NT
+ * 412 Precondition Failed. If NT header does not equal upnp:event,
+ * the publisher must respond with HTTP error 412 Precondition
+ * Failed.
+ * [For resubscription, use 412 if unknown uuid].
+ * Unable to accept subscription
+ * 5xx. If a publisher is not able to accept a subscription (such as
+ * due to insufficient resources), it must respond with a
+ * HTTP 500-series error code.
+ * 599 Too many subscriptions (not a standard HTTP error)
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
+ http_put_empty(buf, ret);
+ http_request_send_and_deinit(req, buf);
+ os_free(callback_urls);
+}
+
+
+/* Given that we have received a header w/ UNSUBSCRIBE, act upon it
+ *
+ * Format of UNSUBSCRIBE (case-insensitive):
+ *
+ * First line must be:
+ * UNSUBSCRIBE /wps_event HTTP/1.1
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Content-Length: 0
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
+ struct http_request *req,
+ const char *filename)
+{
+ struct wpabuf *buf;
+ char *hdr = http_request_get_hdr(req);
+ char *h;
+ char *match;
+ int match_len;
+ char *end;
+ u8 uuid[UUID_LEN];
+ int got_uuid = 0;
+ struct subscription *s = NULL;
+ enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
+
+ /* Parse/validate headers */
+ h = hdr;
+ /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
+ * has already been parsed.
+ */
+ if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
+ ret = HTTP_PRECONDITION_FAILED;
+ goto send_msg;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
+ end = os_strchr(h, '\n');
+
+ for (; end != NULL; h = end + 1) {
+ /* Option line by option line */
+ h = end + 1;
+ end = os_strchr(h, '\n');
+ if (end == NULL)
+ break; /* no unterminated lines allowed */
+
+ /* HOST should refer to us */
+#if 0
+ match = "HOST:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ .....
+ }
+#endif
+ /* SID is only for renewal */
+ match = "SID:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) == 0) {
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ match = "uuid:";
+ match_len = os_strlen(match);
+ if (os_strncasecmp(h, match, match_len) != 0) {
+ ret = HTTP_BAD_REQUEST;
+ goto send_msg;
+ }
+ h += match_len;
+ while (*h == ' ' || *h == '\t')
+ h++;
+ if (uuid_str2bin(h, uuid)) {
+ ret = HTTP_BAD_REQUEST;
+ goto send_msg;
+ }
+ got_uuid = 1;
+ continue;
+ }
+ }
+
+ if (got_uuid) {
+ s = subscription_find(sm, uuid);
+ if (s) {
+ struct subscr_addr *sa;
+ sa = dl_list_first(&s->addr_list, struct subscr_addr,
+ list);
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
+ s, (sa && sa->domain_and_port) ?
+ sa->domain_and_port : "-null-");
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ }
+ } else {
+ wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
+ "found)");
+ ret = HTTP_PRECONDITION_FAILED;
+ goto send_msg;
+ }
+
+ ret = HTTP_OK;
+
+send_msg:
+ buf = wpabuf_alloc(200);
+ if (buf == NULL) {
+ http_request_deinit(req);
+ return;
+ }
+ http_put_empty(buf, ret);
+ http_request_send_and_deinit(req, buf);
+}
+
+
+/* Send error in response to unknown requests */
+static void web_connection_unimplemented(struct http_request *req)
+{
+ struct wpabuf *buf;
+ buf = wpabuf_alloc(200);
+ if (buf == NULL) {
+ http_request_deinit(req);
+ return;
+ }
+ http_put_empty(buf, HTTP_UNIMPLEMENTED);
+ http_request_send_and_deinit(req, buf);
+}
+
+
+
+/* Called when we have gotten an apparently valid http request.
+ */
+static void web_connection_check_data(void *ctx, struct http_request *req)
+{
+ struct upnp_wps_device_sm *sm = ctx;
+ enum httpread_hdr_type htype = http_request_get_type(req);
+ char *filename = http_request_get_uri(req);
+ struct sockaddr_in *cli = http_request_get_cli_addr(req);
+
+ if (!filename) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
+ http_request_deinit(req);
+ return;
+ }
+ /* Trim leading slashes from filename */
+ while (*filename == '/')
+ filename++;
+
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
+ htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
+
+ switch (htype) {
+ case HTTPREAD_HDR_TYPE_GET:
+ web_connection_parse_get(sm, req, filename);
+ break;
+ case HTTPREAD_HDR_TYPE_POST:
+ web_connection_parse_post(sm, cli, req, filename);
+ break;
+ case HTTPREAD_HDR_TYPE_SUBSCRIBE:
+ web_connection_parse_subscribe(sm, req, filename);
+ break;
+ case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
+ web_connection_parse_unsubscribe(sm, req, filename);
+ break;
+
+ /* We are not required to support M-POST; just plain
+ * POST is supposed to work, so we only support that.
+ * If for some reason we need to support M-POST, it is
+ * mostly the same as POST, with small differences.
+ */
+ default:
+ /* Send 501 for anything else */
+ web_connection_unimplemented(req);
+ break;
+ }
+}
+
+
+/*
+ * Listening for web connections
+ * We have a single TCP listening port, and hand off connections as we get
+ * them.
+ */
+
+void web_listener_stop(struct upnp_wps_device_sm *sm)
+{
+ http_server_deinit(sm->web_srv);
+ sm->web_srv = NULL;
+}
+
+
+int web_listener_start(struct upnp_wps_device_sm *sm)
+{
+ struct in_addr addr;
+ addr.s_addr = sm->ip_addr;
+ sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
+ sm);
+ if (sm->web_srv == NULL) {
+ web_listener_stop(sm);
+ return -1;
+ }
+ sm->web_port = http_server_get_port(sm->web_srv);
+
+ return 0;
+}
diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c
new file mode 100644
index 0000000..c3071a0
--- /dev/null
+++ b/src/wps/wps_validate.c
@@ -0,0 +1,1981 @@
+/*
+ * Wi-Fi Protected Setup - Strict protocol validation routines
+ * Copyright (c) 2010, Atheros Communications, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "wps_i.h"
+#include "wps.h"
+
+
+#ifndef WPS_STRICT_ALL
+#define WPS_STRICT_WPS2
+#endif /* WPS_STRICT_ALL */
+
+
+static int wps_validate_version(const u8 *version, int mandatory)
+{
+ if (version == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Version attribute "
+ "missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*version != 0x10) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version attribute "
+ "value 0x%x", *version);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_version2(const u8 *version2, int mandatory)
+{
+ if (version2 == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Version2 attribute "
+ "missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*version2 < 0x20) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version2 attribute "
+ "value 0x%x", *version2);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_request_type(const u8 *request_type, int mandatory)
+{
+ if (request_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Request Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*request_type > 0x03) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request Type "
+ "attribute value 0x%x", *request_type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_response_type(const u8 *response_type, int mandatory)
+{
+ if (response_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Response Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*response_type > 0x03) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Response Type "
+ "attribute value 0x%x", *response_type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int valid_config_methods(u16 val, int wps2)
+{
+ if (wps2) {
+ if ((val & 0x6000) && !(val & WPS_CONFIG_DISPLAY)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
+ "Display flag without old Display flag "
+ "set");
+ return 0;
+ }
+ if (!(val & 0x6000) && (val & WPS_CONFIG_DISPLAY)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Display flag "
+ "without Physical/Virtual Display flag");
+ return 0;
+ }
+ if ((val & 0x0600) && !(val & WPS_CONFIG_PUSHBUTTON)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
+ "PushButton flag without old PushButton "
+ "flag set");
+ return 0;
+ }
+ if (!(val & 0x0600) && (val & WPS_CONFIG_PUSHBUTTON)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: PushButton flag "
+ "without Physical/Virtual PushButton flag");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+static int wps_validate_config_methods(const u8 *config_methods, int wps2,
+ int mandatory)
+{
+ u16 val;
+
+ if (config_methods == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Configuration "
+ "Methods attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+
+ val = WPA_GET_BE16(config_methods);
+ if (!valid_config_methods(val, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
+ "Methods attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_ap_config_methods(const u8 *config_methods, int wps2,
+ int mandatory)
+{
+ u16 val;
+
+ if (wps_validate_config_methods(config_methods, wps2, mandatory) < 0)
+ return -1;
+ if (config_methods == NULL)
+ return 0;
+ val = WPA_GET_BE16(config_methods);
+ if (val & WPS_CONFIG_PUSHBUTTON) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
+ "Methods attribute value 0x%04x in AP info "
+ "(PushButton not allowed for registering new ER)",
+ val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_uuid_e(const u8 *uuid_e, int mandatory)
+{
+ if (uuid_e == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: UUID-E "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_uuid_r(const u8 *uuid_r, int mandatory)
+{
+ if (uuid_r == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: UUID-R "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_primary_dev_type(const u8 *primary_dev_type,
+ int mandatory)
+{
+ if (primary_dev_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Primary Device Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_rf_bands(const u8 *rf_bands, int mandatory)
+{
+ if (rf_bands == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: RF Bands "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ &&
+ *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands "
+ "attribute value 0x%x", *rf_bands);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_assoc_state(const u8 *assoc_state, int mandatory)
+{
+ u16 val;
+ if (assoc_state == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Association State "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(assoc_state);
+ if (val > 4) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Association State "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_config_error(const u8 *config_error, int mandatory)
+{
+ u16 val;
+
+ if (config_error == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Configuration Error "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(config_error);
+ if (val > 18) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_dev_password_id(const u8 *dev_password_id,
+ int mandatory)
+{
+ u16 val;
+
+ if (dev_password_id == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Device Password ID "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(dev_password_id);
+ if (val >= 0x0006 && val <= 0x000f) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_manufacturer(const u8 *manufacturer, size_t len,
+ int mandatory)
+{
+ if (manufacturer == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Manufacturer "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && manufacturer[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Manufacturer "
+ "attribute value", manufacturer, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_model_name(const u8 *model_name, size_t len,
+ int mandatory)
+{
+ if (model_name == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Model Name "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && model_name[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Name "
+ "attribute value", model_name, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_model_number(const u8 *model_number, size_t len,
+ int mandatory)
+{
+ if (model_number == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Model Number "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && model_number[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Number "
+ "attribute value", model_number, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_serial_number(const u8 *serial_number, size_t len,
+ int mandatory)
+{
+ if (serial_number == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Serial Number "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && serial_number[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Serial "
+ "Number attribute value",
+ serial_number, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_dev_name(const u8 *dev_name, size_t len,
+ int mandatory)
+{
+ if (dev_name == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Device Name "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && dev_name[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Device Name "
+ "attribute value", dev_name, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_request_to_enroll(const u8 *request_to_enroll,
+ int mandatory)
+{
+ if (request_to_enroll == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Request to Enroll "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*request_to_enroll > 0x01) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request to Enroll "
+ "attribute value 0x%x", *request_to_enroll);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_req_dev_type(const u8 *req_dev_type[], size_t num,
+ int mandatory)
+{
+ if (num == 0) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Requested Device "
+ "Type attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_wps_state(const u8 *wps_state, int mandatory)
+{
+ if (wps_state == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Wi-Fi Protected "
+ "Setup State attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*wps_state != WPS_STATE_NOT_CONFIGURED &&
+ *wps_state != WPS_STATE_CONFIGURED) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Wi-Fi Protected "
+ "Setup State attribute value 0x%x", *wps_state);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_ap_setup_locked(const u8 *ap_setup_locked,
+ int mandatory)
+{
+ if (ap_setup_locked == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: AP Setup Locked "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*ap_setup_locked > 1) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid AP Setup Locked "
+ "attribute value 0x%x", *ap_setup_locked);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_selected_registrar(const u8 *selected_registrar,
+ int mandatory)
+{
+ if (selected_registrar == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*selected_registrar > 1) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
+ "attribute value 0x%x", *selected_registrar);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_sel_reg_config_methods(const u8 *config_methods,
+ int wps2, int mandatory)
+{
+ u16 val;
+
+ if (config_methods == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
+ "Configuration Methods attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+
+ val = WPA_GET_BE16(config_methods);
+ if (!valid_config_methods(val, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
+ "Configuration Methods attribute value 0x%04x",
+ val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_authorized_macs(const u8 *authorized_macs, size_t len,
+ int mandatory)
+{
+ if (authorized_macs == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authorized MACs "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 30 && (len % ETH_ALEN) != 0) {
+ wpa_hexdump(MSG_INFO, "WPS-STRICT: Invalid Authorized "
+ "MACs attribute value", authorized_macs, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_msg_type(const u8 *msg_type, int mandatory)
+{
+ if (msg_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Message Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*msg_type < WPS_Beacon || *msg_type > WPS_WSC_DONE) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Message Type "
+ "attribute value 0x%x", *msg_type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_mac_addr(const u8 *mac_addr, int mandatory)
+{
+ if (mac_addr == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: MAC Address "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (mac_addr[0] & 0x01) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid MAC Address "
+ "attribute value " MACSTR, MAC2STR(mac_addr));
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_enrollee_nonce(const u8 *enrollee_nonce, int mandatory)
+{
+ if (enrollee_nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Enrollee Nonce "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_registrar_nonce(const u8 *registrar_nonce,
+ int mandatory)
+{
+ if (registrar_nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Registrar Nonce "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_public_key(const u8 *public_key, size_t len,
+ int mandatory)
+{
+ if (public_key == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Public Key "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len != 192) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Public Key "
+ "attribute length %d", (int) len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int num_bits_set(u16 val)
+{
+ int c;
+ for (c = 0; val; c++)
+ val &= val - 1;
+ return c;
+}
+
+
+static int wps_validate_auth_type_flags(const u8 *flags, int mandatory)
+{
+ u16 val;
+
+ if (flags == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
+ "Flags attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(flags);
+ if ((val & ~WPS_AUTH_TYPES) || !(val & WPS_AUTH_WPA2PSK)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
+ "Flags attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_auth_type(const u8 *type, int mandatory)
+{
+ u16 val;
+
+ if (type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(type);
+ if ((val & ~WPS_AUTH_TYPES) || val == 0 ||
+ (num_bits_set(val) > 1 &&
+ val != (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_encr_type_flags(const u8 *flags, int mandatory)
+{
+ u16 val;
+
+ if (flags == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
+ "Flags attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(flags);
+ if ((val & ~WPS_ENCR_TYPES) || !(val & WPS_ENCR_AES)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
+ "Flags attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_encr_type(const u8 *type, int mandatory)
+{
+ u16 val;
+
+ if (type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(type);
+ if ((val & ~WPS_ENCR_TYPES) || val == 0 ||
+ (num_bits_set(val) > 1 && val != (WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_conn_type_flags(const u8 *flags, int mandatory)
+{
+ if (flags == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Connection Type "
+ "Flags attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if ((*flags & ~(WPS_CONN_ESS | WPS_CONN_IBSS)) ||
+ !(*flags & WPS_CONN_ESS)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Connection Type "
+ "Flags attribute value 0x%02x", *flags);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_os_version(const u8 *os_version, int mandatory)
+{
+ if (os_version == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: OS Version "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_authenticator(const u8 *authenticator, int mandatory)
+{
+ if (authenticator == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authenticator "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_hash1(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_hash2(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_hash1(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_hash2(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_encr_settings(const u8 *encr_settings, size_t len,
+ int mandatory)
+{
+ if (encr_settings == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Encrypted Settings "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len < 16) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encrypted Settings "
+ "attribute length %d", (int) len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_settings_delay_time(const u8 *delay, int mandatory)
+{
+ if (delay == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Settings Delay Time "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_snonce1(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_snonce2(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_snonce1(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_snonce2(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_key_wrap_auth(const u8 *auth, int mandatory)
+{
+ if (auth == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Key Wrap "
+ "Authenticator attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_ssid(const u8 *ssid, size_t ssid_len, int mandatory)
+{
+ if (ssid == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: SSID "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (ssid_len == 0 || ssid[ssid_len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid SSID "
+ "attribute value", ssid, ssid_len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_key_index(const u8 *idx, int mandatory)
+{
+ if (idx == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Key Index "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_idx(const u8 *idx, int mandatory)
+{
+ if (idx == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Index "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_key(const u8 *key, size_t key_len,
+ const u8 *encr_type, int mandatory)
+{
+ if (key == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (((encr_type == NULL || WPA_GET_BE16(encr_type) != WPS_ENCR_WEP) &&
+ key_len > 8 && key_len < 64 && key[key_len - 1] == 0) ||
+ key_len > 64) {
+ wpa_hexdump_ascii_key(MSG_INFO, "WPS-STRICT: Invalid Network "
+ "Key attribute value", key, key_len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_key_shareable(const u8 *val, int mandatory)
+{
+ if (val == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
+ "Shareable attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*val > 1) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Network Key "
+ "Shareable attribute value 0x%x", *val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_cred(const u8 *cred, size_t len)
+{
+ struct wps_parse_attr attr;
+ struct wpabuf buf;
+
+ if (cred == NULL)
+ return -1;
+ wpabuf_set(&buf, cred, len);
+ if (wps_parse_msg(&buf, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse Credential");
+ return -1;
+ }
+
+ if (wps_validate_network_idx(attr.network_idx, 1) ||
+ wps_validate_ssid(attr.ssid, attr.ssid_len, 1) ||
+ wps_validate_auth_type(attr.auth_type, 1) ||
+ wps_validate_encr_type(attr.encr_type, 1) ||
+ wps_validate_network_key_index(attr.network_key_idx, 0) ||
+ wps_validate_network_key(attr.network_key, attr.network_key_len,
+ attr.encr_type, 1) ||
+ wps_validate_mac_addr(attr.mac_addr, 1) ||
+ wps_validate_network_key_shareable(attr.network_key_shareable, 0))
+ {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Credential");
+ return -1;
+ }
+
+
+ return 0;
+}
+
+
+static int wps_validate_credential(const u8 *cred[], size_t len[], size_t num,
+ int mandatory)
+{
+ size_t i;
+
+ if (num == 0) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Credential "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (wps_validate_cred(cred[i], len[i]) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_beacon(const struct wpabuf *wps_ie)
+{
+ struct wps_parse_attr attr;
+ int wps2, sel_reg;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in Beacon frame");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "Beacon frame");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ sel_reg = attr.selected_registrar != NULL &&
+ *attr.selected_registrar != 0;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_wps_state(attr.wps_state, 1) ||
+ wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) ||
+ wps_validate_selected_registrar(attr.selected_registrar, 0) ||
+ wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
+ wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
+ wps2, sel_reg) ||
+ wps_validate_uuid_e(attr.uuid_e, 0) ||
+ wps_validate_rf_bands(attr.rf_bands, 0) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authorized_macs(attr.authorized_macs,
+ attr.authorized_macs_len, 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Beacon frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
+ const u8 *addr)
+{
+ struct wps_parse_attr attr;
+ int wps2, sel_reg;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "%sProbe Response frame", probe ? "" : "Beacon/");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "%sProbe Response frame", probe ? "" : "Beacon/");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ sel_reg = attr.selected_registrar != NULL &&
+ *attr.selected_registrar != 0;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_wps_state(attr.wps_state, 1) ||
+ wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) ||
+ wps_validate_selected_registrar(attr.selected_registrar, 0) ||
+ wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
+ wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
+ wps2, sel_reg) ||
+ wps_validate_response_type(attr.response_type, probe) ||
+ wps_validate_uuid_e(attr.uuid_e, probe) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ probe) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len,
+ probe) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ probe) ||
+ wps_validate_serial_number(attr.serial_number,
+ attr.serial_number_len, probe) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, probe) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, probe) ||
+ wps_validate_ap_config_methods(attr.config_methods, wps2, probe) ||
+ wps_validate_rf_bands(attr.rf_bands, 0) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authorized_macs(attr.authorized_macs,
+ attr.authorized_macs_len, 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid %sProbe Response "
+ "frame from " MACSTR, probe ? "" : "Beacon/",
+ MAC2STR(addr));
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "Probe Request frame");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "Probe Request frame");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_request_type(attr.request_type, 1) ||
+ wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+ wps_validate_uuid_e(attr.uuid_e, attr.uuid_r == NULL) ||
+ wps_validate_uuid_r(attr.uuid_r, attr.uuid_e == NULL) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+ wps_validate_rf_bands(attr.rf_bands, 1) ||
+ wps_validate_assoc_state(attr.assoc_state, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_dev_password_id(attr.dev_password_id, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ wps2) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len,
+ wps2) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ wps2) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, wps2) ||
+ wps_validate_request_to_enroll(attr.request_to_enroll, 0) ||
+ wps_validate_req_dev_type(attr.req_dev_type, attr.num_req_dev_type,
+ 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Probe Request "
+ "frame from " MACSTR, MAC2STR(addr));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_assoc_req(const struct wpabuf *wps_ie)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "(Re)Association Request frame");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "(Re)Association Request frame");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_request_type(attr.request_type, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
+ "Request frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "(Re)Association Response frame");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "(Re)Association Response frame");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_response_type(attr.response_type, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
+ "Response frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m1(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M1");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M1");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_uuid_e(attr.uuid_e, 1) ||
+ wps_validate_mac_addr(attr.mac_addr, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_public_key(attr.public_key, attr.public_key_len, 1) ||
+ wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
+ wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
+ wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
+ wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+ wps_validate_wps_state(attr.wps_state, 1) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ 1) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ 1) ||
+ wps_validate_serial_number(attr.serial_number,
+ attr.serial_number_len, 1) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
+ wps_validate_rf_bands(attr.rf_bands, 1) ||
+ wps_validate_assoc_state(attr.assoc_state, 1) ||
+ wps_validate_dev_password_id(attr.dev_password_id, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_os_version(attr.os_version, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_request_to_enroll(attr.request_to_enroll, 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M1");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m2(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M2");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_uuid_r(attr.uuid_r, 1) ||
+ wps_validate_public_key(attr.public_key, attr.public_key_len, 1) ||
+ wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
+ wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
+ wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
+ wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ 1) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ 1) ||
+ wps_validate_serial_number(attr.serial_number,
+ attr.serial_number_len, 1) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
+ wps_validate_rf_bands(attr.rf_bands, 1) ||
+ wps_validate_assoc_state(attr.assoc_state, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_dev_password_id(attr.dev_password_id, 1) ||
+ wps_validate_os_version(attr.os_version, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m2d(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2D");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M2D");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_uuid_r(attr.uuid_r, 1) ||
+ wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
+ wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
+ wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
+ wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ 1) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ 1) ||
+ wps_validate_serial_number(attr.serial_number,
+ attr.serial_number_len, 1) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
+ wps_validate_rf_bands(attr.rf_bands, 1) ||
+ wps_validate_assoc_state(attr.assoc_state, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_os_version(attr.os_version, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2D");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m3(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M3");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M3");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_e_hash1(attr.e_hash1, 1) ||
+ wps_validate_e_hash2(attr.e_hash2, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M3");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m4(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M4");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_r_hash1(attr.r_hash1, 1) ||
+ wps_validate_r_hash2(attr.r_hash2, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M4 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_r_snonce1(attr.r_snonce1, 1) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m5(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M5");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M5 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_e_snonce1(attr.e_snonce1, 1) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m6(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M6");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M6 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_r_snonce2(attr.r_snonce2, 1) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m7(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M7");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_settings_delay_time(attr.settings_delay_time, 0) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M7 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_e_snonce2(attr.e_snonce2, 1) ||
+ wps_validate_ssid(attr.ssid, attr.ssid_len, !ap) ||
+ wps_validate_mac_addr(attr.mac_addr, !ap) ||
+ wps_validate_auth_type(attr.auth_type, !ap) ||
+ wps_validate_encr_type(attr.encr_type, !ap) ||
+ wps_validate_network_key_index(attr.network_key_idx, 0) ||
+ wps_validate_network_key(attr.network_key, attr.network_key_len,
+ attr.encr_type, !ap) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m8(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M8");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M8 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_ssid(attr.ssid, attr.ssid_len, ap) ||
+ wps_validate_auth_type(attr.auth_type, ap) ||
+ wps_validate_encr_type(attr.encr_type, ap) ||
+ wps_validate_network_key_index(attr.network_key_idx, 0) ||
+ wps_validate_mac_addr(attr.mac_addr, ap) ||
+ wps_validate_credential(attr.cred, attr.cred_len, attr.num_cred,
+ !ap) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_wsc_ack(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_ACK");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in WSC_ACK");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_ACK");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_wsc_nack(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_NACK");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in WSC_NACK");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_NACK");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_wsc_done(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_Done");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in WSC_Done");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_Done");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+ int sel_reg;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in "
+ "SetSelectedRegistrar");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in SetSelectedRegistrar");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ sel_reg = attr.selected_registrar != NULL &&
+ *attr.selected_registrar != 0;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
+ wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
+ wps2, sel_reg) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authorized_macs(attr.authorized_macs,
+ attr.authorized_macs_len, wps2) ||
+ wps_validate_uuid_r(attr.uuid_r, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid "
+ "SetSelectedRegistrar");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}