diff options
Diffstat (limited to 'freebsd/contrib/wpa/src')
172 files changed, 78327 insertions, 0 deletions
diff --git a/freebsd/contrib/wpa/src/ap/ap_config.h b/freebsd/contrib/wpa/src/ap/ap_config.h new file mode 100644 index 00000000..de470a96 --- /dev/null +++ b/freebsd/contrib/wpa/src/ap/ap_config.h @@ -0,0 +1,692 @@ +/* + * hostapd / Configuration definitions and helpers functions + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HOSTAPD_CONFIG_H +#define HOSTAPD_CONFIG_H + +#include "common/defs.h" +#include "ip_addr.h" +#include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "wps/wps.h" +#include "fst/fst.h" + +/** + * mesh_conf - local MBSS state and settings + */ +struct mesh_conf { + u8 meshid[32]; + u8 meshid_len; + /* Active Path Selection Protocol Identifier */ + u8 mesh_pp_id; + /* Active Path Selection Metric Identifier */ + u8 mesh_pm_id; + /* Congestion Control Mode Identifier */ + u8 mesh_cc_id; + /* Synchronization Protocol Identifier */ + u8 mesh_sp_id; + /* Authentication Protocol Identifier */ + u8 mesh_auth_id; + u8 *rsn_ie; + int rsn_ie_len; +#define MESH_CONF_SEC_NONE BIT(0) +#define MESH_CONF_SEC_AUTH BIT(1) +#define MESH_CONF_SEC_AMPE BIT(2) + unsigned int security; + int dot11MeshMaxRetries; + int dot11MeshRetryTimeout; /* msec */ + int dot11MeshConfirmTimeout; /* msec */ + int dot11MeshHoldingTimeout; /* msec */ +}; + +#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 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, + SECURITY_OSEN = 5 +} secpolicy; + +struct hostapd_ssid { + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; + unsigned int ssid_set:1; + unsigned int utf8_ssid:1; + unsigned int wpa_passphrase_set:1; + unsigned int wpa_psk_set:1; + + char vlan[IFNAMSIZ + 1]; + secpolicy security_policy; + + struct hostapd_wpa_psk *wpa_psk; + char *wpa_passphrase; + char *wpa_psk_file; + + struct hostapd_wep_keys wep; + +#define DYNAMIC_VLAN_DISABLED 0 +#define DYNAMIC_VLAN_OPTIONAL 1 +#define DYNAMIC_VLAN_REQUIRED 2 + int dynamic_vlan; +#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0 +#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1 +#define DYNAMIC_VLAN_NAMING_END 2 + int vlan_naming; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + char *vlan_tagged_interface; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +}; + + +#define VLAN_ID_WILDCARD -1 + +struct hostapd_vlan { + struct hostapd_vlan *next; + int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ + char ifname[IFNAMSIZ + 1]; + int configured; + int dynamic_vlan; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + +#define DVLAN_CLEAN_WLAN_PORT 0x8 + int clean; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +}; + +#define PMK_LEN 32 +struct hostapd_sta_wpa_psk_short { + struct hostapd_sta_wpa_psk_short *next; + u8 psk[PMK_LEN]; +}; + +struct hostapd_wpa_psk { + struct hostapd_wpa_psk *next; + int group; + u8 psk[PMK_LEN]; + u8 addr[ETH_ALEN]; + u8 p2p_dev_addr[ETH_ALEN]; +}; + +struct hostapd_eap_user { + struct hostapd_eap_user *next; + u8 *identity; + size_t identity_len; + struct { + int vendor; + u32 method; + } methods[EAP_MAX_METHODS]; + u8 *password; + size_t password_len; + int phase2; + int force_version; + unsigned int wildcard_prefix:1; + unsigned int password_hash:1; /* whether password is hashed with + * nt_password_hash() */ + unsigned int remediation:1; + unsigned int macacl:1; + int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ + struct hostapd_radius_attr *accept_attr; +}; + +struct hostapd_radius_attr { + u8 type; + struct wpabuf *val; + struct hostapd_radius_attr *next; +}; + + +#define NUM_TX_QUEUES 4 + +struct hostapd_tx_queue_params { + int aifs; + int cwmin; + int cwmax; + int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */ +}; + + +#define MAX_ROAMING_CONSORTIUM_LEN 15 + +struct hostapd_roaming_consortium { + u8 len; + u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; +}; + +struct hostapd_lang_string { + u8 lang[3]; + u8 name_len; + u8 name[252]; +}; + +#define MAX_NAI_REALMS 10 +#define MAX_NAI_REALMLEN 255 +#define MAX_NAI_EAP_METHODS 5 +#define MAX_NAI_AUTH_TYPES 4 +struct hostapd_nai_realm_data { + u8 encoding; + char realm_buf[MAX_NAI_REALMLEN + 1]; + char *realm[MAX_NAI_REALMS]; + u8 eap_method_count; + struct hostapd_nai_realm_eap { + u8 eap_method; + u8 num_auths; + u8 auth_id[MAX_NAI_AUTH_TYPES]; + u8 auth_val[MAX_NAI_AUTH_TYPES]; + } eap_method[MAX_NAI_EAP_METHODS]; +}; + +/** + * struct hostapd_bss_config - Per-BSS configuration + */ +struct hostapd_bss_config { + char iface[IFNAMSIZ + 1]; + char bridge[IFNAMSIZ + 1]; + char vlan_bridge[IFNAMSIZ + 1]; + char wds_bridge[IFNAMSIZ + 1]; + + enum hostapd_logger_level logger_syslog_level, logger_stdout_level; + + unsigned int logger_syslog; /* module bitfield */ + unsigned int logger_stdout; /* module bitfield */ + + int max_num_sta; /* maximum number of STAs in station table */ + + int dtim_period; + int bss_load_update_period; + + int ieee802_1x; /* use IEEE 802.1X */ + int eapol_version; + int eap_server; /* Use internal EAP server instead of external + * RADIUS server */ + struct hostapd_eap_user *eap_user; + char *eap_user_sqlite; + char *eap_sim_db; + int eap_server_erp; /* Whether ERP is enabled on internal EAP server */ + struct hostapd_ip_addr own_ip_addr; + char *nas_identifier; + struct hostapd_radius_servers *radius; + int acct_interim_interval; + int radius_request_cui; + struct hostapd_radius_attr *radius_auth_req_attr; + struct hostapd_radius_attr *radius_acct_req_attr; + int radius_das_port; + unsigned int radius_das_time_window; + int radius_das_require_event_timestamp; + struct hostapd_ip_addr radius_das_client_addr; + u8 *radius_das_shared_secret; + size_t radius_das_shared_secret_len; + + struct hostapd_ssid ssid; + + char *eap_req_id_text; /* optional displayable message sent with + * EAP Request-Identity */ + size_t eap_req_id_text_len; + int eapol_key_index_workaround; + + size_t default_wep_key_len; + int individual_wep_key_len; + int wep_rekeying_period; + int broadcast_key_idx_min, broadcast_key_idx_max; + int eap_reauth_period; + int erp_send_reauth_start; + char *erp_domain; + + int ieee802_11f; /* use IEEE 802.11f (IAPP) */ + char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast + * frames */ + + enum { + ACCEPT_UNLESS_DENIED = 0, + DENY_UNLESS_ACCEPTED = 1, + USE_EXTERNAL_RADIUS_AUTH = 2 + } macaddr_acl; + struct mac_acl_entry *accept_mac; + int num_accept_mac; + struct mac_acl_entry *deny_mac; + int num_deny_mac; + int wds_sta; + int isolate; + int start_disabled; + + int auth_algs; /* bitfield of allowed IEEE 802.11 authentication + * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ + + int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */ + int wpa_key_mgmt; +#ifdef CONFIG_IEEE80211W + enum mfp_options ieee80211w; + int group_mgmt_cipher; + /* dot11AssociationSAQueryMaximumTimeout (in TUs) */ + unsigned int assoc_sa_query_max_timeout; + /* dot11AssociationSAQueryRetryTimeout (in TUs) */ + int assoc_sa_query_retry_timeout; +#endif /* CONFIG_IEEE80211W */ + enum { + PSK_RADIUS_IGNORED = 0, + PSK_RADIUS_ACCEPTED = 1, + PSK_RADIUS_REQUIRED = 2 + } wpa_psk_radius; + int wpa_pairwise; + int wpa_group; + int wpa_group_rekey; + int wpa_strict_rekey; + int wpa_gmk_rekey; + int wpa_ptk_rekey; + int rsn_pairwise; + int rsn_preauth; + char *rsn_preauth_interfaces; + int peerkey; + +#ifdef CONFIG_IEEE80211R + /* IEEE 802.11r - Fast BSS Transition */ + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r1_key_holder[FT_R1KH_ID_LEN]; + u32 r0_key_lifetime; + u32 reassociation_deadline; + struct ft_remote_r0kh *r0kh_list; + struct ft_remote_r1kh *r1kh_list; + int pmk_r1_push; + int ft_over_ds; +#endif /* CONFIG_IEEE80211R */ + + char *ctrl_interface; /* directory for UNIX domain sockets */ +#ifndef CONFIG_NATIVE_WINDOWS + gid_t ctrl_interface_gid; +#endif /* CONFIG_NATIVE_WINDOWS */ + int ctrl_interface_gid_set; + + char *ca_cert; + char *server_cert; + char *private_key; + char *private_key_passwd; + int check_crl; + unsigned int tls_session_lifetime; + char *ocsp_stapling_response; + char *dh_file; + char *openssl_ciphers; + 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_acct_port; + int radius_server_ipv6; + + int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group + * address instead of individual address + * (for driver_wired.c). + */ + + int ap_max_inactivity; + int ignore_broadcast_ssid; + + int wmm_enabled; + int wmm_uapsd; + + struct hostapd_vlan *vlan; + + macaddr bssid; + + /* + * Maximum listen interval that STAs can use when associating with this + * BSS. If a STA tries to use larger value, the association will be + * denied with status code 51. + */ + u16 max_listen_interval; + + int disable_pmksa_caching; + int okc; /* Opportunistic Key Caching */ + + int wps_state; +#ifdef CONFIG_WPS + int wps_independent; + int ap_setup_locked; + u8 uuid[16]; + char *wps_pin_requests; + char *device_name; + char *manufacturer; + char *model_name; + char *model_number; + char *serial_number; + u8 device_type[WPS_DEV_TYPE_LEN]; + char *config_methods; + u8 os_version[4]; + char *ap_pin; + int skip_cred_build; + u8 *extra_cred; + size_t extra_cred_len; + int wps_cred_processing; + int force_per_enrollee_psk; + u8 *ap_settings; + size_t ap_settings_len; + char *upnp_iface; + char *friendly_name; + char *manufacturer_url; + char *model_description; + char *model_url; + char *upc; + struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; + int wps_nfc_pw_from_config; + int wps_nfc_dev_pw_id; + struct wpabuf *wps_nfc_dh_pubkey; + struct wpabuf *wps_nfc_dh_privkey; + struct wpabuf *wps_nfc_dev_pw; +#endif /* CONFIG_WPS */ + int pbc_in_m1; + char *server_id; + +#define P2P_ENABLED BIT(0) +#define P2P_GROUP_OWNER BIT(1) +#define P2P_GROUP_FORMATION BIT(2) +#define P2P_MANAGE BIT(3) +#define P2P_ALLOW_CROSS_CONNECTION BIT(4) + int p2p; +#ifdef CONFIG_P2P + u8 ip_addr_go[4]; + u8 ip_addr_mask[4]; + u8 ip_addr_start[4]; + u8 ip_addr_end[4]; +#endif /* CONFIG_P2P */ + + int disassoc_low_ack; + int skip_inactivity_poll; + +#define TDLS_PROHIBIT BIT(0) +#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1) + int tdls; + int disable_11n; + int disable_11ac; + + /* IEEE 802.11v */ + int time_advertisement; + char *time_zone; + int wnm_sleep_mode; + int bss_transition; + + /* IEEE 802.11u - Interworking */ + int interworking; + int access_network_type; + int internet; + int asra; + int esr; + int uesa; + int venue_info_set; + u8 venue_group; + u8 venue_type; + u8 hessid[ETH_ALEN]; + + /* IEEE 802.11u - Roaming Consortium list */ + unsigned int roaming_consortium_count; + struct hostapd_roaming_consortium *roaming_consortium; + + /* IEEE 802.11u - Venue Name duples */ + unsigned int venue_name_count; + struct hostapd_lang_string *venue_name; + + /* IEEE 802.11u - Network Authentication Type */ + u8 *network_auth_type; + size_t network_auth_type_len; + + /* IEEE 802.11u - IP Address Type Availability */ + u8 ipaddr_type_availability; + u8 ipaddr_type_configured; + + /* IEEE 802.11u - 3GPP Cellular Network */ + u8 *anqp_3gpp_cell_net; + size_t anqp_3gpp_cell_net_len; + + /* IEEE 802.11u - Domain Name */ + u8 *domain_name; + size_t domain_name_len; + + unsigned int nai_realm_count; + struct hostapd_nai_realm_data *nai_realm_data; + + u16 gas_comeback_delay; + int gas_frag_limit; + + u8 qos_map_set[16 + 2 * 21]; + unsigned int qos_map_set_len; + + int osen; + int proxy_arp; + int na_mcast_to_ucast; +#ifdef CONFIG_HS20 + int hs20; + int disable_dgaf; + u16 anqp_domain_id; + unsigned int hs20_oper_friendly_name_count; + struct hostapd_lang_string *hs20_oper_friendly_name; + u8 *hs20_wan_metrics; + u8 *hs20_connection_capability; + size_t hs20_connection_capability_len; + u8 *hs20_operating_class; + u8 hs20_operating_class_len; + struct hs20_icon { + u16 width; + u16 height; + char language[3]; + char type[256]; + char name[256]; + char file[256]; + } *hs20_icons; + size_t hs20_icons_count; + u8 osu_ssid[SSID_MAX_LEN]; + size_t osu_ssid_len; + struct hs20_osu_provider { + unsigned int friendly_name_count; + struct hostapd_lang_string *friendly_name; + char *server_uri; + int *method_list; + char **icons; + size_t icons_count; + char *osu_nai; + unsigned int service_desc_count; + struct hostapd_lang_string *service_desc; + } *hs20_osu_providers, *last_osu; + size_t hs20_osu_providers_count; + unsigned int hs20_deauth_req_timeout; + char *subscr_remediation_url; + u8 subscr_remediation_method; +#endif /* CONFIG_HS20 */ + + u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ + +#ifdef CONFIG_RADIUS_TEST + char *dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ + + struct wpabuf *vendor_elements; + + unsigned int sae_anti_clogging_threshold; + int *sae_groups; + + char *wowlan_triggers; /* Wake-on-WLAN triggers */ + +#ifdef CONFIG_TESTING_OPTIONS + u8 bss_load_test[5]; + u8 bss_load_test_set; + struct wpabuf *own_ie_override; +#endif /* CONFIG_TESTING_OPTIONS */ + +#define MESH_ENABLED BIT(0) + int mesh; + + int radio_measurements; + + int vendor_vht; + + char *no_probe_resp_if_seen_on; + char *no_auth_if_seen_on; +}; + + +/** + * 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; + u8 acs; + struct wpa_freq_range_list acs_ch_list; + enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ + enum { + LONG_PREAMBLE = 0, + SHORT_PREAMBLE = 1 + } preamble; + + int *supported_rates; + int *basic_rates; + + const struct wpa_driver_ops *driver; + char *driver_params; + + int ap_table_max_size; + int ap_table_expiration_time; + + unsigned int track_sta_max_num; + unsigned int track_sta_max_age; + + char country[3]; /* first two octets: country code as described in + * ISO/IEC 3166-1. Third octet: + * ' ' (ascii 32): all environments + * 'O': Outdoor environemnt only + * 'I': Indoor environment only + */ + + int ieee80211d; + + int ieee80211h; /* DFS */ + + /* + * Local power constraint is an octet encoded as an unsigned integer in + * units of decibels. Invalid value -1 indicates that Power Constraint + * element will not be added. + */ + int local_pwr_constraint; + + /* Control Spectrum Management bit */ + int spectrum_mgmt_required; + + 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 no_pri_sec_switch; + int require_ht; + int obss_interval; + u32 vht_capab; + int ieee80211ac; + int require_vht; + u8 vht_oper_chwidth; + u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; + +#ifdef CONFIG_FST + struct fst_iface_cfg fst_cfg; +#endif /* CONFIG_FST */ + +#ifdef CONFIG_P2P + u8 p2p_go_ctwindow; +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_TESTING_OPTIONS + double ignore_probe_probability; + double ignore_auth_probability; + double ignore_assoc_probability; + double ignore_reassoc_probability; + double corrupt_gtk_rekey_mic_probability; +#endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_ACS + unsigned int acs_num_scans; + struct acs_bias { + int channel; + double bias; + } *acs_chan_bias; + unsigned int num_acs_chan_bias; +#endif /* CONFIG_ACS */ +}; + + +int hostapd_mac_comp(const void *a, const void *b); +int hostapd_mac_comp_empty(const void *a); +struct hostapd_config * hostapd_config_defaults(void); +void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); +void hostapd_config_free_eap_user(struct hostapd_eap_user *user); +void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p); +void hostapd_config_free_bss(struct hostapd_bss_config *conf); +void hostapd_config_free(struct hostapd_config *conf); +int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, + const u8 *addr, int *vlan_id); +int hostapd_rate_found(int *list, int rate); +const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, + const u8 *addr, const u8 *p2p_dev_addr, + const u8 *prev_psk); +int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); +int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id); +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, + int vlan_id); +struct hostapd_radius_attr * +hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type); +int hostapd_config_check(struct hostapd_config *conf, int full_config); +void hostapd_set_security_params(struct hostapd_bss_config *bss, + int full_config); + +#endif /* HOSTAPD_CONFIG_H */ diff --git a/freebsd/contrib/wpa/src/ap/ap_drv_ops.c b/freebsd/contrib/wpa/src/ap/ap_drv_ops.c new file mode 100644 index 00000000..d20ee93f --- /dev/null +++ b/freebsd/contrib/wpa/src/ap/ap_drv_ops.c @@ -0,0 +1,844 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * hostapd - Driver operations + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/hw_features_common.h" +#include "wps/wps.h" +#include "p2p/p2p.h" +#include "hostapd.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "ap_config.h" +#include "p2p_hostapd.h" +#include "hs20.h" +#include "ap_drv_ops.h" + + +u32 hostapd_sta_flags_to_drv(u32 flags) +{ + int res = 0; + if (flags & WLAN_STA_AUTHORIZED) + res |= WPA_STA_AUTHORIZED; + if (flags & WLAN_STA_WMM) + res |= WPA_STA_WMM; + if (flags & WLAN_STA_SHORT_PREAMBLE) + res |= WPA_STA_SHORT_PREAMBLE; + if (flags & WLAN_STA_MFP) + res |= WPA_STA_MFP; + return res; +} + + +int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf **beacon_ret, + struct wpabuf **proberesp_ret, + struct wpabuf **assocresp_ret) +{ + struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL; + u8 buf[200], *pos; + + *beacon_ret = *proberesp_ret = *assocresp_ret = NULL; + + pos = buf; + pos = hostapd_eid_time_adv(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + } + pos = hostapd_eid_time_zone(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } + + pos = buf; + pos = hostapd_eid_ext_capab(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&assocresp, pos - buf) != 0) + goto fail; + wpabuf_put_data(assocresp, buf, pos - buf); + } + pos = hostapd_eid_interworking(hapd, pos); + pos = hostapd_eid_adv_proto(hapd, pos); + pos = hostapd_eid_roaming_consortium(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } + +#ifdef CONFIG_FST + if (hapd->iface->fst_ies) { + size_t add = wpabuf_len(hapd->iface->fst_ies); + + if (wpabuf_resize(&beacon, add) < 0) + goto fail; + wpabuf_put_buf(beacon, hapd->iface->fst_ies); + if (wpabuf_resize(&proberesp, add) < 0) + goto fail; + wpabuf_put_buf(proberesp, hapd->iface->fst_ies); + if (wpabuf_resize(&assocresp, add) < 0) + goto fail; + wpabuf_put_buf(assocresp, hapd->iface->fst_ies); + } +#endif /* CONFIG_FST */ + + if (hapd->wps_beacon_ie) { + if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) < + 0) + goto fail; + wpabuf_put_buf(beacon, hapd->wps_beacon_ie); + } + + if (hapd->wps_probe_resp_ie) { + if (wpabuf_resize(&proberesp, + wpabuf_len(hapd->wps_probe_resp_ie)) < 0) + goto fail; + wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie); + } + +#ifdef CONFIG_P2P + if (hapd->p2p_beacon_ie) { + if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) < + 0) + goto fail; + wpabuf_put_buf(beacon, hapd->p2p_beacon_ie); + } + + if (hapd->p2p_probe_resp_ie) { + if (wpabuf_resize(&proberesp, + wpabuf_len(hapd->p2p_probe_resp_ie)) < 0) + goto fail; + wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie); + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_P2P_MANAGER + if (hapd->conf->p2p & P2P_MANAGE) { + if (wpabuf_resize(&beacon, 100) == 0) { + u8 *start, *p; + start = wpabuf_put(beacon, 0); + p = hostapd_eid_p2p_manage(hapd, start); + wpabuf_put(beacon, p - start); + } + + if (wpabuf_resize(&proberesp, 100) == 0) { + u8 *start, *p; + start = wpabuf_put(proberesp, 0); + p = hostapd_eid_p2p_manage(hapd, start); + wpabuf_put(proberesp, p - start); + } + } +#endif /* CONFIG_P2P_MANAGER */ + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state) { + struct wpabuf *a = wps_build_assoc_resp_ie(); + if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) + wpabuf_put_buf(assocresp, a); + wpabuf_free(a); + } +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_P2P_MANAGER + if (hapd->conf->p2p & P2P_MANAGE) { + if (wpabuf_resize(&assocresp, 100) == 0) { + u8 *start, *p; + start = wpabuf_put(assocresp, 0); + p = hostapd_eid_p2p_manage(hapd, start); + wpabuf_put(assocresp, p - start); + } + } +#endif /* CONFIG_P2P_MANAGER */ + +#ifdef CONFIG_WIFI_DISPLAY + if (hapd->p2p_group) { + struct wpabuf *a; + a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS); + if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) + wpabuf_put_buf(assocresp, a); + wpabuf_free(a); + } +#endif /* CONFIG_WIFI_DISPLAY */ + +#ifdef CONFIG_HS20 + pos = buf; + pos = hostapd_eid_hs20_indication(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } + + pos = hostapd_eid_osen(hapd, buf); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } +#endif /* CONFIG_HS20 */ + + if (hapd->conf->vendor_elements) { + size_t add = wpabuf_len(hapd->conf->vendor_elements); + if (wpabuf_resize(&beacon, add) == 0) + wpabuf_put_buf(beacon, hapd->conf->vendor_elements); + if (wpabuf_resize(&proberesp, add) == 0) + wpabuf_put_buf(proberesp, hapd->conf->vendor_elements); + } + + *beacon_ret = beacon; + *proberesp_ret = proberesp; + *assocresp_ret = assocresp; + + return 0; + +fail: + wpabuf_free(beacon); + wpabuf_free(proberesp); + wpabuf_free(assocresp); + return -1; +} + + +void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf *beacon, + struct wpabuf *proberesp, + struct wpabuf *assocresp) +{ + wpabuf_free(beacon); + wpabuf_free(proberesp); + wpabuf_free(assocresp); +} + + +int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL) + return 0; + + return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL); +} + + +int hostapd_set_ap_wps_ie(struct hostapd_data *hapd) +{ + struct wpabuf *beacon, *proberesp, *assocresp; + int ret; + + if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL) + return 0; + + if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) < + 0) + return -1; + + ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp, + assocresp); + + hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); + + return ret; +} + + +int hostapd_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized) +{ + if (authorized) { + return hostapd_sta_set_flags(hapd, sta->addr, + hostapd_sta_flags_to_drv( + sta->flags), + WPA_STA_AUTHORIZED, ~0); + } + + return hostapd_sta_set_flags(hapd, sta->addr, + hostapd_sta_flags_to_drv(sta->flags), + 0, ~WPA_STA_AUTHORIZED); +} + + +int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta) +{ + int set_flags, total_flags, flags_and, flags_or; + total_flags = hostapd_sta_flags_to_drv(sta->flags); + set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP; + if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) || + sta->auth_alg == WLAN_AUTH_FT) && + sta->flags & WLAN_STA_AUTHORIZED) + set_flags |= WPA_STA_AUTHORIZED; + flags_or = total_flags & set_flags; + flags_and = total_flags | ~set_flags; + return hostapd_sta_set_flags(hapd, sta->addr, total_flags, + flags_or, flags_and); +} + + +int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, + int enabled) +{ + struct wpa_bss_params params; + os_memset(¶ms, 0, sizeof(params)); + params.ifname = ifname; + params.enabled = enabled; + if (enabled) { + params.wpa = hapd->conf->wpa; + params.ieee802_1x = hapd->conf->ieee802_1x; + params.wpa_group = hapd->conf->wpa_group; + if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == + (WPA_PROTO_WPA | WPA_PROTO_RSN)) + params.wpa_pairwise = hapd->conf->wpa_pairwise | + hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_RSN) + params.wpa_pairwise = hapd->conf->rsn_pairwise; + else if (hapd->conf->wpa & WPA_PROTO_WPA) + params.wpa_pairwise = hapd->conf->wpa_pairwise; + params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt; + params.rsn_preauth = hapd->conf->rsn_preauth; +#ifdef CONFIG_IEEE80211W + params.ieee80211w = hapd->conf->ieee80211w; +#endif /* CONFIG_IEEE80211W */ + } + return hostapd_set_ieee8021x(hapd, ¶ms); +} + + +int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname) +{ + char force_ifname[IFNAMSIZ]; + u8 if_addr[ETH_ALEN]; + return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr, + NULL, NULL, force_ifname, if_addr, NULL, 0); +} + + +int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname) +{ + return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname); +} + + +int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds, + const u8 *addr, int aid, int val) +{ + const char *bridge = NULL; + + if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL) + return -1; + if (hapd->conf->wds_bridge[0]) + bridge = hapd->conf->wds_bridge; + else if (hapd->conf->bridge[0]) + bridge = hapd->conf->bridge; + return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val, + bridge, ifname_wds); +} + + +int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, + u16 auth_alg) +{ + if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL) + return 0; + return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg); +} + + +int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->sta_auth == NULL) + return 0; + return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr, + seq, status, ie, len); +} + + +int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, + int reassoc, u16 status, const u8 *ie, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL) + return 0; + return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr, + reassoc, status, ie, len); +} + + +int hostapd_sta_add(struct hostapd_data *hapd, + const u8 *addr, u16 aid, u16 capability, + const u8 *supp_rates, size_t supp_rates_len, + u16 listen_interval, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u32 flags, u8 qosinfo, u8 vht_opmode) +{ + struct hostapd_sta_add_params params; + + if (hapd->driver == NULL) + return 0; + if (hapd->driver->sta_add == NULL) + return 0; + + os_memset(¶ms, 0, sizeof(params)); + params.addr = addr; + params.aid = aid; + params.capability = capability; + params.supp_rates = supp_rates; + params.supp_rates_len = supp_rates_len; + params.listen_interval = listen_interval; + params.ht_capabilities = ht_capab; + params.vht_capabilities = vht_capab; + params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED); + params.vht_opmode = vht_opmode; + params.flags = hostapd_sta_flags_to_drv(flags); + params.qosinfo = qosinfo; + return hapd->driver->sta_add(hapd->drv_priv, ¶ms); +} + + +int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, + u8 *tspec_ie, size_t tspec_ielen) +{ + if (hapd->driver == NULL || hapd->driver->add_tspec == NULL) + return 0; + return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie, + tspec_ielen); +} + + +int hostapd_set_privacy(struct hostapd_data *hapd, int enabled) +{ + if (hapd->driver == NULL || hapd->driver->set_privacy == NULL) + return 0; + return hapd->driver->set_privacy(hapd->drv_priv, enabled); +} + + +int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, + size_t elem_len) +{ + if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL) + return 0; + return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len); +} + + +int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL) + return 0; + return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len); +} + + +int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL) + return 0; + return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len); +} + + +int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, + const char *ifname, const u8 *addr, void *bss_ctx, + void **drv_priv, char *force_ifname, u8 *if_addr, + const char *bridge, int use_existing) +{ + if (hapd->driver == NULL || hapd->driver->if_add == NULL) + return -1; + return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr, + bss_ctx, drv_priv, force_ifname, if_addr, + bridge, use_existing); +} + + +int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type, + const char *ifname) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->if_remove == NULL) + return -1; + return hapd->driver->if_remove(hapd->drv_priv, type, ifname); +} + + +int hostapd_set_ieee8021x(struct hostapd_data *hapd, + struct wpa_bss_params *params) +{ + if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL) + return 0; + return hapd->driver->set_ieee8021x(hapd->drv_priv, params); +} + + +int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, + const u8 *addr, int idx, u8 *seq) +{ + if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL) + return 0; + return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx, + seq); +} + + +int hostapd_flush(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->flush == NULL) + return 0; + return hapd->driver->flush(hapd->drv_priv); +} + + +int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode, + int freq, int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1) +{ + struct hostapd_freq_params data; + + if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled, + vht_enabled, sec_channel_offset, + vht_oper_chwidth, + center_segment0, center_segment1, + hapd->iface->current_mode ? + hapd->iface->current_mode->vht_capab : 0)) + return -1; + + if (hapd->driver == NULL) + return 0; + if (hapd->driver->set_freq == NULL) + return 0; + return hapd->driver->set_freq(hapd->drv_priv, &data); +} + +int hostapd_set_rts(struct hostapd_data *hapd, int rts) +{ + if (hapd->driver == NULL || hapd->driver->set_rts == NULL) + return 0; + return hapd->driver->set_rts(hapd->drv_priv, rts); +} + + +int hostapd_set_frag(struct hostapd_data *hapd, int frag) +{ + if (hapd->driver == NULL || hapd->driver->set_frag == NULL) + return 0; + return hapd->driver->set_frag(hapd->drv_priv, frag); +} + + +int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL) + return 0; + return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags, + flags_or, flags_and); +} + + +int hostapd_set_country(struct hostapd_data *hapd, const char *country) +{ + if (hapd->driver == NULL || + hapd->driver->set_country == NULL) + return 0; + return hapd->driver->set_country(hapd->drv_priv, country); +} + + +int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, + int cw_min, int cw_max, int burst_time) +{ + if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL) + return 0; + return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs, + cw_min, cw_max, burst_time); +} + + +struct hostapd_hw_modes * +hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, + u16 *flags) +{ + if (hapd->driver == NULL || + hapd->driver->get_hw_feature_data == NULL) + return NULL; + return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes, + flags); +} + + +int hostapd_driver_commit(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->commit == NULL) + return 0; + return hapd->driver->commit(hapd->drv_priv); +} + + +int hostapd_drv_none(struct hostapd_data *hapd) +{ + return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0; +} + + +int hostapd_driver_scan(struct hostapd_data *hapd, + struct wpa_driver_scan_params *params) +{ + if (hapd->driver && hapd->driver->scan2) + return hapd->driver->scan2(hapd->drv_priv, params); + return -1; +} + + +struct wpa_scan_results * hostapd_driver_get_scan_results( + struct hostapd_data *hapd) +{ + if (hapd->driver && hapd->driver->get_scan_results2) + return hapd->driver->get_scan_results2(hapd->drv_priv); + return NULL; +} + + +int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration) +{ + if (hapd->driver && hapd->driver->set_noa) + return hapd->driver->set_noa(hapd->drv_priv, count, start, + duration); + return -1; +} + + +int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + if (hapd->driver == NULL || hapd->driver->set_key == NULL) + return 0; + return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr, + key_idx, set_tx, seq, seq_len, key, + key_len); +} + + +int hostapd_drv_send_mlme(struct hostapd_data *hapd, + const void *msg, size_t len, int noack) +{ + if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) + return 0; + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0); +} + + +int hostapd_drv_sta_deauth(struct hostapd_data *hapd, + const u8 *addr, int reason) +{ + if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) + return 0; + return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr, + reason); +} + + +int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, + const u8 *addr, int reason) +{ + if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) + return 0; + return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, + reason); +} + + +int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper, + const u8 *peer, u8 *buf, u16 *buf_len) +{ + if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL) + return -1; + return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf, + buf_len); +} + + +int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *data, + size_t len) +{ + if (hapd->driver == NULL || hapd->driver->send_action == NULL) + return 0; + return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, + hapd->own_addr, hapd->own_addr, data, + len, 0); +} + + +int hostapd_start_dfs_cac(struct hostapd_iface *iface, + enum hostapd_hw_mode mode, int freq, + int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1) +{ + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_freq_params data; + int res; + + if (!hapd->driver || !hapd->driver->start_dfs_cac) + return 0; + + if (!iface->conf->ieee80211h) { + wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality " + "is not enabled"); + return -1; + } + + if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled, + vht_enabled, sec_channel_offset, + vht_oper_chwidth, center_segment0, + center_segment1, + iface->current_mode->vht_capab)) { + wpa_printf(MSG_ERROR, "Can't set freq params"); + return -1; + } + + res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data); + if (!res) { + iface->cac_started = 1; + os_get_reltime(&iface->dfs_cac_start); + } + + return res; +} + + +int hostapd_drv_set_qos_map(struct hostapd_data *hapd, + const u8 *qos_map_set, u8 qos_map_set_len) +{ + if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL) + return 0; + return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set, + qos_map_set_len); +} + + +static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, + struct hostapd_hw_modes *mode, + int acs_ch_list_all, + int **freq_list) +{ + int i; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + + if ((acs_ch_list_all || + freq_range_list_includes(&hapd->iface->conf->acs_ch_list, + chan->chan)) && + !(chan->flag & HOSTAPD_CHAN_DISABLED)) + int_array_add_unique(freq_list, chan->freq); + } +} + + +int hostapd_drv_do_acs(struct hostapd_data *hapd) +{ + struct drv_acs_params params; + int ret, i, acs_ch_list_all = 0; + u8 *channels = NULL; + unsigned int num_channels = 0; + struct hostapd_hw_modes *mode; + int *freq_list = NULL; + + if (hapd->driver == NULL || hapd->driver->do_acs == NULL) + return 0; + + os_memset(¶ms, 0, sizeof(params)); + params.hw_mode = hapd->iface->conf->hw_mode; + + /* + * If no chanlist config parameter is provided, include all enabled + * channels of the selected hw_mode. + */ + if (!hapd->iface->conf->acs_ch_list.num) + acs_ch_list_all = 1; + + mode = hapd->iface->current_mode; + if (mode) { + channels = os_malloc(mode->num_channels); + if (channels == NULL) + return -1; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (!acs_ch_list_all && + !freq_range_list_includes( + &hapd->iface->conf->acs_ch_list, + chan->chan)) + continue; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) { + channels[num_channels++] = chan->chan; + int_array_add_unique(&freq_list, chan->freq); + } + } + } else { + for (i = 0; i < hapd->iface->num_hw_features; i++) { + mode = &hapd->iface->hw_features[i]; + hostapd_get_hw_mode_any_channels(hapd, mode, + acs_ch_list_all, + &freq_list); + } + } + + params.ch_list = channels; + params.ch_list_len = num_channels; + params.freq_list = freq_list; + + params.ht_enabled = !!(hapd->iface->conf->ieee80211n); + params.ht40_enabled = !!(hapd->iface->conf->ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); + params.vht_enabled = !!(hapd->iface->conf->ieee80211ac); + params.ch_width = 20; + if (hapd->iface->conf->ieee80211n && params.ht40_enabled) + params.ch_width = 40; + + /* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth + */ + if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) { + if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) + params.ch_width = 80; + else if (hapd->iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_160MHZ || + hapd->iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_80P80MHZ) + params.ch_width = 160; + } + + ret = hapd->driver->do_acs(hapd->drv_priv, ¶ms); + os_free(channels); + + return ret; +} diff --git a/freebsd/contrib/wpa/src/ap/ap_drv_ops.h b/freebsd/contrib/wpa/src/ap/ap_drv_ops.h new file mode 100644 index 00000000..82eaf3f0 --- /dev/null +++ b/freebsd/contrib/wpa/src/ap/ap_drv_ops.h @@ -0,0 +1,340 @@ +/* + * hostapd - Driver operations + * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef AP_DRV_OPS +#define AP_DRV_OPS + +enum wpa_driver_if_type; +struct wpa_bss_params; +struct wpa_driver_scan_params; +struct ieee80211_ht_capabilities; +struct ieee80211_vht_capabilities; +struct hostapd_freq_params; + +u32 hostapd_sta_flags_to_drv(u32 flags); +int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf **beacon, + struct wpabuf **proberesp, + struct wpabuf **assocresp); +void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon, + struct wpabuf *proberesp, + struct wpabuf *assocresp); +int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd); +int hostapd_set_ap_wps_ie(struct hostapd_data *hapd); +int hostapd_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta); +int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, + int enabled); +int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname); +int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname); +int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds, + const u8 *addr, int aid, int val); +int hostapd_sta_add(struct hostapd_data *hapd, + const u8 *addr, u16 aid, u16 capability, + const u8 *supp_rates, size_t supp_rates_len, + u16 listen_interval, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u32 flags, u8 qosinfo, u8 vht_opmode); +int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); +int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, + size_t elem_len); +int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len); +int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len); +int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, + const char *ifname, const u8 *addr, void *bss_ctx, + void **drv_priv, char *force_ifname, u8 *if_addr, + const char *bridge, int use_existing); +int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type, + const char *ifname); +int hostapd_set_ieee8021x(struct hostapd_data *hapd, + struct wpa_bss_params *params); +int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, + const u8 *addr, int idx, u8 *seq); +int hostapd_flush(struct hostapd_data *hapd); +int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode, + int freq, int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1); +int hostapd_set_rts(struct hostapd_data *hapd, int rts); +int hostapd_set_frag(struct hostapd_data *hapd, int frag); +int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, + int total_flags, int flags_or, int flags_and); +int hostapd_set_country(struct hostapd_data *hapd, const char *country); +int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, + int cw_min, int cw_max, int burst_time); +struct hostapd_hw_modes * +hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, + u16 *flags); +int hostapd_driver_commit(struct hostapd_data *hapd); +int hostapd_drv_none(struct hostapd_data *hapd); +int hostapd_driver_scan(struct hostapd_data *hapd, + struct wpa_driver_scan_params *params); +struct wpa_scan_results * hostapd_driver_get_scan_results( + struct hostapd_data *hapd); +int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration); +int hostapd_drv_set_key(const char *ifname, + struct hostapd_data *hapd, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); +int hostapd_drv_send_mlme(struct hostapd_data *hapd, + const void *msg, size_t len, int noack); +int hostapd_drv_sta_deauth(struct hostapd_data *hapd, + const u8 *addr, int reason); +int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, + const u8 *addr, int reason); +int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *data, + size_t len); +int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, + u16 auth_alg); +int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len); +int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, + int reassoc, u16 status, const u8 *ie, size_t len); +int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, + u8 *tspec_ie, size_t tspec_ielen); +int hostapd_start_dfs_cac(struct hostapd_iface *iface, + enum hostapd_hw_mode mode, int freq, + int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1); +int hostapd_drv_do_acs(struct hostapd_data *hapd); + + +#include "drivers/driver.h" + +int hostapd_drv_wnm_oper(struct hostapd_data *hapd, + enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len); + +int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, + u8 qos_map_set_len); + +static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, + int enabled) +{ + if (hapd->driver == NULL || + hapd->driver->hapd_set_countermeasures == NULL) + return 0; + return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled); +} + +static inline int hostapd_drv_set_sta_vlan(const char *ifname, + struct hostapd_data *hapd, + const u8 *addr, int vlan_id) +{ + if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL) + return 0; + return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, + vlan_id); +} + +static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) + return 0; + return hapd->driver->get_inact_sec(hapd->drv_priv, addr); +} + +static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) + return 0; + return hapd->driver->sta_remove(hapd->drv_priv, addr); +} + +static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd, + const u8 *addr, const u8 *data, + size_t data_len, int encrypt, + u32 flags) +{ + if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL) + return 0; + return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data, + data_len, encrypt, + hapd->own_addr, flags); +} + +static inline int hostapd_drv_read_sta_data( + struct hostapd_data *hapd, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) + return -1; + return hapd->driver->read_sta_data(hapd->drv_priv, data, addr); +} + +static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) + return 0; + return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); +} + +static inline int hostapd_drv_set_acl(struct hostapd_data *hapd, + struct hostapd_acl_params *params) +{ + if (hapd->driver == NULL || hapd->driver->set_acl == NULL) + return 0; + return hapd->driver->set_acl(hapd->drv_priv, params); +} + +static inline int hostapd_drv_set_ap(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params) +{ + if (hapd->driver == NULL || hapd->driver->set_ap == NULL) + return 0; + return hapd->driver->set_ap(hapd->drv_priv, params); +} + +static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd, + const u8 *mac, int accepted, + u32 session_timeout) +{ + if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL) + return 0; + return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted, + session_timeout); +} + +static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd, + const u8 *mac) +{ + if (hapd->driver == NULL || + hapd->driver->set_radius_acl_expire == NULL) + return 0; + return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac); +} + +static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd, + int auth_algs) +{ + if (hapd->driver == NULL || hapd->driver->set_authmode == NULL) + return 0; + return hapd->driver->set_authmode(hapd->drv_priv, auth_algs); +} + +static inline void hostapd_drv_poll_client(struct hostapd_data *hapd, + const u8 *own_addr, const u8 *addr, + int qos) +{ + if (hapd->driver == NULL || hapd->driver->poll_client == NULL) + return; + hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos); +} + +static inline int hostapd_drv_get_survey(struct hostapd_data *hapd, + unsigned int freq) +{ + if (hapd->driver == NULL) + return -1; + if (!hapd->driver->get_survey) + return -1; + return hapd->driver->get_survey(hapd->drv_priv, freq); +} + +static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2) +{ + if (hapd->driver == NULL || hapd->driver->get_country == NULL) + return -1; + return hapd->driver->get_country(hapd->drv_priv, alpha2); +} + +static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->get_radio_name == NULL) + return NULL; + return hapd->driver->get_radio_name(hapd->drv_priv); +} + +static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd, + struct csa_settings *settings) +{ + if (hapd->driver == NULL || hapd->driver->switch_channel == NULL) + return -ENOTSUP; + + return hapd->driver->switch_channel(hapd->drv_priv, settings); +} + +static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf, + size_t buflen) +{ + if (hapd->driver == NULL || hapd->driver->status == NULL) + return -1; + return hapd->driver->status(hapd->drv_priv, buf, buflen); +} + +static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd, + int version, const u8 *ipaddr, + int prefixlen, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->br_add_ip_neigh == NULL) + return -1; + return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr, + prefixlen, addr); +} + +static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd, + u8 version, const u8 *ipaddr) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->br_delete_ip_neigh == NULL) + return -1; + return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version, + ipaddr); +} + +static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd, + enum drv_br_port_attr attr, + unsigned int val) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->br_port_set_attr == NULL) + return -1; + return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val); +} + +static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd, + enum drv_br_net_param param, + unsigned int val) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->br_set_net_param == NULL) + return -1; + return hapd->driver->br_set_net_param(hapd->drv_priv, param, val); +} + +static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd, + int vendor_id, int subcmd, + const u8 *data, size_t data_len, + struct wpabuf *buf) +{ + if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL) + return -1; + return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data, + data_len, buf); +} + +static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->stop_ap == NULL) + return 0; + return hapd->driver->stop_ap(hapd->drv_priv); +} + +#endif /* AP_DRV_OPS */ diff --git a/freebsd/contrib/wpa/src/ap/hostapd.h b/freebsd/contrib/wpa/src/ap/hostapd.h new file mode 100644 index 00000000..dcf51f00 --- /dev/null +++ b/freebsd/contrib/wpa/src/ap/hostapd.h @@ -0,0 +1,494 @@ +/* + * hostapd / Initialization and configuration + * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HOSTAPD_H +#define HOSTAPD_H + +#include "common/defs.h" +#include "utils/list.h" +#include "ap_config.h" +#include "drivers/driver.h" + +struct wpa_ctrl_dst; +struct radius_server_data; +struct upnp_wps_device_sm; +struct hostapd_data; +struct sta_info; +struct ieee80211_ht_capabilities; +struct full_dynamic_vlan; +enum wps_event; +union wps_event_data; +#ifdef CONFIG_MESH +struct mesh_conf; +#endif /* CONFIG_MESH */ + +struct hostapd_iface; + +struct hapd_interfaces { + int (*reload_config)(struct hostapd_iface *iface); + struct hostapd_config * (*config_read_cb)(const char *config_fname); + int (*ctrl_iface_init)(struct hostapd_data *hapd); + void (*ctrl_iface_deinit)(struct hostapd_data *hapd); + int (*for_each_interface)(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx); + int (*driver_init)(struct hostapd_iface *iface); + + size_t count; + int global_ctrl_sock; + struct wpa_ctrl_dst *global_ctrl_dst; + char *global_iface_path; + char *global_iface_name; +#ifndef CONFIG_NATIVE_WINDOWS + gid_t ctrl_iface_group; +#endif /* CONFIG_NATIVE_WINDOWS */ + struct hostapd_iface **iface; + + size_t terminate_on_error; +#ifndef CONFIG_NO_VLAN + struct dynamic_iface *vlan_priv; +#endif /* CONFIG_NO_VLAN */ +}; + +enum hostapd_chan_status { + HOSTAPD_CHAN_VALID = 0, /* channel is ready */ + HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */ + HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */ +}; + +struct hostapd_probereq_cb { + int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, + const u8 *ie, size_t ie_len, int ssi_signal); + void *ctx; +}; + +#define HOSTAPD_RATE_BASIC 0x00000001 + +struct hostapd_rate_data { + int rate; /* rate in 100 kbps */ + int flags; /* HOSTAPD_RATE_ flags */ +}; + +struct hostapd_frame_info { + u32 channel; + u32 datarate; + int ssi_signal; /* dBm */ +}; + +enum wps_status { + WPS_STATUS_SUCCESS = 1, + WPS_STATUS_FAILURE +}; + +enum pbc_status { + WPS_PBC_STATUS_DISABLE, + WPS_PBC_STATUS_ACTIVE, + WPS_PBC_STATUS_TIMEOUT, + WPS_PBC_STATUS_OVERLAP +}; + +struct wps_stat { + enum wps_status status; + enum wps_error_indication failure_reason; + enum pbc_status pbc_status; + u8 peer_addr[ETH_ALEN]; +}; + + +/** + * struct hostapd_data - hostapd per-BSS data structure + */ +struct hostapd_data { + struct hostapd_iface *iface; + struct hostapd_config *iconf; + struct hostapd_bss_config *conf; + int interface_added; /* virtual interface added for this BSS */ + unsigned int started:1; + unsigned int disabled:1; + unsigned int reenable_beacon:1; + + u8 own_addr[ETH_ALEN]; + + int num_sta; /* number of entries in sta_list */ + struct sta_info *sta_list; /* STA info list head */ +#define STA_HASH_SIZE 256 +#define STA_HASH(sta) (sta[5]) + struct sta_info *sta_hash[STA_HASH_SIZE]; + + /* + * Bitfield for indicating which AIDs are allocated. Only AID values + * 1-2007 are used and as such, the bit at index 0 corresponds to AID + * 1. + */ +#define AID_WORDS ((2008 + 31) / 32) + u32 sta_aid[AID_WORDS]; + + const struct wpa_driver_ops *driver; + void *drv_priv; + + void (*new_assoc_sta_cb)(struct hostapd_data *hapd, + struct sta_info *sta, int reassoc); + + void *msg_ctx; /* ctx for wpa_msg() calls */ + void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */ + + struct radius_client_data *radius; + u32 acct_session_id_hi, acct_session_id_lo; + struct radius_das_data *radius_das; + + struct iapp_data *iapp; + + struct hostapd_cached_radius_acl *acl_cache; + struct hostapd_acl_query_data *acl_queries; + + struct wpa_authenticator *wpa_auth; + struct eapol_authenticator *eapol_auth; + + struct rsn_preauth_interface *preauth_iface; + struct os_reltime michael_mic_failure; + int michael_mic_failures; + int tkip_countermeasures; + + int ctrl_sock; + struct wpa_ctrl_dst *ctrl_dst; + + void *ssl_ctx; + void *eap_sim_db_priv; + struct radius_server_data *radius_srv; + struct dl_list erp_keys; /* struct eap_server_erp_key */ + + int parameter_set_count; + + /* Time Advertisement */ + u8 time_update_counter; + struct wpabuf *time_adv; + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + struct full_dynamic_vlan *full_dynamic_vlan; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + struct l2_packet_data *l2; + struct wps_context *wps; + + int beacon_set_done; + struct wpabuf *wps_beacon_ie; + struct wpabuf *wps_probe_resp_ie; +#ifdef CONFIG_WPS + unsigned int ap_pin_failures; + unsigned int ap_pin_failures_consecutive; + struct upnp_wps_device_sm *wps_upnp; + unsigned int ap_pin_lockout_time; + + struct wps_stat wps_stats; +#endif /* CONFIG_WPS */ + + struct hostapd_probereq_cb *probereq_cb; + size_t num_probereq_cb; + + void (*public_action_cb)(void *ctx, const u8 *buf, size_t len, + int freq); + void *public_action_cb_ctx; + void (*public_action_cb2)(void *ctx, const u8 *buf, size_t len, + int freq); + void *public_action_cb2_ctx; + + int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len, + int freq); + void *vendor_action_cb_ctx; + + void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr, + const u8 *uuid_e); + void *wps_reg_success_cb_ctx; + + void (*wps_event_cb)(void *ctx, enum wps_event event, + union wps_event_data *data); + void *wps_event_cb_ctx; + + void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr, + int authorized, const u8 *p2p_dev_addr); + void *sta_authorized_cb_ctx; + + void (*setup_complete_cb)(void *ctx); + void *setup_complete_cb_ctx; + + void (*new_psk_cb)(void *ctx, const u8 *mac_addr, + const u8 *p2p_dev_addr, const u8 *psk, + size_t psk_len); + void *new_psk_cb_ctx; + + /* channel switch parameters */ + struct hostapd_freq_params cs_freq_params; + u8 cs_count; + int cs_block_tx; + unsigned int cs_c_off_beacon; + unsigned int cs_c_off_proberesp; + int csa_in_progress; + + /* BSS Load */ + unsigned int bss_load_update_timeout; + +#ifdef CONFIG_P2P + struct p2p_data *p2p; + struct p2p_group *p2p_group; + struct wpabuf *p2p_beacon_ie; + struct wpabuf *p2p_probe_resp_ie; + + /* Number of non-P2P association stations */ + int num_sta_no_p2p; + + /* Periodic NoA (used only when no non-P2P clients in the group) */ + int noa_enabled; + int noa_start; + int noa_duration; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + size_t gas_frag_limit; +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_PROXYARP + struct l2_packet_data *sock_dhcp; + struct l2_packet_data *sock_ndisc; +#endif /* CONFIG_PROXYARP */ +#ifdef CONFIG_MESH + int num_plinks; + int max_plinks; + void (*mesh_sta_free_cb)(struct sta_info *sta); + struct wpabuf *mesh_pending_auth; + struct os_reltime mesh_pending_auth_time; +#endif /* CONFIG_MESH */ + +#ifdef CONFIG_SQLITE + struct hostapd_eap_user tmp_eap_user; +#endif /* CONFIG_SQLITE */ + +#ifdef CONFIG_SAE + /** Key used for generating SAE anti-clogging tokens */ + u8 sae_token_key[8]; + struct os_reltime last_sae_token_key_update; + int dot11RSNASAERetransPeriod; /* msec */ +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_TESTING_OPTIONS + unsigned int ext_mgmt_frame_handling:1; + unsigned int ext_eapol_frame_io:1; + + struct l2_packet_data *l2_test; +#endif /* CONFIG_TESTING_OPTIONS */ +}; + + +struct hostapd_sta_info { + struct dl_list list; + u8 addr[ETH_ALEN]; + struct os_reltime last_seen; +}; + +/** + * struct hostapd_iface - hostapd per-interface data structure + */ +struct hostapd_iface { + struct hapd_interfaces *interfaces; + void *owner; + char *config_fname; + struct hostapd_config *conf; + char phy[16]; /* Name of the PHY (radio) */ + + enum hostapd_iface_state { + HAPD_IFACE_UNINITIALIZED, + HAPD_IFACE_DISABLED, + HAPD_IFACE_COUNTRY_UPDATE, + HAPD_IFACE_ACS, + HAPD_IFACE_HT_SCAN, + HAPD_IFACE_DFS, + HAPD_IFACE_ENABLED + } state; + +#ifdef CONFIG_MESH + struct mesh_conf *mconf; +#endif /* CONFIG_MESH */ + + size_t num_bss; + struct hostapd_data **bss; + + unsigned int wait_channel_update:1; + unsigned int cac_started:1; +#ifdef CONFIG_FST + struct fst_iface *fst; + const struct wpabuf *fst_ies; +#endif /* CONFIG_FST */ + + /* + * When set, indicates that the driver will handle the AP + * teardown: delete global keys, station keys, and stations. + */ + unsigned int driver_ap_teardown:1; + + int num_ap; /* number of entries in ap_list */ + struct ap_info *ap_list; /* AP info list head */ + struct ap_info *ap_hash[STA_HASH_SIZE]; + + u64 drv_flags; + + /* SMPS modes supported by the driver (WPA_DRIVER_SMPS_MODE_*) */ + unsigned int smps_modes; + + /* + * A bitmap of supported protocols for probe response offload. See + * struct wpa_driver_capa in driver.h + */ + unsigned int probe_resp_offloads; + + /* extended capabilities supported by the driver */ + const u8 *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; + + unsigned int drv_max_acl_mac_addrs; + + struct hostapd_hw_modes *hw_features; + int num_hw_features; + struct hostapd_hw_modes *current_mode; + /* Rates that are currently used (i.e., filtered copy of + * current_mode->channels */ + int num_rates; + struct hostapd_rate_data *current_rates; + int *basic_rates; + int freq; + + u16 hw_flags; + + /* Number of associated Non-ERP stations (i.e., stations using 802.11b + * in 802.11g BSS) */ + int num_sta_non_erp; + + /* Number of associated stations that do not support Short Slot Time */ + int num_sta_no_short_slot_time; + + /* Number of associated stations that do not support Short Preamble */ + int num_sta_no_short_preamble; + + int olbc; /* Overlapping Legacy BSS Condition */ + + /* Number of HT associated stations that do not support greenfield */ + int num_sta_ht_no_gf; + + /* Number of associated non-HT stations */ + int num_sta_no_ht; + + /* Number of HT associated stations 20 MHz */ + int num_sta_ht_20mhz; + + /* Number of HT40 intolerant stations */ + int num_sta_ht40_intolerant; + + /* Overlapping BSS information */ + int olbc_ht; + + u16 ht_op_mode; + + /* surveying helpers */ + + /* number of channels surveyed */ + unsigned int chans_surveyed; + + /* lowest observed noise floor in dBm */ + s8 lowest_nf; + + /* channel utilization calculation */ + u64 last_channel_time; + u64 last_channel_time_busy; + u8 channel_utilization; + + unsigned int dfs_cac_ms; + struct os_reltime dfs_cac_start; + + /* Latched with the actual secondary channel information and will be + * used while juggling between HT20 and HT40 modes. */ + int secondary_ch; + +#ifdef CONFIG_ACS + unsigned int acs_num_completed_scans; +#endif /* CONFIG_ACS */ + + void (*scan_cb)(struct hostapd_iface *iface); + int num_ht40_scan_tries; + + struct dl_list sta_seen; /* struct hostapd_sta_info */ + unsigned int num_sta_seen; +}; + +/* hostapd.c */ +int hostapd_for_each_interface(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx); +int hostapd_reload_config(struct hostapd_iface *iface); +struct hostapd_data * +hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, + struct hostapd_config *conf, + struct hostapd_bss_config *bss); +int hostapd_setup_interface(struct hostapd_iface *iface); +int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err); +void hostapd_interface_deinit(struct hostapd_iface *iface); +void hostapd_interface_free(struct hostapd_iface *iface); +struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, + const char *config_file); +struct hostapd_iface * +hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy, + const char *config_fname, int debug); +void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, + int reassoc); +void hostapd_interface_deinit_free(struct hostapd_iface *iface); +int hostapd_enable_iface(struct hostapd_iface *hapd_iface); +int hostapd_reload_iface(struct hostapd_iface *hapd_iface); +int hostapd_disable_iface(struct hostapd_iface *hapd_iface); +int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf); +int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf); +void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator); +void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s); +const char * hostapd_state_text(enum hostapd_iface_state s); +int hostapd_switch_channel(struct hostapd_data *hapd, + struct csa_settings *settings); +void +hostapd_switch_channel_fallback(struct hostapd_iface *iface, + const struct hostapd_freq_params *freq_params); +void hostapd_cleanup_cs_params(struct hostapd_data *hapd); +void hostapd_periodic_iface(struct hostapd_iface *iface); + +/* utils.c */ +int hostapd_register_probereq_cb(struct hostapd_data *hapd, + int (*cb)(void *ctx, const u8 *sa, + const u8 *da, const u8 *bssid, + const u8 *ie, size_t ie_len, + int ssi_signal), + void *ctx); +void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr); + +/* drv_callbacks.c (TODO: move to somewhere else?) */ +int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, + const u8 *ie, size_t ielen, int reassoc); +void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); +void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr); +void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, + const u8 *addr, int reason_code); +int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, + const u8 *bssid, const u8 *ie, size_t ie_len, + int ssi_signal); +void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, + int offset, int width, int cf1, int cf2); + +const struct hostapd_eap_user * +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2); + +struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, + const char *ifname); + +#ifdef CONFIG_FST +void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, + struct fst_wpa_obj *iface_obj); +#endif /* CONFIG_FST */ + +#endif /* HOSTAPD_H */ diff --git a/freebsd/contrib/wpa/src/ap/hs20.c b/freebsd/contrib/wpa/src/ap/hs20.c new file mode 100644 index 00000000..1a836666 --- /dev/null +++ b/freebsd/contrib/wpa/src/ap/hs20.c @@ -0,0 +1,179 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Hotspot 2.0 AP ANQP processing + * Copyright (c) 2009, Atheros Communications, Inc. + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "hs20.h" + + +u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid) +{ + u8 conf; + if (!hapd->conf->hs20) + return eid; + *eid++ = WLAN_EID_VENDOR_SPECIFIC; + *eid++ = 7; + WPA_PUT_BE24(eid, OUI_WFA); + eid += 3; + *eid++ = HS20_INDICATION_OUI_TYPE; + conf = HS20_VERSION; /* Release Number */ + conf |= HS20_ANQP_DOMAIN_ID_PRESENT; + if (hapd->conf->disable_dgaf) + conf |= HS20_DGAF_DISABLED; + *eid++ = conf; + WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id); + eid += 2; + + return eid; +} + + +u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid) +{ + u8 *len; + u16 capab; + + if (!hapd->conf->osen) + return eid; + + *eid++ = WLAN_EID_VENDOR_SPECIFIC; + len = eid++; /* to be filled */ + WPA_PUT_BE24(eid, OUI_WFA); + eid += 3; + *eid++ = HS20_OSEN_OUI_TYPE; + + /* Group Data Cipher Suite */ + RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + eid += RSN_SELECTOR_LEN; + + /* Pairwise Cipher Suite Count and List */ + WPA_PUT_LE16(eid, 1); + eid += 2; + RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP); + eid += RSN_SELECTOR_LEN; + + /* AKM Suite Count and List */ + WPA_PUT_LE16(eid, 1); + eid += 2; + RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN); + eid += RSN_SELECTOR_LEN; + + /* RSN Capabilities */ + capab = 0; + if (hapd->conf->wmm_enabled) { + /* 4 PTKSA replay counters when using WMM */ + capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); + } +#ifdef CONFIG_IEEE80211W + if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + capab |= WPA_CAPABILITY_MFPC; + if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) + capab |= WPA_CAPABILITY_MFPR; + } +#endif /* CONFIG_IEEE80211W */ + WPA_PUT_LE16(eid, capab); + eid += 2; + + *len = eid - len - 1; + + return eid; +} + + +int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr, + u8 osu_method, const char *url) +{ + struct wpabuf *buf; + size_t len = 0; + int ret; + + /* TODO: should refuse to send notification if the STA is not associated + * or if the STA did not indicate support for WNM-Notification */ + + if (url) { + len = 1 + os_strlen(url); + if (5 + len > 255) { + wpa_printf(MSG_INFO, "HS 2.0: Too long URL for " + "WNM-Notification: '%s'", url); + return -1; + } + } + + buf = wpabuf_alloc(4 + 7 + len); + if (buf == NULL) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); + wpabuf_put_u8(buf, 1); /* Dialog token */ + wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */ + + /* Subscription Remediation subelement */ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 5 + len); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED); + if (url) { + wpabuf_put_u8(buf, len - 1); + wpabuf_put_data(buf, url, len - 1); + wpabuf_put_u8(buf, osu_method); + } else { + /* Server URL and Server Method fields not included */ + wpabuf_put_u8(buf, 0); + } + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + + wpabuf_free(buf); + + return ret; +} + + +int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd, + const u8 *addr, + const struct wpabuf *payload) +{ + struct wpabuf *buf; + int ret; + + /* TODO: should refuse to send notification if the STA is not associated + * or if the STA did not indicate support for WNM-Notification */ + + buf = wpabuf_alloc(4 + 6 + wpabuf_len(payload)); + if (buf == NULL) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); + wpabuf_put_u8(buf, 1); /* Dialog token */ + wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */ + + /* Deauthentication Imminent Notice subelement */ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 4 + wpabuf_len(payload)); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_WNM_DEAUTH_IMMINENT_NOTICE); + wpabuf_put_buf(buf, payload); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + + wpabuf_free(buf); + + return ret; +} diff --git a/freebsd/contrib/wpa/src/ap/hs20.h b/freebsd/contrib/wpa/src/ap/hs20.h new file mode 100644 index 00000000..152439f4 --- /dev/null +++ b/freebsd/contrib/wpa/src/ap/hs20.h @@ -0,0 +1,22 @@ +/* + * Hotspot 2.0 AP ANQP processing + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HS20_H +#define HS20_H + +struct hostapd_data; + +u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid); +int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr, + u8 osu_method, const char *url); +int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd, + const u8 *addr, + const struct wpabuf *payload); + +#endif /* HS20_H */ diff --git a/freebsd/contrib/wpa/src/ap/ieee802_11.h b/freebsd/contrib/wpa/src/ap/ieee802_11.h new file mode 100644 index 00000000..44c1bff3 --- /dev/null +++ b/freebsd/contrib/wpa/src/ap/ieee802_11.h @@ -0,0 +1,107 @@ +/* + * hostapd / IEEE 802.11 Management + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_11_H +#define IEEE802_11_H + +struct hostapd_iface; +struct hostapd_data; +struct sta_info; +struct hostapd_frame_info; +struct ieee80211_ht_capabilities; +struct ieee80211_vht_capabilities; +struct ieee80211_mgmt; + +int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, + struct hostapd_frame_info *fi); +void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, + u16 stype, int ok); +void hostapd_2040_coex_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t 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); +void ap_ht2040_timeout(void *eloop_data, void *user_data); +u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid); +int hostapd_ht_operation_update(struct hostapd_iface *iface); +void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *trans_id); +void hostapd_get_ht_capab(struct hostapd_data *hapd, + struct ieee80211_ht_capabilities *ht_cap, + struct ieee80211_ht_capabilities *neg_ht_cap); +void hostapd_get_vht_capab(struct hostapd_data *hapd, + struct ieee80211_vht_capabilities *vht_cap, + struct ieee80211_vht_capabilities *neg_vht_cap); +u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ht_capab); +u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ie, size_t len); + +void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); +void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta); +void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta); +u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_capab); +u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_opmode); +void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, + const u8 *buf, size_t len, int ack); +void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t len, int ack); +void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, + int wds); +u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eid); +void ieee802_11_sa_query_action(struct hostapd_data *hapd, + const u8 *sa, const u8 action_type, + const u8 *trans_id); +u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid); +int hostapd_update_time_adv(struct hostapd_data *hapd); +void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr); +u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid); + +int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta); +#ifdef CONFIG_SAE +void sae_clear_retransmit_timer(struct hostapd_data *hapd, + struct sta_info *sta); +#else /* CONFIG_SAE */ +static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} +#endif /* CONFIG_SAE */ + +#endif /* IEEE802_11_H */ diff --git a/freebsd/contrib/wpa/src/ap/ieee802_11_auth.h b/freebsd/contrib/wpa/src/ap/ieee802_11_auth.h new file mode 100644 index 00000000..b66f244b --- /dev/null +++ b/freebsd/contrib/wpa/src/ap/ieee802_11_auth.h @@ -0,0 +1,29 @@ +/* + * hostapd / IEEE 802.11 authentication (ACL) + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_11_AUTH_H +#define IEEE802_11_AUTH_H + +enum { + HOSTAPD_ACL_REJECT = 0, + HOSTAPD_ACL_ACCEPT = 1, + HOSTAPD_ACL_PENDING = 2, + HOSTAPD_ACL_ACCEPT_TIMEOUT = 3 +}; + +int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, + u32 *acct_interim_interval, int *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui); +int hostapd_acl_init(struct hostapd_data *hapd); +void hostapd_acl_deinit(struct hostapd_data *hapd); +void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk); +void hostapd_acl_expire(struct hostapd_data *hapd); + +#endif /* IEEE802_11_AUTH_H */ diff --git a/freebsd/contrib/wpa/src/ap/ieee802_11_shared.c b/freebsd/contrib/wpa/src/ap/ieee802_11_shared.c new file mode 100644 index 00000000..ec6deaf5 --- /dev/null +++ b/freebsd/contrib/wpa/src/ap/ieee802_11_shared.c @@ -0,0 +1,510 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * hostapd / IEEE 802.11 Management + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "ieee802_11.h" + + +#ifdef CONFIG_IEEE80211W + +u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eid) +{ + u8 *pos = eid; + u32 timeout, tu; + struct os_reltime now, passed; + + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; + os_get_reltime(&now); + os_reltime_sub(&now, &sta->sa_query_start, &passed); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (hapd->conf->assoc_sa_query_max_timeout > tu) + timeout = hapd->conf->assoc_sa_query_max_timeout - tu; + else + timeout = 0; + if (timeout < hapd->conf->assoc_sa_query_max_timeout) + timeout++; /* add some extra time for local timers */ + WPA_PUT_LE32(pos, timeout); + pos += 4; + + return pos; +} + + +/* MLME-SAQuery.request */ +void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *trans_id) +{ + struct ieee80211_mgmt mgmt; + u8 *end; + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " + MACSTR, MAC2STR(addr)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt.da, addr, ETH_ALEN); + os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); + mgmt.u.action.category = WLAN_ACTION_SA_QUERY; + mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; + os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; + if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) + wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed"); +} + + +static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, + const u8 *sa, const u8 *trans_id) +{ + struct sta_info *sta; + struct ieee80211_mgmt resp; + u8 *end; + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " + MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + sta = ap_get_sta(hapd, sa); + if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request " + "from unassociated STA " MACSTR, MAC2STR(sa)); + return; + } + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " + MACSTR, MAC2STR(sa)); + + os_memset(&resp, 0, sizeof(resp)); + resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(resp.da, sa, ETH_ALEN); + os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN); + resp.u.action.category = WLAN_ACTION_SA_QUERY; + resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; + os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; + if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0) + wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed"); +} + + +void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, + const u8 action_type, const u8 *trans_id) +{ + struct sta_info *sta; + int i; + + if (action_type == WLAN_SA_QUERY_REQUEST) { + ieee802_11_send_sa_query_resp(hapd, sa, trans_id); + return; + } + + if (action_type != WLAN_SA_QUERY_RESPONSE) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " + "Action %d", action_type); + return; + } + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from " + MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + /* MLME-SAQuery.confirm */ + + sta = ap_get_sta(hapd, sa); + if (sta == NULL || sta->sa_query_trans_id == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " + "pending SA Query request found"); + return; + } + + for (i = 0; i < sta->sa_query_count; i++) { + if (os_memcmp(sta->sa_query_trans_id + + i * WLAN_SA_QUERY_TR_ID_LEN, + trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0) + break; + } + + if (i >= sta->sa_query_count) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " + "transaction identifier found"); + return; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Reply to pending SA Query received"); + ap_sta_stop_sa_query(hapd, sta); +} + +#endif /* CONFIG_IEEE80211W */ + + +static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) +{ + *pos = 0x00; + + switch (idx) { + case 0: /* Bits 0-7 */ + if (hapd->iconf->obss_interval) + *pos |= 0x01; /* Bit 0 - Coexistence management */ + break; + case 1: /* Bits 8-15 */ + if (hapd->conf->proxy_arp) + *pos |= 0x10; /* Bit 12 - Proxy ARP */ + break; + case 2: /* Bits 16-23 */ + if (hapd->conf->wnm_sleep_mode) + *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ + if (hapd->conf->bss_transition) + *pos |= 0x08; /* Bit 19 - BSS Transition */ + break; + case 3: /* Bits 24-31 */ +#ifdef CONFIG_WNM + *pos |= 0x02; /* Bit 25 - SSID List */ +#endif /* CONFIG_WNM */ + if (hapd->conf->time_advertisement == 2) + *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ + if (hapd->conf->interworking) + *pos |= 0x80; /* Bit 31 - Interworking */ + break; + case 4: /* Bits 32-39 */ + if (hapd->conf->qos_map_set_len) + *pos |= 0x01; /* Bit 32 - QoS Map */ + if (hapd->conf->tdls & TDLS_PROHIBIT) + *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ + if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) { + /* Bit 39 - TDLS Channel Switching Prohibited */ + *pos |= 0x80; + } + break; + case 5: /* Bits 40-47 */ +#ifdef CONFIG_HS20 + if (hapd->conf->hs20) + *pos |= 0x40; /* Bit 46 - WNM-Notification */ +#endif /* CONFIG_HS20 */ + break; + case 6: /* Bits 48-55 */ + if (hapd->conf->ssid.utf8_ssid) + *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ + break; + } +} + + +u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + u8 len = 0, i; + + if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)) + len = 5; + if (len < 4 && hapd->conf->interworking) + len = 4; + if (len < 3 && hapd->conf->wnm_sleep_mode) + len = 3; + if (len < 1 && hapd->iconf->obss_interval) + len = 1; + if (len < 7 && hapd->conf->ssid.utf8_ssid) + len = 7; +#ifdef CONFIG_WNM + if (len < 4) + len = 4; +#endif /* CONFIG_WNM */ +#ifdef CONFIG_HS20 + if (hapd->conf->hs20 && len < 6) + len = 6; +#endif /* CONFIG_HS20 */ + if (len < hapd->iface->extended_capa_len) + len = hapd->iface->extended_capa_len; + if (len == 0) + return eid; + + *pos++ = WLAN_EID_EXT_CAPAB; + *pos++ = len; + for (i = 0; i < len; i++, pos++) { + hostapd_ext_capab_byte(hapd, pos, i); + + if (i < hapd->iface->extended_capa_len) { + *pos &= ~hapd->iface->extended_capa_mask[i]; + *pos |= hapd->iface->extended_capa[i]; + } + } + + while (len > 0 && eid[1 + len] == 0) { + len--; + eid[1] = len; + } + if (len == 0) + return eid; + + return eid + 2 + len; +} + + +u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + u8 len = hapd->conf->qos_map_set_len; + + if (!len) + return eid; + + *pos++ = WLAN_EID_QOS_MAP_SET; + *pos++ = len; + os_memcpy(pos, hapd->conf->qos_map_set, len); + pos += len; + + return pos; +} + + +u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; +#ifdef CONFIG_INTERWORKING + u8 *len; + + if (!hapd->conf->interworking) + return eid; + + *pos++ = WLAN_EID_INTERWORKING; + len = pos++; + + *pos = hapd->conf->access_network_type; + if (hapd->conf->internet) + *pos |= INTERWORKING_ANO_INTERNET; + if (hapd->conf->asra) + *pos |= INTERWORKING_ANO_ASRA; + if (hapd->conf->esr) + *pos |= INTERWORKING_ANO_ESR; + if (hapd->conf->uesa) + *pos |= INTERWORKING_ANO_UESA; + pos++; + + if (hapd->conf->venue_info_set) { + *pos++ = hapd->conf->venue_group; + *pos++ = hapd->conf->venue_type; + } + + if (!is_zero_ether_addr(hapd->conf->hessid)) { + os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); + pos += ETH_ALEN; + } + + *len = pos - len - 1; +#endif /* CONFIG_INTERWORKING */ + + return pos; +} + + +u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; +#ifdef CONFIG_INTERWORKING + + /* TODO: Separate configuration for ANQP? */ + if (!hapd->conf->interworking) + return eid; + + *pos++ = WLAN_EID_ADV_PROTO; + *pos++ = 2; + *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */ + *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL; +#endif /* CONFIG_INTERWORKING */ + + return pos; +} + + +u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; +#ifdef CONFIG_INTERWORKING + u8 *len; + unsigned int i, count; + + if (!hapd->conf->interworking || + hapd->conf->roaming_consortium == NULL || + hapd->conf->roaming_consortium_count == 0) + return eid; + + *pos++ = WLAN_EID_ROAMING_CONSORTIUM; + len = pos++; + + /* Number of ANQP OIs (in addition to the max 3 listed here) */ + if (hapd->conf->roaming_consortium_count > 3 + 255) + *pos++ = 255; + else if (hapd->conf->roaming_consortium_count > 3) + *pos++ = hapd->conf->roaming_consortium_count - 3; + else + *pos++ = 0; + + /* OU #1 and #2 Lengths */ + *pos = hapd->conf->roaming_consortium[0].len; + if (hapd->conf->roaming_consortium_count > 1) + *pos |= hapd->conf->roaming_consortium[1].len << 4; + pos++; + + if (hapd->conf->roaming_consortium_count > 3) + count = 3; + else + count = hapd->conf->roaming_consortium_count; + + for (i = 0; i < count; i++) { + os_memcpy(pos, hapd->conf->roaming_consortium[i].oi, + hapd->conf->roaming_consortium[i].len); + pos += hapd->conf->roaming_consortium[i].len; + } + + *len = pos - len - 1; +#endif /* CONFIG_INTERWORKING */ + + return pos; +} + + +u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid) +{ + if (hapd->conf->time_advertisement != 2) + return eid; + + if (hapd->time_adv == NULL && + hostapd_update_time_adv(hapd) < 0) + return eid; + + if (hapd->time_adv == NULL) + return eid; + + os_memcpy(eid, wpabuf_head(hapd->time_adv), + wpabuf_len(hapd->time_adv)); + eid += wpabuf_len(hapd->time_adv); + + return eid; +} + + +u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid) +{ + size_t len; + + if (hapd->conf->time_advertisement != 2) + return eid; + + len = os_strlen(hapd->conf->time_zone); + + *eid++ = WLAN_EID_TIME_ZONE; + *eid++ = len; + os_memcpy(eid, hapd->conf->time_zone, len); + eid += len; + + return eid; +} + + +int hostapd_update_time_adv(struct hostapd_data *hapd) +{ + const int elen = 2 + 1 + 10 + 5 + 1; + struct os_time t; + struct os_tm tm; + u8 *pos; + + if (hapd->conf->time_advertisement != 2) + return 0; + + if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0) + return -1; + + if (!hapd->time_adv) { + hapd->time_adv = wpabuf_alloc(elen); + if (hapd->time_adv == NULL) + return -1; + pos = wpabuf_put(hapd->time_adv, elen); + } else + pos = wpabuf_mhead_u8(hapd->time_adv); + + *pos++ = WLAN_EID_TIME_ADVERTISEMENT; + *pos++ = 1 + 10 + 5 + 1; + + *pos++ = 2; /* UTC time at which the TSF timer is 0 */ + + /* Time Value at TSF 0 */ + /* FIX: need to calculate this based on the current TSF value */ + WPA_PUT_LE16(pos, tm.year); /* Year */ + pos += 2; + *pos++ = tm.month; /* Month */ + *pos++ = tm.day; /* Day of month */ + *pos++ = tm.hour; /* Hours */ + *pos++ = tm.min; /* Minutes */ + *pos++ = tm.sec; /* Seconds */ + WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */ + pos += 2; + *pos++ = 0; /* Reserved */ + + /* Time Error */ + /* TODO: fill in an estimate on the error */ + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + + *pos++ = hapd->time_update_counter++; + + return 0; +} + + +u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + +#ifdef CONFIG_WNM + if (hapd->conf->ap_max_inactivity > 0) { + unsigned int val; + *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; + *pos++ = 3; + val = hapd->conf->ap_max_inactivity; + if (val > 68000) + val = 68000; + val *= 1000; + val /= 1024; + if (val == 0) + val = 1; + if (val > 65535) + val = 65535; + WPA_PUT_LE16(pos, val); + pos += 2; + *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ + } +#endif /* CONFIG_WNM */ + + return pos; +} diff --git a/freebsd/contrib/wpa/src/ap/p2p_hostapd.h b/freebsd/contrib/wpa/src/ap/p2p_hostapd.h new file mode 100644 index 00000000..0e3921c6 --- /dev/null +++ b/freebsd/contrib/wpa/src/ap/p2p_hostapd.h @@ -0,0 +1,35 @@ +/* + * hostapd / P2P integration + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef P2P_HOSTAPD_H +#define P2P_HOSTAPD_H + +#ifdef CONFIG_P2P + +int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); +int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration); +void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd); +void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd); + + +#else /* CONFIG_P2P */ + +static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + return 0; +} + +#endif /* CONFIG_P2P */ + +u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid); + +#endif /* P2P_HOSTAPD_H */ diff --git a/freebsd/contrib/wpa/src/ap/sta_info.h b/freebsd/contrib/wpa/src/ap/sta_info.h new file mode 100644 index 00000000..420d64e5 --- /dev/null +++ b/freebsd/contrib/wpa/src/ap/sta_info.h @@ -0,0 +1,241 @@ +/* + * hostapd / Station table + * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef STA_INFO_H +#define STA_INFO_H + +#ifdef CONFIG_MESH +/* needed for mesh_plink_state enum */ +#include "common/defs.h" +#endif /* CONFIG_MESH */ + +#include "list.h" + +/* STA flags */ +#define WLAN_STA_AUTH BIT(0) +#define WLAN_STA_ASSOC BIT(1) +#define WLAN_STA_AUTHORIZED BIT(5) +#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ +#define WLAN_STA_SHORT_PREAMBLE BIT(7) +#define WLAN_STA_PREAUTH BIT(8) +#define WLAN_STA_WMM BIT(9) +#define WLAN_STA_MFP BIT(10) +#define WLAN_STA_HT BIT(11) +#define WLAN_STA_WPS BIT(12) +#define WLAN_STA_MAYBE_WPS BIT(13) +#define WLAN_STA_WDS BIT(14) +#define WLAN_STA_ASSOC_REQ_OK BIT(15) +#define WLAN_STA_WPS2 BIT(16) +#define WLAN_STA_GAS BIT(17) +#define WLAN_STA_VHT BIT(18) +#define WLAN_STA_WNM_SLEEP_MODE BIT(19) +#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20) +#define WLAN_STA_VENDOR_VHT BIT(21) +#define WLAN_STA_PENDING_DISASSOC_CB BIT(29) +#define WLAN_STA_PENDING_DEAUTH_CB BIT(30) +#define WLAN_STA_NONERP BIT(31) + +/* Maximum number of supported rates (from both Supported Rates and Extended + * Supported Rates IEs). */ +#define WLAN_SUPP_RATES_MAX 32 + + +struct sta_info { + struct sta_info *next; /* next entry in sta list */ + struct sta_info *hnext; /* next entry in hash table list */ + u8 addr[6]; + be32 ipaddr; + struct dl_list ip6addr; /* list head for struct ip6addr */ + u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ + u32 flags; /* Bitfield of WLAN_STA_* */ + u16 capability; + u16 listen_interval; /* or beacon_int for APs */ + u8 supported_rates[WLAN_SUPP_RATES_MAX]; + int supported_rates_len; + u8 qosinfo; /* Valid when WLAN_STA_WMM is set */ + +#ifdef CONFIG_MESH + enum mesh_plink_state plink_state; + u16 peer_lid; + u16 my_lid; + u16 mpm_close_reason; + int mpm_retries; + u8 my_nonce[32]; + u8 peer_nonce[32]; + u8 aek[32]; /* SHA256 digest length */ + u8 mtk[16]; + u8 mgtk[16]; + u8 sae_auth_retry; +#endif /* CONFIG_MESH */ + + 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 ht40_intolerant_set:1; + unsigned int ht_20mhz_set:1; + unsigned int no_p2p_set:1; + unsigned int qos_map_enabled:1; + unsigned int remediation:1; + unsigned int hs20_deauth_requested:1; + unsigned int session_timeout_set:1; + unsigned int radius_das_match:1; + + u16 auth_alg; + + enum { + STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE, + STA_DISASSOC_FROM_CLI + } timeout_next; + + u16 deauth_reason; + u16 disassoc_reason; + + /* IEEE 802.1X related data */ + struct eapol_state_machine *eapol_sm; + + u32 acct_session_id_hi; + u32 acct_session_id_lo; + struct os_reltime acct_session_start; + int acct_session_started; + int acct_terminate_cause; /* Acct-Terminate-Cause */ + int acct_interim_interval; /* Acct-Interim-Interval */ + + unsigned long last_rx_bytes; + unsigned long last_tx_bytes; + u32 acct_input_gigawords; /* Acct-Input-Gigawords */ + u32 acct_output_gigawords; /* Acct-Output-Gigawords */ + + u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ + + struct wpa_state_machine *wpa_sm; + struct rsn_preauth_interface *preauth_iface; + + int vlan_id; /* 0: none, >0: VID */ + int vlan_id_bound; /* updated by ap_sta_bind_vlan() */ + /* PSKs from RADIUS authentication server */ + struct hostapd_sta_wpa_psk_short *psk; + + char *identity; /* User-Name from RADIUS */ + char *radius_cui; /* Chargeable-User-Identity from RADIUS */ + + struct ieee80211_ht_capabilities *ht_capabilities; + struct ieee80211_vht_capabilities *vht_capabilities; + u8 vht_opmode; + +#ifdef CONFIG_IEEE80211W + int sa_query_count; /* number of pending SA Query requests; + * 0 = no SA Query in progress */ + int sa_query_timed_out; + u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * + * sa_query_count octets of pending SA Query + * transaction identifiers */ + struct os_reltime sa_query_start; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_INTERWORKING +#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */ + struct gas_dialog_info *gas_dialog; + u8 gas_dialog_next; +#endif /* CONFIG_INTERWORKING */ + + struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ + struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */ + struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */ + u8 remediation_method; + char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */ + struct wpabuf *hs20_deauth_req; + char *hs20_session_info_url; + int hs20_disassoc_timer; +#ifdef CONFIG_FST + struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */ +#endif /* CONFIG_FST */ + + struct os_reltime connected_time; + +#ifdef CONFIG_SAE + struct sae_data *sae; +#endif /* CONFIG_SAE */ + + u32 session_timeout; /* valid only if session_timeout_set == 1 */ + + /* Last Authentication/(Re)Association Request/Action frame sequence + * control */ + u16 last_seq_ctrl; + /* Last Authentication/(Re)Association Request/Action frame subtype */ + u8 last_subtype; +}; + + +/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has + * passed since last received frame from the station, a nullfunc data frame is + * sent to the station. If this frame is not acknowledged and no other frames + * have been received, the station will be disassociated after + * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated + * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ +#define AP_MAX_INACTIVITY (5 * 60) +#define AP_DISASSOC_DELAY (1) +#define AP_DEAUTH_DELAY (1) +/* Number of seconds to keep STA entry with Authenticated flag after it has + * been disassociated. */ +#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30) +/* Number of seconds to keep STA entry after it has been deauthenticated. */ +#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5) + + +struct hostapd_data; + +int ap_for_each_sta(struct hostapd_data *hapd, + int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, + void *ctx), + void *ctx); +struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); +struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr); +void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta); +void hostapd_free_stas(struct hostapd_data *hapd); +void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); +void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout); +void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout); +void ap_sta_no_session_timeout(struct hostapd_data *hapd, + struct sta_info *sta); +void ap_sta_session_warning_timeout(struct hostapd_data *hapd, + struct sta_info *sta, int warning_time); +struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr); +void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason); +void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason); +#ifdef CONFIG_WPS +int ap_sta_wps_cancel(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx); +#endif /* CONFIG_WPS */ +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); +int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *addr, u16 reason); + +void ap_sta_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +static inline int ap_sta_is_authorized(struct sta_info *sta) +{ + return sta->flags & WLAN_STA_AUTHORIZED; +} + +void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta); + +int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen); + +#endif /* STA_INFO_H */ diff --git a/freebsd/contrib/wpa/src/common/defs.h b/freebsd/contrib/wpa/src/common/defs.h new file mode 100644 index 00000000..6aea3751 --- /dev/null +++ b/freebsd/contrib/wpa/src/common/defs.h @@ -0,0 +1,337 @@ +/* + * WPA Supplicant - Common definitions + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DEFS_H +#define DEFS_H + +#ifdef FALSE +#undef FALSE +#endif +#ifdef TRUE +#undef TRUE +#endif +typedef enum { FALSE = 0, TRUE = 1 } Boolean; + + +#define WPA_CIPHER_NONE BIT(0) +#define WPA_CIPHER_WEP40 BIT(1) +#define WPA_CIPHER_WEP104 BIT(2) +#define WPA_CIPHER_TKIP BIT(3) +#define WPA_CIPHER_CCMP BIT(4) +#define WPA_CIPHER_AES_128_CMAC BIT(5) +#define WPA_CIPHER_GCMP BIT(6) +#define WPA_CIPHER_SMS4 BIT(7) +#define WPA_CIPHER_GCMP_256 BIT(8) +#define WPA_CIPHER_CCMP_256 BIT(9) +#define WPA_CIPHER_BIP_GMAC_128 BIT(11) +#define WPA_CIPHER_BIP_GMAC_256 BIT(12) +#define WPA_CIPHER_BIP_CMAC_256 BIT(13) +#define WPA_CIPHER_GTK_NOT_USED BIT(14) + +#define WPA_KEY_MGMT_IEEE8021X BIT(0) +#define WPA_KEY_MGMT_PSK BIT(1) +#define WPA_KEY_MGMT_NONE BIT(2) +#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3) +#define WPA_KEY_MGMT_WPA_NONE BIT(4) +#define WPA_KEY_MGMT_FT_IEEE8021X BIT(5) +#define WPA_KEY_MGMT_FT_PSK BIT(6) +#define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7) +#define WPA_KEY_MGMT_PSK_SHA256 BIT(8) +#define WPA_KEY_MGMT_WPS BIT(9) +#define WPA_KEY_MGMT_SAE BIT(10) +#define WPA_KEY_MGMT_FT_SAE BIT(11) +#define WPA_KEY_MGMT_WAPI_PSK BIT(12) +#define WPA_KEY_MGMT_WAPI_CERT BIT(13) +#define WPA_KEY_MGMT_CCKM BIT(14) +#define WPA_KEY_MGMT_OSEN BIT(15) +#define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16) +#define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17) + +static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_CCKM | + WPA_KEY_MGMT_OSEN | + WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SUITE_B | + WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)); +} + +static inline int wpa_key_mgmt_wpa_psk(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_FT_SAE)); +} + +static inline int wpa_key_mgmt_ft(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_FT_SAE)); +} + +static inline int wpa_key_mgmt_sae(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_FT_SAE)); +} + +static inline int wpa_key_mgmt_sha256(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_OSEN | + WPA_KEY_MGMT_IEEE8021X_SUITE_B)); +} + +static inline int wpa_key_mgmt_sha384(int akm) +{ + return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192); +} + +static inline int wpa_key_mgmt_suite_b(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B | + WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)); +} + +static inline int wpa_key_mgmt_wpa(int akm) +{ + return wpa_key_mgmt_wpa_ieee8021x(akm) || + wpa_key_mgmt_wpa_psk(akm) || + wpa_key_mgmt_sae(akm); +} + +static inline int wpa_key_mgmt_wpa_any(int akm) +{ + return wpa_key_mgmt_wpa(akm) || (akm & WPA_KEY_MGMT_WPA_NONE); +} + +static inline int wpa_key_mgmt_cckm(int akm) +{ + return akm == WPA_KEY_MGMT_CCKM; +} + + +#define WPA_PROTO_WPA BIT(0) +#define WPA_PROTO_RSN BIT(1) +#define WPA_PROTO_WAPI BIT(2) +#define WPA_PROTO_OSEN BIT(3) + +#define WPA_AUTH_ALG_OPEN BIT(0) +#define WPA_AUTH_ALG_SHARED BIT(1) +#define WPA_AUTH_ALG_LEAP BIT(2) +#define WPA_AUTH_ALG_FT BIT(3) +#define WPA_AUTH_ALG_SAE BIT(4) + + +enum wpa_alg { + WPA_ALG_NONE, + WPA_ALG_WEP, + WPA_ALG_TKIP, + WPA_ALG_CCMP, + WPA_ALG_IGTK, + WPA_ALG_PMK, + WPA_ALG_GCMP, + WPA_ALG_SMS4, + WPA_ALG_KRK, + WPA_ALG_GCMP_256, + WPA_ALG_CCMP_256, + WPA_ALG_BIP_GMAC_128, + WPA_ALG_BIP_GMAC_256, + WPA_ALG_BIP_CMAC_256 +}; + +/** + * enum wpa_states - wpa_supplicant state + * + * 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 state is entered if the network interface is disabled, e.g., + * due to rfkill. wpa_supplicant refuses any new operations that would + * use the radio until the interface has been enabled. + */ + WPA_INTERFACE_DISABLED, + + /** + * WPA_INACTIVE - Inactive state (wpa_supplicant disabled) + * + * This state is entered if there are no enabled networks in the + * configuration. wpa_supplicant is not trying to associate with a new + * network and external interaction (e.g., ctrl_iface call to add or + * enable a network) is needed to start association. + */ + WPA_INACTIVE, + + /** + * WPA_SCANNING - Scanning for a network + * + * This state is entered when wpa_supplicant starts scanning for a + * network. + */ + WPA_SCANNING, + + /** + * WPA_AUTHENTICATING - Trying to authenticate with a BSS/SSID + * + * This state is entered when wpa_supplicant has found a suitable BSS + * to authenticate with and the driver is configured to try to + * authenticate with this BSS. This state is used only with drivers + * that use wpa_supplicant as the SME. + */ + WPA_AUTHENTICATING, + + /** + * WPA_ASSOCIATING - Trying to associate with a BSS/SSID + * + * This state is entered when wpa_supplicant has found a suitable BSS + * to associate with and the driver is configured to try to associate + * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this + * state is entered when the driver is configured to try to associate + * with a network using the configured SSID and security policy. + */ + WPA_ASSOCIATING, + + /** + * WPA_ASSOCIATED - Association completed + * + * This state is entered when the driver reports that association has + * been successfully completed with an AP. If IEEE 802.1X is used + * (with or without WPA/WPA2), wpa_supplicant remains in this state + * until the IEEE 802.1X/EAPOL authentication has been completed. + */ + WPA_ASSOCIATED, + + /** + * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress + * + * This state is entered when WPA/WPA2 4-Way Handshake is started. In + * case of WPA-PSK, this happens when receiving the first EAPOL-Key + * frame after association. In case of WPA-EAP, this state is entered + * when the IEEE 802.1X/EAPOL authentication has been completed. + */ + WPA_4WAY_HANDSHAKE, + + /** + * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress + * + * This state is entered when 4-Way Key Handshake has been completed + * (i.e., when the supplicant sends out message 4/4) and when Group + * Key rekeying is started by the AP (i.e., when supplicant receives + * message 1/2). + */ + WPA_GROUP_HANDSHAKE, + + /** + * WPA_COMPLETED - All authentication completed + * + * This state is entered when the full authentication process is + * completed. In case of WPA2, this happens when the 4-Way Handshake is + * successfully completed. With WPA, this state is entered after the + * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is + * completed after dynamic keys are received (or if not used, after + * the EAP authentication has been completed). With static WEP keys and + * plaintext connections, this state is entered when an association + * has been completed. + * + * This state indicates that the supplicant has completed its + * processing for the association phase and that data connection is + * fully configured. + */ + WPA_COMPLETED +}; + +#define MLME_SETPROTECTION_PROTECT_TYPE_NONE 0 +#define MLME_SETPROTECTION_PROTECT_TYPE_RX 1 +#define MLME_SETPROTECTION_PROTECT_TYPE_TX 2 +#define MLME_SETPROTECTION_PROTECT_TYPE_RX_TX 3 + +#define MLME_SETPROTECTION_KEY_TYPE_GROUP 0 +#define MLME_SETPROTECTION_KEY_TYPE_PAIRWISE 1 + + +/** + * enum mfp_options - Management frame protection (IEEE 802.11w) options + */ +enum mfp_options { + NO_MGMT_FRAME_PROTECTION = 0, + MGMT_FRAME_PROTECTION_OPTIONAL = 1, + MGMT_FRAME_PROTECTION_REQUIRED = 2, +}; +#define MGMT_FRAME_PROTECTION_DEFAULT 3 + +/** + * enum hostapd_hw_mode - Hardware mode + */ +enum hostapd_hw_mode { + HOSTAPD_MODE_IEEE80211B, + HOSTAPD_MODE_IEEE80211G, + HOSTAPD_MODE_IEEE80211A, + HOSTAPD_MODE_IEEE80211AD, + HOSTAPD_MODE_IEEE80211ANY, + NUM_HOSTAPD_MODES +}; + +/** + * enum wpa_ctrl_req_type - Control interface request types + */ +enum wpa_ctrl_req_type { + WPA_CTRL_REQ_UNKNOWN, + WPA_CTRL_REQ_EAP_IDENTITY, + WPA_CTRL_REQ_EAP_PASSWORD, + WPA_CTRL_REQ_EAP_NEW_PASSWORD, + WPA_CTRL_REQ_EAP_PIN, + WPA_CTRL_REQ_EAP_OTP, + WPA_CTRL_REQ_EAP_PASSPHRASE, + WPA_CTRL_REQ_SIM, + WPA_CTRL_REQ_PSK_PASSPHRASE, + NUM_WPA_CTRL_REQS +}; + +/* Maximum number of EAP methods to store for EAP server user information */ +#define EAP_MAX_METHODS 8 + +enum mesh_plink_state { + PLINK_LISTEN = 1, + PLINK_OPEN_SENT, + PLINK_OPEN_RCVD, + PLINK_CNF_RCVD, + PLINK_ESTAB, + PLINK_HOLDING, + PLINK_BLOCKED, +}; + +enum set_band { + WPA_SETBAND_AUTO, + WPA_SETBAND_5G, + WPA_SETBAND_2G +}; + +#endif /* DEFS_H */ diff --git a/freebsd/contrib/wpa/src/common/eapol_common.h b/freebsd/contrib/wpa/src/common/eapol_common.h new file mode 100644 index 00000000..6958661f --- /dev/null +++ b/freebsd/contrib/wpa/src/common/eapol_common.h @@ -0,0 +1,92 @@ +/* + * EAPOL definitions shared between hostapd and wpa_supplicant + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAPOL_COMMON_H +#define EAPOL_COMMON_H + +/* IEEE Std 802.1X-2004 */ + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee802_1x_hdr { + u8 version; + u8 type; + be16 length; + /* followed by length octets of data */ +} STRUCT_PACKED; + +struct ieee8023_hdr { + u8 dest[ETH_ALEN]; + u8 src[ETH_ALEN]; + u16 ethertype; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#ifdef CONFIG_MACSEC +#define EAPOL_VERSION 3 +#else /* CONFIG_MACSEC */ +#define EAPOL_VERSION 2 +#endif /* CONFIG_MACSEC */ + +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, + IEEE802_1X_TYPE_EAPOL_MKA = 5, +}; + +enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, + EAPOL_KEY_TYPE_WPA = 254 }; + + +#define IEEE8021X_REPLAY_COUNTER_LEN 8 +#define IEEE8021X_KEY_SIGN_LEN 16 +#define IEEE8021X_KEY_IV_LEN 16 + +#define IEEE8021X_KEY_INDEX_FLAG 0x80 +#define IEEE8021X_KEY_INDEX_MASK 0x03 + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee802_1x_eapol_key { + u8 type; + /* Note: key_length is unaligned */ + u8 key_length[2]; + /* does not repeat within the life of the keying material used to + * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ + u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; + u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ + u8 key_index; /* key flag in the most significant bit: + * 0 = broadcast (default key), + * 1 = unicast (key mapping key); key index is in the + * 7 least significant bits */ + /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as + * the key */ + u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; + + /* followed by key: if packet body length = 44 + key length, then the + * key field (of key_length bytes) contains the key in encrypted form; + * if packet body length = 44, key field is absent and key_length + * represents the number of least significant octets from + * MS-MPPE-Send-Key attribute to be used as the keying material; + * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#endif /* EAPOL_COMMON_H */ diff --git a/freebsd/contrib/wpa/src/common/gas.c b/freebsd/contrib/wpa/src/common/gas.c new file mode 100644 index 00000000..1aa3c806 --- /dev/null +++ b/freebsd/contrib/wpa/src/common/gas.c @@ -0,0 +1,275 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Generic advertisement service (GAS) (IEEE 802.11u) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011-2012, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ieee802_11_defs.h" +#include "gas.h" + + +static struct wpabuf * +gas_build_req(u8 action, u8 dialog_token, size_t size) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100 + size); + if (buf == NULL) + return NULL; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, action); + wpabuf_put_u8(buf, dialog_token); + + return buf; +} + + +struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size) +{ + return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token, + size); +} + + +struct wpabuf * gas_build_comeback_req(u8 dialog_token) +{ + return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0); +} + + +static struct wpabuf * +gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id, + u8 more, u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100 + size); + if (buf == NULL) + return NULL; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, action); + wpabuf_put_u8(buf, dialog_token); + wpabuf_put_le16(buf, status_code); + if (action == WLAN_PA_GAS_COMEBACK_RESP) + wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0)); + wpabuf_put_le16(buf, comeback_delay); + + return buf; +} + + +struct wpabuf * +gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, + size_t size) +{ + return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token, + status_code, 0, 0, comeback_delay, size); +} + + +static struct wpabuf * +gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, + u16 comeback_delay, size_t size) +{ + return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token, + status_code, frag_id, more, comeback_delay, + size); +} + + +/** + * gas_add_adv_proto_anqp - Add an Advertisement Protocol element + * @buf: Buffer to which the element is added + * @query_resp_len_limit: Query Response Length Limit in units of 256 octets + * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1) + * + * + * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means + * that the maximum limit is determined by the maximum allowable number of + * fragments in the GAS Query Response Fragment ID. + */ +static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit, + u8 pame_bi) +{ + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 2); /* Length */ + wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | + (pame_bi ? 0x80 : 0)); + /* Advertisement Protocol */ + wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL); +} + + +struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_initial_req(dialog_token, 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0, 0); + + wpabuf_put(buf, 2); /* Query Request Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay, + 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0x7f, 0); + + wpabuf_put(buf, 2); /* Query Response Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, + u16 status_code, + u16 comeback_delay, + struct wpabuf *payload) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_initial_resp(dialog_token, status_code, + comeback_delay, + payload ? wpabuf_len(payload) : 0); + if (buf == NULL) + return NULL; + + if (payload) + wpabuf_put_buf(buf, payload); + + gas_anqp_set_len(buf); + + return buf; +} + + +struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_comeback_resp(dialog_token, status_code, + frag_id, more, comeback_delay, 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0x7f, 0); + + wpabuf_put(buf, 2); /* Query Response Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, + u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, + struct wpabuf *payload) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, + more, comeback_delay, + payload ? wpabuf_len(payload) : 0); + if (buf == NULL) + return NULL; + + if (payload) + wpabuf_put_buf(buf, payload); + + gas_anqp_set_len(buf); + + return buf; +} + + +/** + * gas_anqp_set_len - Set Query Request/Response Length + * @buf: GAS message + * + * This function is used to update the Query Request/Response Length field once + * the payload has been filled. + */ +void gas_anqp_set_len(struct wpabuf *buf) +{ + u8 action; + size_t offset; + u8 *len; + + if (buf == NULL || wpabuf_len(buf) < 2) + return; + + action = *(wpabuf_head_u8(buf) + 1); + switch (action) { + case WLAN_PA_GAS_INITIAL_REQ: + offset = 3 + 4; + break; + case WLAN_PA_GAS_INITIAL_RESP: + offset = 7 + 4; + break; + case WLAN_PA_GAS_COMEBACK_RESP: + offset = 8 + 4; + break; + default: + return; + } + + if (wpabuf_len(buf) < offset + 2) + return; + + len = wpabuf_mhead_u8(buf) + offset; + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); +} + + +/** + * gas_anqp_add_element - Add ANQP element header + * @buf: GAS message + * @info_id: ANQP Info ID + * Returns: Pointer to the Length field for gas_anqp_set_element_len() + */ +u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id) +{ + wpabuf_put_le16(buf, info_id); + return wpabuf_put(buf, 2); /* Length to be filled */ +} + + +/** + * gas_anqp_set_element_len - Update ANQP element Length field + * @buf: GAS message + * @len_pos: Length field position from gas_anqp_add_element() + * + * This function is called after the ANQP element payload has been added to the + * buffer. + */ +void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos) +{ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2); +} diff --git a/freebsd/contrib/wpa/src/common/gas.h b/freebsd/contrib/wpa/src/common/gas.h new file mode 100644 index 00000000..306adc58 --- /dev/null +++ b/freebsd/contrib/wpa/src/common/gas.h @@ -0,0 +1,37 @@ +/* + * Generic advertisement service (GAS) (IEEE 802.11u) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011-2012, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef GAS_H +#define GAS_H + +struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size); +struct wpabuf * gas_build_comeback_req(u8 dialog_token); +struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size); +struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, + u16 status_code, + u16 comeback_delay, + struct wpabuf *payload); +struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, + u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, + struct wpabuf *payload); +void gas_anqp_set_len(struct wpabuf *buf); + +u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id); +void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos); + +#endif /* GAS_H */ diff --git a/freebsd/contrib/wpa/src/common/hw_features_common.c b/freebsd/contrib/wpa/src/common/hw_features_common.c new file mode 100644 index 00000000..81b8e695 --- /dev/null +++ b/freebsd/contrib/wpa/src/common/hw_features_common.c @@ -0,0 +1,457 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Common hostapd/wpa_supplicant HW features + * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2015, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "defs.h" +#include "ieee802_11_defs.h" +#include "ieee802_11_common.h" +#include "hw_features_common.h" + + +struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode, + int chan, int *freq) +{ + int i; + + if (freq) + *freq = 0; + + if (!mode) + return NULL; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *ch = &mode->channels[i]; + if (ch->chan == chan) { + if (freq) + *freq = ch->freq; + return ch; + } + } + + return NULL; +} + + +struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode, + int freq, int *chan) +{ + int i; + + if (chan) + *chan = 0; + + if (!mode) + return NULL; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *ch = &mode->channels[i]; + if (ch->freq == freq) { + if (chan) + *chan = ch->chan; + return ch; + } + } + + return NULL; +} + + +int hw_get_freq(struct hostapd_hw_modes *mode, int chan) +{ + int freq; + + hw_get_channel_chan(mode, chan, &freq); + + return freq; +} + + +int hw_get_chan(struct hostapd_hw_modes *mode, int freq) +{ + int chan; + + hw_get_channel_freq(mode, freq, &chan); + + return chan; +} + + +int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, + int sec_chan) +{ + int ok, j, first; + int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, + 149, 157, 184, 192 }; + size_t k; + + if (pri_chan == sec_chan || !sec_chan) + return 1; /* HT40 not used */ + + wpa_printf(MSG_DEBUG, + "HT40: control channel: %d secondary channel: %d", + pri_chan, sec_chan); + + /* Verify that HT40 secondary channel is an allowed 20 MHz + * channel */ + ok = 0; + for (j = 0; j < mode->num_channels; j++) { + struct hostapd_channel_data *chan = &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 (mode->mode != HOSTAPD_MODE_IEEE80211A) + return 1; + + first = pri_chan < sec_chan ? pri_chan : sec_chan; + + ok = 0; + for (k = 0; k < ARRAY_SIZE(allowed); k++) { + if (first == allowed[k]) { + ok = 1; + break; + } + } + if (!ok) { + wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", + pri_chan, sec_chan); + return 0; + } + + return 1; +} + + +void 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) { + oper = (struct ieee80211_ht_operation *) elems.ht_operation; + *pri_chan = oper->primary_chan; + if (oper->ht_param & HT_INFO_HT_PARAM_STA_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; + } + } +} + + +int check_40mhz_5g(struct hostapd_hw_modes *mode, + struct wpa_scan_results *scan_res, int pri_chan, + int sec_chan) +{ + int pri_freq, sec_freq, pri_bss, sec_bss; + int bss_pri_chan, bss_sec_chan; + size_t i; + int match; + + if (!mode || !scan_res || !pri_chan || !sec_chan || + pri_chan == sec_chan) + return 0; + + pri_freq = hw_get_freq(mode, pri_chan); + sec_freq = hw_get_freq(mode, sec_chan); + + /* + * 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"); + return 2; + } + + /* + * 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]; + 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]; + 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)); + return 2; + } + } + } + + return 1; +} + + +static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, + int end) +{ + struct ieee802_11_elems elems; + struct ieee80211_ht_operation *oper; + + if (bss->freq < start || bss->freq > end || bss->freq == pri_freq) + return 0; + + ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); + if (!elems.ht_capabilities) { + wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: " + MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); + return 1; + } + + if (elems.ht_operation) { + oper = (struct ieee80211_ht_operation *) elems.ht_operation; + if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK) + return 0; + + wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: " + MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); + return 1; + } + return 0; +} + + +int check_40mhz_2g4(struct hostapd_hw_modes *mode, + struct wpa_scan_results *scan_res, int pri_chan, + int sec_chan) +{ + int pri_freq, sec_freq; + int affected_start, affected_end; + size_t i; + + if (!mode || !scan_res || !pri_chan || !sec_chan || + pri_chan == sec_chan) + return 0; + + pri_freq = hw_get_freq(mode, pri_chan); + sec_freq = hw_get_freq(mode, sec_chan); + + 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; + struct ieee802_11_elems elems; + + /* Check for overlapping 20 MHz BSS */ + if (check_20mhz_bss(bss, pri_freq, affected_start, + affected_end)) { + wpa_printf(MSG_DEBUG, + "Overlapping 20 MHz BSS is found"); + return 0; + } + + 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; + } + } + + ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, + 0); + if (elems.ht_capabilities) { + struct ieee80211_ht_capabilities *ht_cap = + (struct ieee80211_ht_capabilities *) + elems.ht_capabilities; + + if (le_to_host16(ht_cap->ht_capabilities_info) & + HT_CAP_INFO_40MHZ_INTOLERANT) { + wpa_printf(MSG_DEBUG, + "40 MHz Intolerant is set on channel %d in BSS " + MACSTR, pri, MAC2STR(bss->bssid)); + return 0; + } + } + } + + return 1; +} + + +int hostapd_set_freq_params(struct hostapd_freq_params *data, + enum hostapd_hw_mode mode, + int freq, int channel, int ht_enabled, + int vht_enabled, int sec_channel_offset, + int vht_oper_chwidth, int center_segment0, + int center_segment1, u32 vht_caps) +{ + os_memset(data, 0, sizeof(*data)); + data->mode = mode; + data->freq = freq; + data->channel = channel; + data->ht_enabled = ht_enabled; + data->vht_enabled = vht_enabled; + data->sec_channel_offset = sec_channel_offset; + data->center_freq1 = freq + sec_channel_offset * 10; + data->center_freq2 = 0; + data->bandwidth = sec_channel_offset ? 40 : 20; + + if (data->vht_enabled) switch (vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + if (center_segment1 || + (center_segment0 != 0 && + 5000 + center_segment0 * 5 != data->center_freq1 && + 2407 + center_segment0 * 5 != data->center_freq1)) + return -1; + break; + case VHT_CHANWIDTH_80P80MHZ: + if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) { + wpa_printf(MSG_ERROR, + "80+80 channel width is not supported!"); + return -1; + } + if (center_segment1 == center_segment0 + 4 || + center_segment1 == center_segment0 - 4) + return -1; + data->center_freq2 = 5000 + center_segment1 * 5; + /* fall through */ + case VHT_CHANWIDTH_80MHZ: + data->bandwidth = 80; + if ((vht_oper_chwidth == 1 && center_segment1) || + (vht_oper_chwidth == 3 && !center_segment1) || + !sec_channel_offset) + return -1; + if (!center_segment0) { + if (channel <= 48) + center_segment0 = 42; + else if (channel <= 64) + center_segment0 = 58; + else if (channel <= 112) + center_segment0 = 106; + else if (channel <= 128) + center_segment0 = 122; + else if (channel <= 144) + center_segment0 = 138; + else if (channel <= 161) + center_segment0 = 155; + data->center_freq1 = 5000 + center_segment0 * 5; + } else { + /* + * Note: HT/VHT config and params are coupled. Check if + * HT40 channel band is in VHT80 Pri channel band + * configuration. + */ + if (center_segment0 == channel + 6 || + center_segment0 == channel + 2 || + center_segment0 == channel - 2 || + center_segment0 == channel - 6) + data->center_freq1 = 5000 + center_segment0 * 5; + else + return -1; + } + break; + case VHT_CHANWIDTH_160MHZ: + data->bandwidth = 160; + if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | + VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { + wpa_printf(MSG_ERROR, + "160MHZ channel width is not supported!"); + return -1; + } + if (center_segment1) + return -1; + if (!sec_channel_offset) + return -1; + /* + * Note: HT/VHT config and params are coupled. Check if + * HT40 channel band is in VHT160 channel band configuration. + */ + if (center_segment0 == channel + 14 || + center_segment0 == channel + 10 || + center_segment0 == channel + 6 || + center_segment0 == channel + 2 || + center_segment0 == channel - 2 || + center_segment0 == channel - 6 || + center_segment0 == channel - 10 || + center_segment0 == channel - 14) + data->center_freq1 = 5000 + center_segment0 * 5; + else + return -1; + break; + } + + return 0; +} diff --git a/freebsd/contrib/wpa/src/common/hw_features_common.h b/freebsd/contrib/wpa/src/common/hw_features_common.h new file mode 100644 index 00000000..7360b4e3 --- /dev/null +++ b/freebsd/contrib/wpa/src/common/hw_features_common.h @@ -0,0 +1,39 @@ +/* + * Common hostapd/wpa_supplicant HW features + * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2015, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HW_FEATURES_COMMON_H +#define HW_FEATURES_COMMON_H + +#include "drivers/driver.h" + +struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode, + int chan, int *freq); +struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode, + int freq, int *chan); + +int hw_get_freq(struct hostapd_hw_modes *mode, int chan); +int hw_get_chan(struct hostapd_hw_modes *mode, int freq); + +int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, + int sec_chan); +void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan); +int check_40mhz_5g(struct hostapd_hw_modes *mode, + struct wpa_scan_results *scan_res, int pri_chan, + int sec_chan); +int check_40mhz_2g4(struct hostapd_hw_modes *mode, + struct wpa_scan_results *scan_res, int pri_chan, + int sec_chan); +int hostapd_set_freq_params(struct hostapd_freq_params *data, + enum hostapd_hw_mode mode, + int freq, int channel, int ht_enabled, + int vht_enabled, int sec_channel_offset, + int vht_oper_chwidth, int center_segment0, + int center_segment1, u32 vht_caps); + +#endif /* HW_FEATURES_COMMON_H */ diff --git a/freebsd/contrib/wpa/src/common/ieee802_11_common.c b/freebsd/contrib/wpa/src/common/ieee802_11_common.c new file mode 100644 index 00000000..ba7ef0a8 --- /dev/null +++ b/freebsd/contrib/wpa/src/common/ieee802_11_common.c @@ -0,0 +1,1149 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * IEEE 802.11 Common routines + * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "defs.h" +#include "wpa_common.h" +#include "qca-vendor.h" +#include "ieee802_11_defs.h" +#include "ieee802_11_common.h" + + +static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, + struct ieee802_11_elems *elems, + int show_errors) +{ + unsigned int oui; + + /* first 3 bytes in vendor specific information element are the IEEE + * OUI of the vendor. The following byte is used a vendor specific + * sub-type. */ + if (elen < 4) { + if (show_errors) { + wpa_printf(MSG_MSGDUMP, "short vendor specific " + "information element ignored (len=%lu)", + (unsigned long) elen); + } + return -1; + } + + oui = WPA_GET_BE24(pos); + switch (oui) { + case OUI_MICROSOFT: + /* Microsoft/Wi-Fi information elements are further typed and + * subtyped */ + switch (pos[3]) { + case 1: + /* Microsoft OUI (00:50:F2) with OUI Type 1: + * real WPA information element */ + elems->wpa_ie = pos; + elems->wpa_ie_len = elen; + break; + case WMM_OUI_TYPE: + /* WMM information element */ + if (elen < 5) { + wpa_printf(MSG_MSGDUMP, "short WMM " + "information element ignored " + "(len=%lu)", + (unsigned long) elen); + return -1; + } + switch (pos[4]) { + case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT: + case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT: + /* + * Share same pointer since only one of these + * is used and they start with same data. + * Length field can be used to distinguish the + * IEs. + */ + elems->wmm = pos; + elems->wmm_len = elen; + break; + case WMM_OUI_SUBTYPE_TSPEC_ELEMENT: + elems->wmm_tspec = pos; + elems->wmm_tspec_len = elen; + break; + default: + wpa_printf(MSG_EXCESSIVE, "unknown WMM " + "information element ignored " + "(subtype=%d len=%lu)", + pos[4], (unsigned long) elen); + return -1; + } + break; + case 4: + /* Wi-Fi Protected Setup (WPS) IE */ + elems->wps_ie = pos; + elems->wps_ie_len = elen; + break; + default: + wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft " + "information element ignored " + "(type=%d len=%lu)", + pos[3], (unsigned long) elen); + return -1; + } + break; + + case OUI_WFA: + switch (pos[3]) { + case P2P_OUI_TYPE: + /* Wi-Fi Alliance - P2P IE */ + elems->p2p = pos; + elems->p2p_len = elen; + break; + case WFD_OUI_TYPE: + /* Wi-Fi Alliance - WFD IE */ + elems->wfd = pos; + elems->wfd_len = elen; + break; + case HS20_INDICATION_OUI_TYPE: + /* Hotspot 2.0 */ + elems->hs20 = pos; + elems->hs20_len = elen; + break; + case HS20_OSEN_OUI_TYPE: + /* Hotspot 2.0 OSEN */ + elems->osen = pos; + elems->osen_len = elen; + break; + default: + wpa_printf(MSG_MSGDUMP, "Unknown WFA " + "information element ignored " + "(type=%d len=%lu)", + 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; + case VENDOR_VHT_TYPE: + if (elen > 4 && + (pos[4] == VENDOR_VHT_SUBTYPE || + pos[4] == VENDOR_VHT_SUBTYPE2)) { + elems->vendor_vht = pos; + elems->vendor_vht_len = elen; + } else + return -1; + break; + default: + wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom " + "information element ignored " + "(type=%d len=%lu)", + pos[3], (unsigned long) elen); + return -1; + } + break; + + case OUI_QCA: + switch (pos[3]) { + case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: + elems->pref_freq_list = pos; + elems->pref_freq_list_len = elen; + break; + default: + wpa_printf(MSG_EXCESSIVE, + "Unknown QCA 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: + if (elen > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "Ignored too long SSID element (elen=%u)", + elen); + break; + } + 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_DS_PARAMS: + if (elen < 1) + break; + elems->ds_params = pos; + break; + case WLAN_EID_CF_PARAMS: + case WLAN_EID_TIM: + break; + case WLAN_EID_CHALLENGE: + elems->challenge = pos; + elems->challenge_len = elen; + break; + case WLAN_EID_ERP_INFO: + if (elen < 1) + break; + elems->erp_info = pos; + 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: + break; + case WLAN_EID_SUPPORTED_CHANNELS: + elems->supp_channels = pos; + elems->supp_channels_len = elen; + break; + case WLAN_EID_MOBILITY_DOMAIN: + if (elen < sizeof(struct rsn_mdie)) + break; + elems->mdie = pos; + elems->mdie_len = elen; + break; + case WLAN_EID_FAST_BSS_TRANSITION: + if (elen < sizeof(struct rsn_ftie)) + break; + elems->ftie = pos; + elems->ftie_len = elen; + break; + case WLAN_EID_TIMEOUT_INTERVAL: + if (elen != 5) + break; + elems->timeout_int = pos; + break; + case WLAN_EID_HT_CAP: + if (elen < sizeof(struct ieee80211_ht_capabilities)) + break; + elems->ht_capabilities = pos; + break; + case WLAN_EID_HT_OPERATION: + if (elen < sizeof(struct ieee80211_ht_operation)) + break; + elems->ht_operation = pos; + break; + case WLAN_EID_MESH_CONFIG: + elems->mesh_config = pos; + elems->mesh_config_len = elen; + break; + case WLAN_EID_MESH_ID: + elems->mesh_id = pos; + elems->mesh_id_len = elen; + break; + case WLAN_EID_PEER_MGMT: + elems->peer_mgmt = pos; + elems->peer_mgmt_len = elen; + break; + case WLAN_EID_VHT_CAP: + if (elen < sizeof(struct ieee80211_vht_capabilities)) + break; + elems->vht_capabilities = pos; + break; + case WLAN_EID_VHT_OPERATION: + if (elen < sizeof(struct ieee80211_vht_operation)) + break; + elems->vht_operation = pos; + break; + case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION: + if (elen != 1) + break; + elems->vht_opmode_notif = pos; + break; + case WLAN_EID_LINK_ID: + if (elen < 18) + break; + elems->link_id = pos; + break; + case WLAN_EID_INTERWORKING: + elems->interworking = pos; + elems->interworking_len = elen; + break; + case WLAN_EID_QOS_MAP_SET: + if (elen < 16) + break; + elems->qos_map_set = pos; + elems->qos_map_set_len = elen; + break; + case WLAN_EID_EXT_CAPAB: + elems->ext_capab = pos; + elems->ext_capab_len = elen; + break; + case WLAN_EID_BSS_MAX_IDLE_PERIOD: + if (elen < 3) + break; + elems->bss_max_idle_period = pos; + break; + case WLAN_EID_SSID_LIST: + elems->ssid_list = pos; + elems->ssid_list_len = elen; + break; + case WLAN_EID_AMPE: + elems->ampe = pos; + elems->ampe_len = elen; + break; + case WLAN_EID_MIC: + elems->mic = pos; + elems->mic_len = elen; + /* after mic everything is encrypted, so stop. */ + left = elen; + break; + case WLAN_EID_MULTI_BAND: + if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) { + wpa_printf(MSG_MSGDUMP, + "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)", + id, elen); + break; + } + + elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos; + elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen; + elems->mb_ies.nof_ies++; + break; + default: + unknown++; + if (!show_errors) + break; + wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse " + "ignored unknown element (id=%d elen=%d)", + id, elen); + break; + } + + left -= elen; + pos += elen; + } + + if (left) + return ParseFailed; + + return unknown ? ParseUnknown : ParseOK; +} + + +int ieee802_11_ie_count(const u8 *ies, size_t ies_len) +{ + int count = 0; + const u8 *pos, *end; + + if (ies == NULL) + return 0; + + pos = ies; + end = ies + ies_len; + + while (pos + 2 <= end) { + if (pos + 2 + pos[1] > end) + break; + count++; + pos += 2 + pos[1]; + } + + return count; +} + + +struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, + u32 oui_type) +{ + struct wpabuf *buf; + const u8 *end, *pos, *ie; + + pos = ies; + end = ies + ies_len; + ie = NULL; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + return NULL; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + WPA_GET_BE32(&pos[2]) == oui_type) { + ie = pos; + break; + } + pos += 2 + pos[1]; + } + + if (ie == NULL) + return NULL; /* No specified vendor IE found */ + + buf = wpabuf_alloc(ies_len); + if (buf == NULL) + return NULL; + + /* + * There may be multiple vendor IEs in the message, so need to + * concatenate their data fields. + */ + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + WPA_GET_BE32(&pos[2]) == oui_type) + wpabuf_put_data(buf, pos + 6, pos[1] - 4); + pos += 2 + pos[1]; + } + + return buf; +} + + +const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) +{ + u16 fc, type, stype; + + /* + * PS-Poll frames are 16 bytes. All other frames are + * 24 bytes or longer. + */ + if (len < 16) + return NULL; + + fc = le_to_host16(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + switch (type) { + case WLAN_FC_TYPE_DATA: + if (len < 24) + return NULL; + switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { + case WLAN_FC_FROMDS | WLAN_FC_TODS: + case WLAN_FC_TODS: + return hdr->addr1; + case WLAN_FC_FROMDS: + return hdr->addr2; + default: + return NULL; + } + case WLAN_FC_TYPE_CTRL: + if (stype != WLAN_FC_STYPE_PSPOLL) + return NULL; + return hdr->addr1; + case WLAN_FC_TYPE_MGMT: + return hdr->addr3; + default: + return NULL; + } +} + + +int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], + const char *name, const char *val) +{ + int num, v; + const char *pos; + struct hostapd_wmm_ac_params *ac; + + /* skip 'wme_ac_' or 'wmm_ac_' prefix */ + pos = name + 7; + if (os_strncmp(pos, "be_", 3) == 0) { + num = 0; + pos += 3; + } else if (os_strncmp(pos, "bk_", 3) == 0) { + num = 1; + pos += 3; + } else if (os_strncmp(pos, "vi_", 3) == 0) { + num = 2; + pos += 3; + } else if (os_strncmp(pos, "vo_", 3) == 0) { + num = 3; + pos += 3; + } else { + wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); + return -1; + } + + ac = &wmm_ac_params[num]; + + if (os_strcmp(pos, "aifs") == 0) { + v = atoi(val); + if (v < 1 || v > 255) { + wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); + return -1; + } + ac->aifs = v; + } else if (os_strcmp(pos, "cwmin") == 0) { + v = atoi(val); + if (v < 0 || v > 15) { + wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); + return -1; + } + ac->cwmin = v; + } else if (os_strcmp(pos, "cwmax") == 0) { + v = atoi(val); + if (v < 0 || v > 15) { + wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); + return -1; + } + ac->cwmax = v; + } else if (os_strcmp(pos, "txop_limit") == 0) { + v = atoi(val); + if (v < 0 || v > 0xffff) { + wpa_printf(MSG_ERROR, "Invalid txop value %d", v); + return -1; + } + ac->txop_limit = v; + } else if (os_strcmp(pos, "acm") == 0) { + v = atoi(val); + if (v < 0 || v > 1) { + wpa_printf(MSG_ERROR, "Invalid acm value %d", v); + return -1; + } + ac->admission_control_mandatory = v; + } else { + wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); + return -1; + } + + return 0; +} + + +enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) +{ + u8 op_class; + + return ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, channel); +} + + +/** + * ieee80211_freq_to_channel_ext - Convert frequency into channel info + * for HT40 and VHT. DFS channels are not covered. + * @freq: Frequency (MHz) to convert + * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below + * @vht: 0 - non-VHT, 1 - 80 MHz + * @op_class: Buffer for returning operating class + * @channel: Buffer for returning channel number + * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure + */ +enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, + int sec_channel, int vht, + u8 *op_class, u8 *channel) +{ + /* TODO: more operating classes */ + + if (sec_channel > 1 || sec_channel < -1) + return NUM_HOSTAPD_MODES; + + if (freq >= 2412 && freq <= 2472) { + if ((freq - 2407) % 5) + return NUM_HOSTAPD_MODES; + + if (vht) + return NUM_HOSTAPD_MODES; + + /* 2.407 GHz, channels 1..13 */ + if (sec_channel == 1) + *op_class = 83; + else if (sec_channel == -1) + *op_class = 84; + else + *op_class = 81; + + *channel = (freq - 2407) / 5; + + return HOSTAPD_MODE_IEEE80211G; + } + + if (freq == 2484) { + if (sec_channel || vht) + return NUM_HOSTAPD_MODES; + + *op_class = 82; /* channel 14 */ + *channel = 14; + + return HOSTAPD_MODE_IEEE80211B; + } + + if (freq >= 4900 && freq < 5000) { + if ((freq - 4000) % 5) + return NUM_HOSTAPD_MODES; + *channel = (freq - 4000) / 5; + *op_class = 0; /* TODO */ + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 36..48 */ + if (freq >= 5180 && freq <= 5240) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + if (sec_channel == 1) + *op_class = 116; + else if (sec_channel == -1) + *op_class = 117; + else if (vht) + *op_class = 128; + else + *op_class = 115; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 149..161 */ + if (freq >= 5745 && freq <= 5805) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + if (sec_channel == 1) + *op_class = 126; + else if (sec_channel == -1) + *op_class = 127; + else if (vht) + *op_class = 128; + else + *op_class = 124; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + + /* 5 GHz, channels 149..169 */ + if (freq >= 5745 && freq <= 5845) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + *op_class = 125; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + + if (freq >= 5000 && freq < 5900) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + *channel = (freq - 5000) / 5; + *op_class = 0; /* TODO */ + return HOSTAPD_MODE_IEEE80211A; + } + + /* 56.16 GHz, channel 1..4 */ + if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { + if (sec_channel || vht) + return NUM_HOSTAPD_MODES; + + *channel = (freq - 56160) / 2160; + *op_class = 180; + + return HOSTAPD_MODE_IEEE80211AD; + } + + return NUM_HOSTAPD_MODES; +} + + +static const char *const us_op_class_cc[] = { + "US", "CA", NULL +}; + +static const char *const eu_op_class_cc[] = { + "AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE", + "DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT", + "LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT", + "RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL +}; + +static const char *const jp_op_class_cc[] = { + "JP", NULL +}; + +static const char *const cn_op_class_cc[] = { + "CN", NULL +}; + + +static int country_match(const char *const cc[], const char *const country) +{ + int i; + + if (country == NULL) + return 0; + for (i = 0; cc[i]; i++) { + if (cc[i][0] == country[0] && cc[i][1] == country[1]) + return 1; + } + + return 0; +} + + +static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan) +{ + switch (op_class) { + case 12: /* channels 1..11 */ + case 32: /* channels 1..7; 40 MHz */ + case 33: /* channels 5..11; 40 MHz */ + if (chan < 1 || chan > 11) + return -1; + return 2407 + 5 * chan; + case 1: /* channels 36,40,44,48 */ + case 2: /* channels 52,56,60,64; dfs */ + case 22: /* channels 36,44; 40 MHz */ + case 23: /* channels 52,60; 40 MHz */ + case 27: /* channels 40,48; 40 MHz */ + case 28: /* channels 56,64; 40 MHz */ + if (chan < 36 || chan > 64) + return -1; + return 5000 + 5 * chan; + case 4: /* channels 100-144 */ + case 24: /* channels 100-140; 40 MHz */ + if (chan < 100 || chan > 144) + return -1; + return 5000 + 5 * chan; + case 3: /* channels 149,153,157,161 */ + case 25: /* channels 149,157; 40 MHz */ + case 26: /* channels 149,157; 40 MHz */ + case 30: /* channels 153,161; 40 MHz */ + case 31: /* channels 153,161; 40 MHz */ + if (chan < 149 || chan > 161) + return -1; + return 5000 + 5 * chan; + case 5: /* channels 149,153,157,161,165 */ + if (chan < 149 || chan > 165) + return -1; + return 5000 + 5 * chan; + case 34: /* 60 GHz band, channels 1..3 */ + if (chan < 1 || chan > 3) + return -1; + return 56160 + 2160 * chan; + } + return -1; +} + + +static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan) +{ + switch (op_class) { + case 4: /* channels 1..13 */ + case 11: /* channels 1..9; 40 MHz */ + case 12: /* channels 5..13; 40 MHz */ + if (chan < 1 || chan > 13) + return -1; + return 2407 + 5 * chan; + case 1: /* channels 36,40,44,48 */ + case 2: /* channels 52,56,60,64; dfs */ + case 5: /* channels 36,44; 40 MHz */ + case 6: /* channels 52,60; 40 MHz */ + case 8: /* channels 40,48; 40 MHz */ + case 9: /* channels 56,64; 40 MHz */ + if (chan < 36 || chan > 64) + return -1; + return 5000 + 5 * chan; + case 3: /* channels 100-140 */ + case 7: /* channels 100-132; 40 MHz */ + case 10: /* channels 104-136; 40 MHz */ + case 16: /* channels 100-140 */ + if (chan < 100 || chan > 140) + return -1; + return 5000 + 5 * chan; + case 17: /* channels 149,153,157,161,165,169 */ + if (chan < 149 || chan > 169) + return -1; + return 5000 + 5 * chan; + case 18: /* 60 GHz band, channels 1..4 */ + if (chan < 1 || chan > 4) + return -1; + return 56160 + 2160 * chan; + } + return -1; +} + + +static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan) +{ + switch (op_class) { + case 30: /* channels 1..13 */ + case 56: /* channels 1..9; 40 MHz */ + case 57: /* channels 5..13; 40 MHz */ + if (chan < 1 || chan > 13) + return -1; + return 2407 + 5 * chan; + case 31: /* channel 14 */ + if (chan != 14) + return -1; + return 2414 + 5 * chan; + case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */ + case 32: /* channels 52,56,60,64 */ + case 33: /* channels 52,56,60,64 */ + case 36: /* channels 36,44; 40 MHz */ + case 37: /* channels 52,60; 40 MHz */ + case 38: /* channels 52,60; 40 MHz */ + case 41: /* channels 40,48; 40 MHz */ + case 42: /* channels 56,64; 40 MHz */ + case 43: /* channels 56,64; 40 MHz */ + if (chan < 34 || chan > 64) + return -1; + return 5000 + 5 * chan; + case 34: /* channels 100-140 */ + case 35: /* channels 100-140 */ + case 39: /* channels 100-132; 40 MHz */ + case 40: /* channels 100-132; 40 MHz */ + case 44: /* channels 104-136; 40 MHz */ + case 45: /* channels 104-136; 40 MHz */ + case 58: /* channels 100-140 */ + if (chan < 100 || chan > 140) + return -1; + return 5000 + 5 * chan; + case 59: /* 60 GHz band, channels 1..4 */ + if (chan < 1 || chan > 3) + return -1; + return 56160 + 2160 * chan; + } + return -1; +} + + +static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan) +{ + switch (op_class) { + case 7: /* channels 1..13 */ + case 8: /* channels 1..9; 40 MHz */ + case 9: /* channels 5..13; 40 MHz */ + if (chan < 1 || chan > 13) + return -1; + return 2407 + 5 * chan; + case 1: /* channels 36,40,44,48 */ + case 2: /* channels 52,56,60,64; dfs */ + case 4: /* channels 36,44; 40 MHz */ + case 5: /* channels 52,60; 40 MHz */ + if (chan < 36 || chan > 64) + return -1; + return 5000 + 5 * chan; + case 3: /* channels 149,153,157,161,165 */ + case 6: /* channels 149,157; 40 MHz */ + if (chan < 149 || chan > 165) + return -1; + return 5000 + 5 * chan; + } + return -1; +} + + +static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan) +{ + /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */ + switch (op_class) { + case 81: + /* channels 1..13 */ + if (chan < 1 || chan > 13) + return -1; + return 2407 + 5 * chan; + case 82: + /* channel 14 */ + if (chan != 14) + return -1; + return 2414 + 5 * chan; + case 83: /* channels 1..9; 40 MHz */ + case 84: /* channels 5..13; 40 MHz */ + if (chan < 1 || chan > 13) + return -1; + return 2407 + 5 * chan; + case 115: /* channels 36,40,44,48; indoor only */ + case 116: /* channels 36,44; 40 MHz; indoor only */ + case 117: /* channels 40,48; 40 MHz; indoor only */ + case 118: /* channels 52,56,60,64; dfs */ + case 119: /* channels 52,60; 40 MHz; dfs */ + case 120: /* channels 56,64; 40 MHz; dfs */ + if (chan < 36 || chan > 64) + return -1; + return 5000 + 5 * chan; + case 121: /* channels 100-140 */ + case 122: /* channels 100-142; 40 MHz */ + case 123: /* channels 104-136; 40 MHz */ + if (chan < 100 || chan > 140) + return -1; + return 5000 + 5 * chan; + case 124: /* channels 149,153,157,161 */ + case 126: /* channels 149,157; 40 MHz */ + case 127: /* channels 153,161; 40 MHz */ + if (chan < 149 || chan > 161) + return -1; + return 5000 + 5 * chan; + case 125: /* channels 149,153,157,161,165,169 */ + if (chan < 149 || chan > 169) + return -1; + return 5000 + 5 * chan; + case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ + case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ + if (chan < 36 || chan > 161) + return -1; + return 5000 + 5 * chan; + case 129: /* center freqs 50, 114; 160 MHz */ + if (chan < 50 || chan > 114) + return -1; + return 5000 + 5 * chan; + case 180: /* 60 GHz band, channels 1..4 */ + if (chan < 1 || chan > 4) + return -1; + return 56160 + 2160 * chan; + } + return -1; +} + +/** + * ieee80211_chan_to_freq - Convert channel info to frequency + * @country: Country code, if known; otherwise, global operating class is used + * @op_class: Operating class + * @chan: Channel number + * Returns: Frequency in MHz or -1 if the specified channel is unknown + */ +int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan) +{ + int freq; + + if (country_match(us_op_class_cc, country)) { + freq = ieee80211_chan_to_freq_us(op_class, chan); + if (freq > 0) + return freq; + } + + if (country_match(eu_op_class_cc, country)) { + freq = ieee80211_chan_to_freq_eu(op_class, chan); + if (freq > 0) + return freq; + } + + if (country_match(jp_op_class_cc, country)) { + freq = ieee80211_chan_to_freq_jp(op_class, chan); + if (freq > 0) + return freq; + } + + if (country_match(cn_op_class_cc, country)) { + freq = ieee80211_chan_to_freq_cn(op_class, chan); + if (freq > 0) + return freq; + } + + return ieee80211_chan_to_freq_global(op_class, chan); +} + + +int ieee80211_is_dfs(int freq) +{ + /* TODO: this could be more accurate to better cover all domains */ + return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700); +} + + +static int is_11b(u8 rate) +{ + return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; +} + + +int supp_rates_11b_only(struct ieee802_11_elems *elems) +{ + int num_11b = 0, num_others = 0; + int i; + + if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL) + return 0; + + for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) { + if (is_11b(elems->supp_rates[i])) + num_11b++; + else + num_others++; + } + + for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len; + i++) { + if (is_11b(elems->ext_supp_rates[i])) + num_11b++; + else + num_others++; + } + + return num_11b > 0 && num_others == 0; +} + + +const char * fc2str(u16 fc) +{ + u16 stype = WLAN_FC_GET_STYPE(fc); +#define C2S(x) case x: return #x; + + switch (WLAN_FC_GET_TYPE(fc)) { + case WLAN_FC_TYPE_MGMT: + switch (stype) { + C2S(WLAN_FC_STYPE_ASSOC_REQ) + C2S(WLAN_FC_STYPE_ASSOC_RESP) + C2S(WLAN_FC_STYPE_REASSOC_REQ) + C2S(WLAN_FC_STYPE_REASSOC_RESP) + C2S(WLAN_FC_STYPE_PROBE_REQ) + C2S(WLAN_FC_STYPE_PROBE_RESP) + C2S(WLAN_FC_STYPE_BEACON) + C2S(WLAN_FC_STYPE_ATIM) + C2S(WLAN_FC_STYPE_DISASSOC) + C2S(WLAN_FC_STYPE_AUTH) + C2S(WLAN_FC_STYPE_DEAUTH) + C2S(WLAN_FC_STYPE_ACTION) + } + break; + case WLAN_FC_TYPE_CTRL: + switch (stype) { + C2S(WLAN_FC_STYPE_PSPOLL) + C2S(WLAN_FC_STYPE_RTS) + C2S(WLAN_FC_STYPE_CTS) + C2S(WLAN_FC_STYPE_ACK) + C2S(WLAN_FC_STYPE_CFEND) + C2S(WLAN_FC_STYPE_CFENDACK) + } + break; + case WLAN_FC_TYPE_DATA: + switch (stype) { + C2S(WLAN_FC_STYPE_DATA) + C2S(WLAN_FC_STYPE_DATA_CFACK) + C2S(WLAN_FC_STYPE_DATA_CFPOLL) + C2S(WLAN_FC_STYPE_DATA_CFACKPOLL) + C2S(WLAN_FC_STYPE_NULLFUNC) + C2S(WLAN_FC_STYPE_CFACK) + C2S(WLAN_FC_STYPE_CFPOLL) + C2S(WLAN_FC_STYPE_CFACKPOLL) + C2S(WLAN_FC_STYPE_QOS_DATA) + C2S(WLAN_FC_STYPE_QOS_DATA_CFACK) + C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL) + C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL) + C2S(WLAN_FC_STYPE_QOS_NULL) + C2S(WLAN_FC_STYPE_QOS_CFPOLL) + C2S(WLAN_FC_STYPE_QOS_CFACKPOLL) + } + break; + } + return "WLAN_FC_TYPE_UNKNOWN"; +#undef C2S +} + + +int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, + size_t ies_len) +{ + os_memset(info, 0, sizeof(*info)); + + while (ies_buf && ies_len >= 2 && + info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) { + size_t len = 2 + ies_buf[1]; + + if (len > ies_len) { + wpa_hexdump(MSG_DEBUG, "Truncated IEs", + ies_buf, ies_len); + return -1; + } + + if (ies_buf[0] == WLAN_EID_MULTI_BAND) { + wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len); + info->ies[info->nof_ies].ie = ies_buf + 2; + info->ies[info->nof_ies].ie_len = ies_buf[1]; + info->nof_ies++; + } + + ies_len -= len; + ies_buf += len; + } + + return 0; +} + + +struct wpabuf * mb_ies_by_info(struct mb_ies_info *info) +{ + struct wpabuf *mb_ies = NULL; + + WPA_ASSERT(info != NULL); + + if (info->nof_ies) { + u8 i; + size_t mb_ies_size = 0; + + for (i = 0; i < info->nof_ies; i++) + mb_ies_size += 2 + info->ies[i].ie_len; + + mb_ies = wpabuf_alloc(mb_ies_size); + if (mb_ies) { + for (i = 0; i < info->nof_ies; i++) { + wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND); + wpabuf_put_u8(mb_ies, info->ies[i].ie_len); + wpabuf_put_data(mb_ies, + info->ies[i].ie, + info->ies[i].ie_len); + } + } + } + + return mb_ies; +} diff --git a/freebsd/contrib/wpa/src/common/ieee802_11_common.h b/freebsd/contrib/wpa/src/common/ieee802_11_common.h new file mode 100644 index 00000000..55ce0223 --- /dev/null +++ b/freebsd/contrib/wpa/src/common/ieee802_11_common.h @@ -0,0 +1,128 @@ +/* + * IEEE 802.11 Common routines + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_11_COMMON_H +#define IEEE802_11_COMMON_H + +#define MAX_NOF_MB_IES_SUPPORTED 5 + +struct mb_ies_info { + struct { + const u8 *ie; + u8 ie_len; + } ies[MAX_NOF_MB_IES_SUPPORTED]; + u8 nof_ies; +}; + +/* Parsed Information Elements */ +struct ieee802_11_elems { + const u8 *ssid; + const u8 *supp_rates; + const u8 *ds_params; + const u8 *challenge; + const u8 *erp_info; + const u8 *ext_supp_rates; + const u8 *wpa_ie; + const u8 *rsn_ie; + const u8 *wmm; /* WMM Information or Parameter Element */ + const u8 *wmm_tspec; + const u8 *wps_ie; + const u8 *supp_channels; + const u8 *mdie; + const u8 *ftie; + const u8 *timeout_int; + const u8 *ht_capabilities; + const u8 *ht_operation; + const u8 *mesh_config; + const u8 *mesh_id; + const u8 *peer_mgmt; + const u8 *vht_capabilities; + const u8 *vht_operation; + const u8 *vht_opmode_notif; + const u8 *vendor_ht_cap; + const u8 *vendor_vht; + const u8 *p2p; + const u8 *wfd; + const u8 *link_id; + const u8 *interworking; + const u8 *qos_map_set; + const u8 *hs20; + const u8 *ext_capab; + const u8 *bss_max_idle_period; + const u8 *ssid_list; + const u8 *osen; + const u8 *ampe; + const u8 *mic; + const u8 *pref_freq_list; + + u8 ssid_len; + u8 supp_rates_len; + u8 challenge_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 supp_channels_len; + u8 mdie_len; + u8 ftie_len; + u8 mesh_config_len; + u8 mesh_id_len; + u8 peer_mgmt_len; + u8 vendor_ht_cap_len; + u8 vendor_vht_len; + u8 p2p_len; + u8 wfd_len; + u8 interworking_len; + u8 qos_map_set_len; + u8 hs20_len; + u8 ext_capab_len; + u8 ssid_list_len; + u8 osen_len; + u8 ampe_len; + u8 mic_len; + u8 pref_freq_list_len; + struct mb_ies_info mb_ies; +}; + +typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; + +ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, + struct ieee802_11_elems *elems, + int show_errors); +int ieee802_11_ie_count(const u8 *ies, size_t ies_len); +struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, + u32 oui_type); +struct ieee80211_hdr; +const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len); + +struct hostapd_wmm_ac_params { + int cwmin; + int cwmax; + int aifs; + int txop_limit; /* in units of 32us */ + int admission_control_mandatory; +}; + +int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], + const char *name, const char *val); +enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel); +int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan); +enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, + int sec_channel, int vht, + u8 *op_class, u8 *channel); +int ieee80211_is_dfs(int freq); + +int supp_rates_11b_only(struct ieee802_11_elems *elems); +int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, + size_t ies_len); +struct wpabuf * mb_ies_by_info(struct mb_ies_info *info); + +const char * fc2str(u16 fc); +#endif /* IEEE802_11_COMMON_H */ diff --git a/freebsd/contrib/wpa/src/common/ieee802_11_defs.h b/freebsd/contrib/wpa/src/common/ieee802_11_defs.h new file mode 100644 index 00000000..44530ce3 --- /dev/null +++ b/freebsd/contrib/wpa/src/common/ieee802_11_defs.h @@ -0,0 +1,1436 @@ +/* + * IEEE 802.11 Frame type definitions + * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008 Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_11_DEFS_H +#define IEEE802_11_DEFS_H + +#include <utils/common.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_INVALID_MGMT_SEQ 0xFFFF + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) +#define WLAN_GET_SEQ_SEQ(seq) \ + (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) + +#define WLAN_FC_TYPE_MGMT 0 +#define WLAN_FC_TYPE_CTRL 1 +#define WLAN_FC_TYPE_DATA 2 + +/* management */ +#define WLAN_FC_STYPE_ASSOC_REQ 0 +#define WLAN_FC_STYPE_ASSOC_RESP 1 +#define WLAN_FC_STYPE_REASSOC_REQ 2 +#define WLAN_FC_STYPE_REASSOC_RESP 3 +#define WLAN_FC_STYPE_PROBE_REQ 4 +#define WLAN_FC_STYPE_PROBE_RESP 5 +#define WLAN_FC_STYPE_BEACON 8 +#define WLAN_FC_STYPE_ATIM 9 +#define WLAN_FC_STYPE_DISASSOC 10 +#define WLAN_FC_STYPE_AUTH 11 +#define WLAN_FC_STYPE_DEAUTH 12 +#define WLAN_FC_STYPE_ACTION 13 + +/* control */ +#define WLAN_FC_STYPE_PSPOLL 10 +#define WLAN_FC_STYPE_RTS 11 +#define WLAN_FC_STYPE_CTS 12 +#define WLAN_FC_STYPE_ACK 13 +#define WLAN_FC_STYPE_CFEND 14 +#define WLAN_FC_STYPE_CFENDACK 15 + +/* data */ +#define WLAN_FC_STYPE_DATA 0 +#define WLAN_FC_STYPE_DATA_CFACK 1 +#define WLAN_FC_STYPE_DATA_CFPOLL 2 +#define WLAN_FC_STYPE_DATA_CFACKPOLL 3 +#define WLAN_FC_STYPE_NULLFUNC 4 +#define WLAN_FC_STYPE_CFACK 5 +#define WLAN_FC_STYPE_CFPOLL 6 +#define WLAN_FC_STYPE_CFACKPOLL 7 +#define WLAN_FC_STYPE_QOS_DATA 8 +#define WLAN_FC_STYPE_QOS_DATA_CFACK 9 +#define WLAN_FC_STYPE_QOS_DATA_CFPOLL 10 +#define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL 11 +#define WLAN_FC_STYPE_QOS_NULL 12 +#define WLAN_FC_STYPE_QOS_CFPOLL 14 +#define WLAN_FC_STYPE_QOS_CFACKPOLL 15 + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 +#define WLAN_AUTH_FT 2 +#define WLAN_AUTH_SAE 3 +#define WLAN_AUTH_LEAP 128 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_ESS BIT(0) +#define WLAN_CAPABILITY_IBSS BIT(1) +#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) +#define WLAN_CAPABILITY_PRIVACY BIT(4) +#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5) +#define WLAN_CAPABILITY_PBCC BIT(6) +#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7) +#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8) +#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10) +#define WLAN_CAPABILITY_DSSS_OFDM BIT(13) + +/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */ +#define WLAN_STATUS_SUCCESS 0 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2 +#define WLAN_STATUS_TDLS_WAKEUP_REJECT 3 +#define WLAN_STATUS_SECURITY_DISABLED 5 +#define WLAN_STATUS_UNACCEPTABLE_LIFETIME 6 +#define WLAN_STATUS_NOT_IN_SAME_BSS 7 +#define WLAN_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 +#define WLAN_STATUS_CHALLENGE_FAIL 15 +#define WLAN_STATUS_AUTH_TIMEOUT 16 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18 +/* IEEE 802.11b */ +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 +/* IEEE 802.11h */ +#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22 +#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23 +#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24 +/* IEEE 802.11g */ +#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25 +#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26 +#define WLAN_STATUS_ASSOC_DENIED_NO_HT 27 +#define WLAN_STATUS_R0KH_UNREACHABLE 28 +#define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29 +/* IEEE 802.11w */ +#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30 +#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31 +#define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32 +#define WLAN_STATUS_REQUEST_DECLINED 37 +#define WLAN_STATUS_INVALID_PARAMETERS 38 +/* IEEE 802.11i */ +#define WLAN_STATUS_INVALID_IE 40 +#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 +#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 +#define WLAN_STATUS_AKMP_NOT_VALID 43 +#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 +#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 +#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 +#define WLAN_STATUS_TS_NOT_CREATED 47 +#define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48 +#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49 +#define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50 +#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51 +/* IEEE 802.11r */ +#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52 +#define WLAN_STATUS_INVALID_PMKID 53 +#define WLAN_STATUS_INVALID_MDIE 54 +#define WLAN_STATUS_INVALID_FTIE 55 +#define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59 +#define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60 +#define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61 +#define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62 +#define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63 +#define WLAN_STATUS_REQ_REFUSED_HOME 64 +#define WLAN_STATUS_ADV_SRV_UNREACHABLE 65 +#define WLAN_STATUS_REQ_REFUSED_SSPN 67 +#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68 +#define WLAN_STATUS_INVALID_RSNIE 72 +#define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76 +#define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77 +#define WLAN_STATUS_TRANSMISSION_FAILURE 79 +#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82 +#define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86 +#define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95 +#define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99 +#define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104 + +/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ +#define WLAN_REASON_UNSPECIFIED 1 +#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 +/* IEEE 802.11s */ +#define WLAN_REASON_MESH_PEERING_CANCELLED 52 +#define WLAN_REASON_MESH_MAX_PEERS 53 +#define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54 +#define WLAN_REASON_MESH_CLOSE_RCVD 55 +#define WLAN_REASON_MESH_MAX_RETRIES 56 +#define WLAN_REASON_MESH_CONFIRM_TIMEOUT 57 +#define WLAN_REASON_MESH_INVALID_GTK 58 +#define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59 +#define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60 + + +/* Information Element IDs */ +#define WLAN_EID_SSID 0 +#define WLAN_EID_SUPP_RATES 1 +#define WLAN_EID_FH_PARAMS 2 +#define WLAN_EID_DS_PARAMS 3 +#define WLAN_EID_CF_PARAMS 4 +#define WLAN_EID_TIM 5 +#define WLAN_EID_IBSS_PARAMS 6 +#define WLAN_EID_COUNTRY 7 +#define WLAN_EID_BSS_LOAD 11 +#define WLAN_EID_CHALLENGE 16 +/* EIDs defined by IEEE 802.11h - START */ +#define WLAN_EID_PWR_CONSTRAINT 32 +#define WLAN_EID_PWR_CAPABILITY 33 +#define WLAN_EID_TPC_REQUEST 34 +#define WLAN_EID_TPC_REPORT 35 +#define WLAN_EID_SUPPORTED_CHANNELS 36 +#define WLAN_EID_CHANNEL_SWITCH 37 +#define WLAN_EID_MEASURE_REQUEST 38 +#define WLAN_EID_MEASURE_REPORT 39 +#define WLAN_EID_QUITE 40 +#define WLAN_EID_IBSS_DFS 41 +/* EIDs defined by IEEE 802.11h - END */ +#define WLAN_EID_ERP_INFO 42 +#define WLAN_EID_HT_CAP 45 +#define WLAN_EID_QOS 46 +#define WLAN_EID_RSN 48 +#define WLAN_EID_EXT_SUPP_RATES 50 +#define WLAN_EID_NEIGHBOR_REPORT 52 +#define WLAN_EID_MOBILITY_DOMAIN 54 +#define WLAN_EID_FAST_BSS_TRANSITION 55 +#define WLAN_EID_TIMEOUT_INTERVAL 56 +#define WLAN_EID_RIC_DATA 57 +#define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59 +#define WLAN_EID_HT_OPERATION 61 +#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 +#define WLAN_EID_WAPI 68 +#define WLAN_EID_TIME_ADVERTISEMENT 69 +#define WLAN_EID_RRM_ENABLED_CAPABILITIES 70 +#define WLAN_EID_20_40_BSS_COEXISTENCE 72 +#define WLAN_EID_20_40_BSS_INTOLERANT 73 +#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 +#define WLAN_EID_MMIE 76 +#define WLAN_EID_SSID_LIST 84 +#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90 +#define WLAN_EID_TFS_REQ 91 +#define WLAN_EID_TFS_RESP 92 +#define WLAN_EID_WNMSLEEP 93 +#define WLAN_EID_TIME_ZONE 98 +#define WLAN_EID_LINK_ID 101 +#define WLAN_EID_INTERWORKING 107 +#define WLAN_EID_ADV_PROTO 108 +#define WLAN_EID_QOS_MAP_SET 110 +#define WLAN_EID_ROAMING_CONSORTIUM 111 +#define WLAN_EID_MESH_CONFIG 113 +#define WLAN_EID_MESH_ID 114 +#define WLAN_EID_PEER_MGMT 117 +#define WLAN_EID_EXT_CAPAB 127 +#define WLAN_EID_AMPE 139 +#define WLAN_EID_MIC 140 +#define WLAN_EID_CCKM 156 +#define WLAN_EID_MULTI_BAND 158 +#define WLAN_EID_SESSION_TRANSITION 164 +#define WLAN_EID_VHT_CAP 191 +#define WLAN_EID_VHT_OPERATION 192 +#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193 +#define WLAN_EID_VHT_WIDE_BW_CHSWITCH 194 +#define WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE 195 +#define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196 +#define WLAN_EID_VHT_AID 197 +#define WLAN_EID_VHT_QUIET_CHANNEL 198 +#define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199 +#define WLAN_EID_VENDOR_SPECIFIC 221 + + +/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */ +#define WLAN_ACTION_SPECTRUM_MGMT 0 +#define WLAN_ACTION_QOS 1 +#define WLAN_ACTION_DLS 2 +#define WLAN_ACTION_BLOCK_ACK 3 +#define WLAN_ACTION_PUBLIC 4 +#define WLAN_ACTION_RADIO_MEASUREMENT 5 +#define WLAN_ACTION_FT 6 +#define WLAN_ACTION_HT 7 +#define WLAN_ACTION_SA_QUERY 8 +#define WLAN_ACTION_PROTECTED_DUAL 9 +#define WLAN_ACTION_WNM 10 +#define WLAN_ACTION_UNPROTECTED_WNM 11 +#define WLAN_ACTION_TDLS 12 +#define WLAN_ACTION_SELF_PROTECTED 15 +#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ +#define WLAN_ACTION_FST 18 +#define WLAN_ACTION_VENDOR_SPECIFIC 127 + +/* Public action codes */ +#define WLAN_PA_20_40_BSS_COEX 0 +#define WLAN_PA_VENDOR_SPECIFIC 9 +#define WLAN_PA_GAS_INITIAL_REQ 10 +#define WLAN_PA_GAS_INITIAL_RESP 11 +#define WLAN_PA_GAS_COMEBACK_REQ 12 +#define WLAN_PA_GAS_COMEBACK_RESP 13 +#define WLAN_TDLS_DISCOVERY_RESPONSE 14 + +/* Protected Dual of Public Action frames */ +#define WLAN_PROT_DSE_ENABLEMENT 1 +#define WLAN_PROT_DSE_DEENABLEMENT 2 +#define WLAN_PROT_EXT_CSA 4 +#define WLAN_PROT_MEASUREMENT_REQ 5 +#define WLAN_PROT_MEASUREMENT_REPORT 6 +#define WLAN_PROT_DSE_POWER_CONSTRAINT 8 +#define WLAN_PROT_VENDOR_SPECIFIC 9 +#define WLAN_PROT_GAS_INITIAL_REQ 10 +#define WLAN_PROT_GAS_INITIAL_RESP 11 +#define WLAN_PROT_GAS_COMEBACK_REQ 12 +#define WLAN_PROT_GAS_COMEBACK_RESP 13 + +/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */ +#define WLAN_SA_QUERY_REQUEST 0 +#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 + +/* Radio Measurement Action codes */ +#define WLAN_RRM_RADIO_MEASUREMENT_REQUEST 0 +#define WLAN_RRM_RADIO_MEASUREMENT_REPORT 1 +#define WLAN_RRM_LINK_MEASUREMENT_REQUEST 2 +#define WLAN_RRM_LINK_MEASUREMENT_REPORT 3 +#define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4 +#define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5 + +/* Radio Measurement capabilities (from RRM Capabilities IE) */ +/* byte 1 (out of 5) */ +#define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0) +#define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1) + +/* Timeout Interval Type */ +#define WLAN_TIMEOUT_REASSOC_DEADLINE 1 +#define WLAN_TIMEOUT_KEY_LIFETIME 2 +#define WLAN_TIMEOUT_ASSOC_COMEBACK 3 + +/* Interworking element (IEEE 802.11u) - Access Network Options */ +#define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f +#define INTERWORKING_ANO_INTERNET 0x10 +#define INTERWORKING_ANO_ASRA 0x20 +#define INTERWORKING_ANO_ESR 0x40 +#define INTERWORKING_ANO_UESA 0x80 + +#define INTERWORKING_ANT_PRIVATE 0 +#define INTERWORKING_ANT_PRIVATE_WITH_GUEST 1 +#define INTERWORKING_ANT_CHARGEABLE_PUBLIC 2 +#define INTERWORKING_ANT_FREE_PUBLIC 3 +#define INTERWORKING_ANT_PERSONAL_DEVICE 4 +#define INTERWORKING_ANT_EMERGENCY_SERVICES 5 +#define INTERWORKING_ANT_TEST 6 +#define INTERWORKING_ANT_WILDCARD 15 + +/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */ +enum adv_proto_id { + ACCESS_NETWORK_QUERY_PROTOCOL = 0, + MIH_INFO_SERVICE = 1, + MIH_CMD_AND_EVENT_DISCOVERY = 2, + EMERGENCY_ALERT_SYSTEM = 3, + ADV_PROTO_VENDOR_SPECIFIC = 221 +}; + +/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */ +enum anqp_info_id { + ANQP_QUERY_LIST = 256, + ANQP_CAPABILITY_LIST = 257, + ANQP_VENUE_NAME = 258, + ANQP_EMERGENCY_CALL_NUMBER = 259, + ANQP_NETWORK_AUTH_TYPE = 260, + ANQP_ROAMING_CONSORTIUM = 261, + ANQP_IP_ADDR_TYPE_AVAILABILITY = 262, + ANQP_NAI_REALM = 263, + ANQP_3GPP_CELLULAR_NETWORK = 264, + ANQP_AP_GEOSPATIAL_LOCATION = 265, + ANQP_AP_CIVIC_LOCATION = 266, + ANQP_AP_LOCATION_PUBLIC_URI = 267, + ANQP_DOMAIN_NAME = 268, + ANQP_EMERGENCY_ALERT_URI = 269, + ANQP_EMERGENCY_NAI = 271, + ANQP_VENDOR_SPECIFIC = 56797 +}; + +/* NAI Realm list - EAP Method subfield - Authentication Parameter ID */ +enum nai_realm_eap_auth_param { + NAI_REALM_EAP_AUTH_EXPANDED_EAP_METHOD = 1, + NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH = 2, + NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD = 3, + NAI_REALM_EAP_AUTH_EXPANDED_INNER_EAP_METHOD = 4, + NAI_REALM_EAP_AUTH_CRED_TYPE = 5, + NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE = 6, + NAI_REALM_EAP_AUTH_VENDOR_SPECIFIC = 221 +}; + +enum nai_realm_eap_auth_inner_non_eap { + NAI_REALM_INNER_NON_EAP_PAP = 1, + NAI_REALM_INNER_NON_EAP_CHAP = 2, + NAI_REALM_INNER_NON_EAP_MSCHAP = 3, + NAI_REALM_INNER_NON_EAP_MSCHAPV2 = 4 +}; + +enum nai_realm_eap_cred_type { + NAI_REALM_CRED_TYPE_SIM = 1, + NAI_REALM_CRED_TYPE_USIM = 2, + NAI_REALM_CRED_TYPE_NFC_SECURE_ELEMENT = 3, + NAI_REALM_CRED_TYPE_HARDWARE_TOKEN = 4, + NAI_REALM_CRED_TYPE_SOFTOKEN = 5, + NAI_REALM_CRED_TYPE_CERTIFICATE = 6, + NAI_REALM_CRED_TYPE_USERNAME_PASSWORD = 7, + NAI_REALM_CRED_TYPE_NONE = 8, + NAI_REALM_CRED_TYPE_ANONYMOUS = 9, + NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10 +}; + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee80211_hdr { + le16 frame_control; + le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + le16 seq_ctrl; + /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame + */ +} STRUCT_PACKED; + +#define IEEE80211_DA_FROMDS addr1 +#define IEEE80211_BSSID_FROMDS addr2 +#define IEEE80211_SA_FROMDS addr3 + +#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr)) + +#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4)) + +struct ieee80211_mgmt { + le16 frame_control; + le16 duration; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + le16 seq_ctrl; + union { + struct { + le16 auth_alg; + le16 auth_transaction; + le16 status_code; + /* possibly followed by Challenge text */ + u8 variable[]; + } STRUCT_PACKED auth; + struct { + le16 reason_code; + u8 variable[]; + } STRUCT_PACKED deauth; + struct { + le16 capab_info; + le16 listen_interval; + /* followed by SSID and Supported rates */ + u8 variable[]; + } STRUCT_PACKED assoc_req; + struct { + le16 capab_info; + le16 status_code; + le16 aid; + /* followed by Supported rates */ + u8 variable[]; + } 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[]; + } STRUCT_PACKED reassoc_req; + struct { + le16 reason_code; + u8 variable[]; + } 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[]; + } 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[]; + } STRUCT_PACKED probe_resp; + struct { + u8 category; + union { + struct { + u8 action_code; + u8 dialog_token; + u8 status_code; + u8 variable[]; + } 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[]; /* 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[]; /* FT Request */ + } STRUCT_PACKED ft_action_resp; + struct { + u8 action; + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + } STRUCT_PACKED sa_query_req; + struct { + u8 action; /* */ + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + } STRUCT_PACKED sa_query_resp; + struct { + u8 action; + u8 dialogtoken; + u8 variable[]; + } STRUCT_PACKED wnm_sleep_req; + struct { + u8 action; + u8 dialogtoken; + le16 keydata_len; + u8 variable[]; + } STRUCT_PACKED wnm_sleep_resp; + struct { + u8 action; + u8 variable[]; + } STRUCT_PACKED public_action; + struct { + u8 action; /* 9 */ + u8 oui[3]; + /* Vendor-specific content */ + u8 variable[]; + } STRUCT_PACKED vs_public_action; + struct { + u8 action; /* 7 */ + u8 dialog_token; + u8 req_mode; + le16 disassoc_timer; + u8 validity_interval; + /* BSS Termination Duration (optional), + * Session Information URL (optional), + * BSS Transition Candidate List + * Entries */ + u8 variable[]; + } STRUCT_PACKED bss_tm_req; + struct { + u8 action; /* 8 */ + u8 dialog_token; + u8 status_code; + u8 bss_termination_delay; + /* Target BSSID (optional), + * BSS Transition Candidate List + * Entries (optional) */ + u8 variable[]; + } STRUCT_PACKED bss_tm_resp; + struct { + u8 action; /* 6 */ + u8 dialog_token; + u8 query_reason; + /* BSS Transition Candidate List + * Entries (optional) */ + u8 variable[]; + } STRUCT_PACKED bss_tm_query; + struct { + u8 action; /* 15 */ + u8 variable[]; + } STRUCT_PACKED slf_prot_action; + struct { + u8 action; + u8 variable[]; + } STRUCT_PACKED fst_action; + } u; + } STRUCT_PACKED action; + } u; +} STRUCT_PACKED; + + +/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */ +#define IEEE80211_HT_MCS_MASK_LEN 10 + +/* HT Capabilities element */ +struct ieee80211_ht_capabilities { + le16 ht_capabilities_info; + u8 a_mpdu_params; /* Maximum A-MPDU Length Exponent B0..B1 + * Minimum MPDU Start Spacing B2..B4 + * Reserved B5..B7 */ + u8 supported_mcs_set[16]; + le16 ht_extended_capabilities; + le32 tx_bf_capability_info; + u8 asel_capabilities; +} STRUCT_PACKED; + + +/* HT Operation element */ +struct ieee80211_ht_operation { + u8 primary_chan; + /* Five octets of HT Operation Information */ + u8 ht_param; /* B0..B7 */ + le16 operation_mode; /* B8..B23 */ + le16 param; /* B24..B39 */ + u8 basic_mcs_set[16]; +} STRUCT_PACKED; + + +struct ieee80211_obss_scan_parameters { + le16 scan_passive_dwell; + le16 scan_active_dwell; + le16 width_trigger_scan_interval; + le16 scan_passive_total_per_channel; + le16 scan_active_total_per_channel; + le16 channel_transition_delay_factor; + le16 scan_activity_threshold; +} STRUCT_PACKED; + + +struct ieee80211_vht_capabilities { + le32 vht_capabilities_info; + struct { + le16 rx_map; + le16 rx_highest; + le16 tx_map; + le16 tx_highest; + } vht_supported_mcs_set; +} STRUCT_PACKED; + +struct ieee80211_vht_operation { + u8 vht_op_info_chwidth; + u8 vht_op_info_chan_center_freq_seg0_idx; + u8 vht_op_info_chan_center_freq_seg1_idx; + le16 vht_basic_mcs_set; +} STRUCT_PACKED; + +struct ieee80211_ampe_ie { + u8 selected_pairwise_suite[4]; + u8 local_nonce[32]; + u8 peer_nonce[32]; + u8 mgtk[16]; + u8 key_rsc[8]; + u8 key_expiration[4]; +} 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 OVERLAPPING_BSS_TRANS_DELAY_FACTOR 5 + +/* HT Capabilities Info field within HT Capabilities element */ +#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)) +/* B13 - Reserved (was PSMP support during P802.11n development) */ +#define HT_CAP_INFO_40MHZ_INTOLERANT ((u16) BIT(14)) +#define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15)) + +/* HT Extended Capabilities field within HT Capabilities element */ +#define EXT_HT_CAP_INFO_PCO ((u16) BIT(0)) +#define EXT_HT_CAP_INFO_PCO_TRANS_TIME_MASK ((u16) (BIT(1) | BIT(2))) +#define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1 +/* B3..B7 - Reserved */ +#define EXT_HT_CAP_INFO_MCS_FEEDBACK_MASK ((u16) (BIT(8) | BIT(9))) +#define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8 +#define EXT_HT_CAP_INFO_HTC_SUPPORT ((u16) BIT(10)) +#define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11)) +/* B12..B15 - Reserved */ + +/* Transmit Beanforming Capabilities within HT Capabilities element */ +#define TX_BF_CAP_IMPLICIT_TXBF_RX_CAP ((u32) BIT(0)) +#define TX_BF_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1)) +#define TX_BF_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2)) +#define TX_BF_CAP_RX_NDP_CAP ((u32) BIT(3)) +#define TX_BF_CAP_TX_NDP_CAP ((u32) BIT(4)) +#define TX_BF_CAP_IMPLICIT_TX_BF_CAP ((u32) BIT(5)) +#define TX_BF_CAP_CALIBRATION_MASK ((u32) (BIT(6) | BIT(7)) +#define TX_BF_CAP_CALIB_OFFSET 6 +#define TX_BF_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8)) +#define TX_BF_CAP_EXPLICIT_NONCOMPR_STEERING_CAP ((u32) BIT(9)) +#define TX_BF_CAP_EXPLICIT_COMPR_STEERING_CAP ((u32) BIT(10)) +#define TX_BF_CAP_EXPLICIT_TX_BF_CSI_FEEDBACK_MASK ((u32) (BIT(10) | BIT(11))) +#define TX_BF_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11 +#define TX_BF_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13 +#define TX_BF_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15 +#define TX_BF_CAP_MINIMAL_GROUPING_OFFSET 17 +#define TX_BF_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19 +#define TX_BF_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21 +#define TX_BF_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23 +#define TX_BF_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25 +#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_MASK ((u32) (BIT(27) | BIT(28))) +#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_OFFSET 27 +/* B29..B31 - Reserved */ + +/* ASEL Capability field within HT Capabilities element */ +#define ASEL_CAP_ASEL_CAPABLE ((u8) BIT(0)) +#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1)) +#define ASEL_CAP_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2)) +#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3)) +#define ASEL_CAP_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4)) +#define ASEL_CAP_RX_AS_CAP ((u8) BIT(5)) +#define ASEL_CAP_TX_SOUNDING_PPDUS_CAP ((u8) BIT(6)) +/* B7 - Reserved */ + +/* First octet of HT Operation Information within HT Operation element */ +#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_STA_CHNL_WIDTH ((u8) BIT(2)) +#define HT_INFO_HT_PARAM_RIFS_MODE ((u8) BIT(3)) +/* B4..B7 - Reserved */ + +/* HT Protection (B8..B9 of HT Operation Information) */ +#define HT_PROT_NO_PROTECTION 0 +#define HT_PROT_NONMEMBER_PROTECTION 1 +#define HT_PROT_20MHZ_PROTECTION 2 +#define HT_PROT_NON_HT_MIXED 3 +/* Bits within ieee80211_ht_operation::operation_mode (BIT(0) maps to B8 in + * HT Operation Information) */ +#define HT_OPER_OP_MODE_HT_PROT_MASK ((u16) (BIT(0) | BIT(1))) /* B8..B9 */ +#define HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT ((u16) BIT(2)) /* B10 */ +/* BIT(3), i.e., B11 in HT Operation Information field - Reserved */ +#define HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT ((u16) BIT(4)) /* B12 */ +/* BIT(5)..BIT(15), i.e., B13..B23 - Reserved */ + +/* Last two octets of HT Operation Information (BIT(0) = B24) */ +/* B24..B29 - Reserved */ +#define HT_OPER_PARAM_DUAL_BEACON ((u16) BIT(6)) +#define HT_OPER_PARAM_DUAL_CTS_PROTECTION ((u16) BIT(7)) +#define HT_OPER_PARAM_STBC_BEACON ((u16) BIT(8)) +#define HT_OPER_PARAM_LSIG_TXOP_PROT_FULL_SUPP ((u16) BIT(9)) +#define HT_OPER_PARAM_PCO_ACTIVE ((u16) BIT(10)) +#define HT_OPER_PARAM_PCO_PHASE ((u16) BIT(11)) +/* B36..B39 - Reserved */ + +#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126 +#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 + +/* VHT Defines */ +#define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0)) +#define VHT_CAP_MAX_MPDU_LENGTH_11454 ((u32) BIT(1)) +#define VHT_CAP_MAX_MPDU_LENGTH_MASK ((u32) BIT(0) | BIT(1)) +#define VHT_CAP_MAX_MPDU_LENGTH_MASK_SHIFT 0 +#define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2)) +#define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3)) +#define VHT_CAP_SUPP_CHAN_WIDTH_MASK ((u32) BIT(2) | BIT(3)) +#define VHT_CAP_RXLDPC ((u32) BIT(4)) +#define VHT_CAP_SHORT_GI_80 ((u32) BIT(5)) +#define VHT_CAP_SHORT_GI_160 ((u32) BIT(6)) +#define VHT_CAP_TXSTBC ((u32) BIT(7)) +#define VHT_CAP_RXSTBC_1 ((u32) BIT(8)) +#define VHT_CAP_RXSTBC_2 ((u32) BIT(9)) +#define VHT_CAP_RXSTBC_3 ((u32) BIT(8) | BIT(9)) +#define VHT_CAP_RXSTBC_4 ((u32) BIT(10)) +#define VHT_CAP_RXSTBC_MASK ((u32) BIT(8) | BIT(9) | \ + BIT(10)) +#define VHT_CAP_RXSTBC_MASK_SHIFT 8 +#define VHT_CAP_SU_BEAMFORMER_CAPABLE ((u32) BIT(11)) +#define VHT_CAP_SU_BEAMFORMEE_CAPABLE ((u32) BIT(12)) +#define VHT_CAP_BEAMFORMEE_STS_MAX ((u32) BIT(13) | \ + BIT(14) | BIT(15)) +#define VHT_CAP_BEAMFORMEE_STS_MAX_SHIFT 13 +#define VHT_CAP_BEAMFORMEE_STS_OFFSET 13 +#define VHT_CAP_SOUNDING_DIMENSION_MAX ((u32) BIT(16) | \ + BIT(17) | BIT(18)) +#define VHT_CAP_SOUNDING_DIMENSION_MAX_SHIFT 16 +#define VHT_CAP_SOUNDING_DIMENSION_OFFSET 16 +#define VHT_CAP_MU_BEAMFORMER_CAPABLE ((u32) BIT(19)) +#define VHT_CAP_MU_BEAMFORMEE_CAPABLE ((u32) BIT(20)) +#define VHT_CAP_VHT_TXOP_PS ((u32) BIT(21)) +#define VHT_CAP_HTC_VHT ((u32) BIT(22)) + +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1 ((u32) BIT(23)) +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2 ((u32) BIT(24)) +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3 ((u32) BIT(23) | BIT(24)) +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4 ((u32) BIT(25)) +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5 ((u32) BIT(23) | BIT(25)) +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6 ((u32) BIT(24) | BIT(25)) +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX ((u32) BIT(23) | \ + BIT(24) | BIT(25)) +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT 23 +#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB ((u32) BIT(27)) +#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27)) +#define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28)) +#define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29)) + +#define VHT_OPMODE_CHANNEL_WIDTH_MASK ((u8) BIT(0) | BIT(1)) +#define VHT_OPMODE_CHANNEL_RxNSS_MASK ((u8) BIT(4) | BIT(5) | \ + BIT(6)) +#define VHT_OPMODE_NOTIF_RX_NSS_SHIFT 4 + +#define VHT_RX_NSS_MAX_STREAMS 8 + +/* VHT channel widths */ +#define VHT_CHANWIDTH_USE_HT 0 +#define VHT_CHANWIDTH_80MHZ 1 +#define VHT_CHANWIDTH_160MHZ 2 +#define VHT_CHANWIDTH_80P80MHZ 3 + +#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) + * 00:50:F2 */ +#define WPA_IE_VENDOR_TYPE 0x0050f201 +#define WMM_IE_VENDOR_TYPE 0x0050f202 +#define WPS_IE_VENDOR_TYPE 0x0050f204 +#define OUI_WFA 0x506f9a +#define P2P_IE_VENDOR_TYPE 0x506f9a09 +#define WFD_IE_VENDOR_TYPE 0x506f9a0a +#define WFD_OUI_TYPE 10 +#define HS20_IE_VENDOR_TYPE 0x506f9a10 +#define OSEN_IE_VENDOR_TYPE 0x506f9a12 + +#define WMM_OUI_TYPE 2 +#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 +#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1 +#define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2 +#define WMM_VERSION 1 + +#define WMM_ACTION_CODE_ADDTS_REQ 0 +#define WMM_ACTION_CODE_ADDTS_RESP 1 +#define WMM_ACTION_CODE_DELTS 2 + +#define WMM_ADDTS_STATUS_ADMISSION_ACCEPTED 0 +#define WMM_ADDTS_STATUS_INVALID_PARAMETERS 1 +/* 2 - Reserved */ +#define WMM_ADDTS_STATUS_REFUSED 3 +/* 4-255 - Reserved */ + +/* WMM TSPEC Direction Field Values */ +#define WMM_TSPEC_DIRECTION_UPLINK 0 +#define WMM_TSPEC_DIRECTION_DOWNLINK 1 +/* 2 - Reserved */ +#define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3 + +/* + * WMM Information Element (used in (Re)Association Request frames; may also be + * used in Beacon frames) + */ +struct wmm_information_element { + /* Element ID: 221 (0xdd); Length: 7 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 0 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specific QoS info */ + +} STRUCT_PACKED; + +#define WMM_QOSINFO_AP_UAPSD 0x80 + +#define WMM_QOSINFO_STA_AC_MASK 0x0f +#define WMM_QOSINFO_STA_SP_MASK 0x03 +#define WMM_QOSINFO_STA_SP_SHIFT 5 + +#define WMM_AC_AIFSN_MASK 0x0f +#define WMM_AC_AIFNS_SHIFT 0 +#define WMM_AC_ACM 0x10 +#define WMM_AC_ACI_MASK 0x60 +#define WMM_AC_ACI_SHIFT 5 + +#define WMM_AC_ECWMIN_MASK 0x0f +#define WMM_AC_ECWMIN_SHIFT 0 +#define WMM_AC_ECWMAX_MASK 0xf0 +#define WMM_AC_ECWMAX_SHIFT 4 + +struct wmm_ac_parameter { + u8 aci_aifsn; /* AIFSN, ACM, ACI */ + u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */ + le16 txop_limit; +} STRUCT_PACKED; + +/* + * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association + * Response frmaes) + */ +struct wmm_parameter_element { + /* Element ID: 221 (0xdd); Length: 24 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 1 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specific QoS info */ + u8 reserved; /* 0 */ + struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */ + +} STRUCT_PACKED; + +/* WMM TSPEC Element */ +struct wmm_tspec_element { + u8 eid; /* 221 = 0xdd */ + u8 length; /* 6 + 55 = 61 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 2 */ + u8 version; /* 1 */ + /* WMM TSPEC body (55 octets): */ + u8 ts_info[3]; + le16 nominal_msdu_size; + le16 maximum_msdu_size; + le32 minimum_service_interval; + le32 maximum_service_interval; + le32 inactivity_interval; + le32 suspension_interval; + le32 service_start_time; + le32 minimum_data_rate; + le32 mean_data_rate; + le32 peak_data_rate; + le32 maximum_burst_size; + le32 delay_bound; + le32 minimum_phy_rate; + le16 surplus_bandwidth_allowance; + le16 medium_time; +} STRUCT_PACKED; + + +/* Access Categories / ACI to AC coding */ +enum wmm_ac { + WMM_AC_BE = 0 /* Best Effort */, + WMM_AC_BK = 1 /* Background */, + WMM_AC_VI = 2 /* Video */, + WMM_AC_VO = 3 /* Voice */, + WMM_AC_NUM = 4 +}; + + +#define HS20_INDICATION_OUI_TYPE 16 +#define HS20_ANQP_OUI_TYPE 17 +#define HS20_OSEN_OUI_TYPE 18 +#define HS20_STYPE_QUERY_LIST 1 +#define HS20_STYPE_CAPABILITY_LIST 2 +#define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3 +#define HS20_STYPE_WAN_METRICS 4 +#define HS20_STYPE_CONNECTION_CAPABILITY 5 +#define HS20_STYPE_NAI_HOME_REALM_QUERY 6 +#define HS20_STYPE_OPERATING_CLASS 7 +#define HS20_STYPE_OSU_PROVIDERS_LIST 8 +#define HS20_STYPE_ICON_REQUEST 10 +#define HS20_STYPE_ICON_BINARY_FILE 11 + +#define HS20_DGAF_DISABLED 0x01 +#define HS20_PPS_MO_ID_PRESENT 0x02 +#define HS20_ANQP_DOMAIN_ID_PRESENT 0x04 +#define HS20_VERSION 0x10 /* Release 2 */ + +/* WNM-Notification WFA vendors specific subtypes */ +#define HS20_WNM_SUB_REM_NEEDED 0 +#define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1 + +#define HS20_DEAUTH_REASON_CODE_BSS 0 +#define HS20_DEAUTH_REASON_CODE_ESS 1 + +/* Wi-Fi Direct (P2P) */ + +#define P2P_OUI_TYPE 9 + +enum p2p_attr_id { + P2P_ATTR_STATUS = 0, + P2P_ATTR_MINOR_REASON_CODE = 1, + P2P_ATTR_CAPABILITY = 2, + P2P_ATTR_DEVICE_ID = 3, + P2P_ATTR_GROUP_OWNER_INTENT = 4, + P2P_ATTR_CONFIGURATION_TIMEOUT = 5, + P2P_ATTR_LISTEN_CHANNEL = 6, + P2P_ATTR_GROUP_BSSID = 7, + P2P_ATTR_EXT_LISTEN_TIMING = 8, + P2P_ATTR_INTENDED_INTERFACE_ADDR = 9, + P2P_ATTR_MANAGEABILITY = 10, + P2P_ATTR_CHANNEL_LIST = 11, + P2P_ATTR_NOTICE_OF_ABSENCE = 12, + P2P_ATTR_DEVICE_INFO = 13, + P2P_ATTR_GROUP_INFO = 14, + P2P_ATTR_GROUP_ID = 15, + P2P_ATTR_INTERFACE = 16, + P2P_ATTR_OPERATING_CHANNEL = 17, + P2P_ATTR_INVITATION_FLAGS = 18, + P2P_ATTR_OOB_GO_NEG_CHANNEL = 19, + P2P_ATTR_SERVICE_HASH = 21, + P2P_ATTR_SESSION_INFORMATION_DATA = 22, + P2P_ATTR_CONNECTION_CAPABILITY = 23, + P2P_ATTR_ADVERTISEMENT_ID = 24, + P2P_ATTR_ADVERTISED_SERVICE = 25, + P2P_ATTR_SESSION_ID = 26, + P2P_ATTR_FEATURE_CAPABILITY = 27, + P2P_ATTR_PERSISTENT_GROUP = 28, + P2P_ATTR_VENDOR_SPECIFIC = 221 +}; + +#define P2P_MAX_GO_INTENT 15 + +/* P2P Capability - Device Capability bitmap */ +#define P2P_DEV_CAPAB_SERVICE_DISCOVERY BIT(0) +#define P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY BIT(1) +#define P2P_DEV_CAPAB_CONCURRENT_OPER BIT(2) +#define P2P_DEV_CAPAB_INFRA_MANAGED BIT(3) +#define P2P_DEV_CAPAB_DEVICE_LIMIT BIT(4) +#define P2P_DEV_CAPAB_INVITATION_PROCEDURE BIT(5) + +/* P2P Capability - Group Capability bitmap */ +#define P2P_GROUP_CAPAB_GROUP_OWNER BIT(0) +#define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1) +#define P2P_GROUP_CAPAB_GROUP_LIMIT BIT(2) +#define P2P_GROUP_CAPAB_INTRA_BSS_DIST BIT(3) +#define P2P_GROUP_CAPAB_CROSS_CONN BIT(4) +#define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5) +#define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6) +#define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7) + +/* P2PS Coordination Protocol Transport Bitmap */ +#define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0) +#define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1) + +struct p2ps_feature_capab { + u8 cpt; + u8 reserved; +} STRUCT_PACKED; + +/* 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, + P2P_SC_SUCCESS_DEFERRED = 12, +}; + +enum p2p_role_indication { + P2P_DEVICE_NOT_IN_GROUP = 0x00, + P2P_CLIENT_IN_A_GROUP = 0x01, + P2P_GO_IN_A_GROUP = 0x02, +}; + +#define P2P_WILDCARD_SSID "DIRECT-" +#define P2P_WILDCARD_SSID_LEN 7 + +/* P2P action frames */ +enum p2p_act_frame_type { + P2P_NOA = 0, + P2P_PRESENCE_REQ = 1, + P2P_PRESENCE_RESP = 2, + P2P_GO_DISC_REQ = 3 +}; + +/* P2P public action frames */ +enum p2p_action_frame_type { + P2P_GO_NEG_REQ = 0, + P2P_GO_NEG_RESP = 1, + P2P_GO_NEG_CONF = 2, + P2P_INVITATION_REQ = 3, + P2P_INVITATION_RESP = 4, + P2P_DEV_DISC_REQ = 5, + P2P_DEV_DISC_RESP = 6, + P2P_PROV_DISC_REQ = 7, + P2P_PROV_DISC_RESP = 8 +}; + +enum p2p_service_protocol_type { + P2P_SERV_ALL_SERVICES = 0, + P2P_SERV_BONJOUR = 1, + P2P_SERV_UPNP = 2, + P2P_SERV_WS_DISCOVERY = 3, + P2P_SERV_WIFI_DISPLAY = 4, + P2P_SERV_P2PS = 11, + P2P_SERV_VENDOR_SPECIFIC = 255 +}; + +enum p2p_sd_status { + P2P_SD_SUCCESS = 0, + P2P_SD_PROTO_NOT_AVAILABLE = 1, + P2P_SD_REQUESTED_INFO_NOT_AVAILABLE = 2, + P2P_SD_BAD_REQUEST = 3 +}; + + +enum wifi_display_subelem { + WFD_SUBELEM_DEVICE_INFO = 0, + WFD_SUBELEM_ASSOCIATED_BSSID = 1, + WFD_SUBELEM_AUDIO_FORMATS = 2, + WFD_SUBELEM_VIDEO_FORMATS = 3, + WFD_SUBELEM_3D_VIDEO_FORMATS = 4, + WFD_SUBELEM_CONTENT_PROTECTION = 5, + WFD_SUBELEM_COUPLED_SINK = 6, + WFD_SUBELEM_EXT_CAPAB = 7, + WFD_SUBELEM_LOCAL_IP_ADDRESS = 8, + WFD_SUBELEM_SESSION_INFO = 9 +}; + +/* 802.11s */ +#define MESH_SYNC_METHOD_NEIGHBOR_OFFSET 1 +#define MESH_SYNC_METHOD_VENDOR 255 +#define MESH_PATH_PROTOCOL_HWMP 1 +#define MESH_PATH_PROTOCOL_VENDOR 255 +#define MESH_PATH_METRIC_AIRTIME 1 +#define MESH_PATH_METRIC_VENDOR 255 + +enum plink_action_field { + PLINK_OPEN = 1, + PLINK_CONFIRM, + PLINK_CLOSE +}; + +#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ +#define VENDOR_VHT_TYPE 0x04 +#define VENDOR_VHT_SUBTYPE 0x08 +#define VENDOR_VHT_SUBTYPE2 0x00 + +#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ + +/* cipher suite selectors */ +#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 +#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 +#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 +/* reserved: 0x000FAC03 */ +#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 +#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 +#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 +#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR 0x000FAC07 +#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 +#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09 +#define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A +#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B +#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C +#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D + +#define WLAN_CIPHER_SUITE_SMS4 0x00147201 + +#define WLAN_CIPHER_SUITE_CKIP 0x00409600 +#define WLAN_CIPHER_SUITE_CKIP_CMIC 0x00409601 +#define WLAN_CIPHER_SUITE_CMIC 0x00409602 +#define WLAN_CIPHER_SUITE_KRK 0x004096FF /* for nl80211 use only */ + +/* AKM suite selectors */ +#define WLAN_AKM_SUITE_8021X 0x000FAC01 +#define WLAN_AKM_SUITE_PSK 0x000FAC02 +#define WLAN_AKM_SUITE_FT_8021X 0x000FAC03 +#define WLAN_AKM_SUITE_FT_PSK 0x000FAC04 +#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05 +#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06 +#define WLAN_AKM_SUITE_8021X_SUITE_B 0x000FAC11 +#define WLAN_AKM_SUITE_8021X_SUITE_B_192 0x000FAC12 +#define WLAN_AKM_SUITE_CCKM 0x00409600 +#define WLAN_AKM_SUITE_OSEN 0x506f9a01 + + +/* IEEE 802.11v - WNM Action field values */ +enum wnm_action { + WNM_EVENT_REQ = 0, + WNM_EVENT_REPORT = 1, + WNM_DIAGNOSTIC_REQ = 2, + WNM_DIAGNOSTIC_REPORT = 3, + WNM_LOCATION_CFG_REQ = 4, + WNM_LOCATION_CFG_RESP = 5, + WNM_BSS_TRANS_MGMT_QUERY = 6, + WNM_BSS_TRANS_MGMT_REQ = 7, + WNM_BSS_TRANS_MGMT_RESP = 8, + WNM_FMS_REQ = 9, + WNM_FMS_RESP = 10, + WNM_COLLOCATED_INTERFERENCE_REQ = 11, + WNM_COLLOCATED_INTERFERENCE_REPORT = 12, + WNM_TFS_REQ = 13, + WNM_TFS_RESP = 14, + WNM_TFS_NOTIFY = 15, + WNM_SLEEP_MODE_REQ = 16, + WNM_SLEEP_MODE_RESP = 17, + WNM_TIM_BROADCAST_REQ = 18, + WNM_TIM_BROADCAST_RESP = 19, + WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20, + WNM_CHANNEL_USAGE_REQ = 21, + WNM_CHANNEL_USAGE_RESP = 22, + WNM_DMS_REQ = 23, + WNM_DMS_RESP = 24, + WNM_TIMING_MEASUREMENT_REQ = 25, + WNM_NOTIFICATION_REQ = 26, + WNM_NOTIFICATION_RESP = 27 +}; + +/* IEEE 802.11v - BSS Transition Management Request - Request Mode */ +#define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0) +#define WNM_BSS_TM_REQ_ABRIDGED BIT(1) +#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2) +#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3) +#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4) + +/* IEEE Std 802.11-2012 - Table 8-253 */ +enum bss_trans_mgmt_status_code { + WNM_BSS_TM_ACCEPT = 0, + WNM_BSS_TM_REJECT_UNSPECIFIED = 1, + WNM_BSS_TM_REJECT_INSUFFICIENT_BEACON = 2, + WNM_BSS_TM_REJECT_INSUFFICIENT_CAPABITY = 3, + WNM_BSS_TM_REJECT_UNDESIRED = 4, + WNM_BSS_TM_REJECT_DELAY_REQUEST = 5, + WNM_BSS_TM_REJECT_STA_CANDIDATE_LIST_PROVIDED = 6, + WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES = 7, + WNM_BSS_TM_REJECT_LEAVING_ESS = 8 +}; + +#define WNM_NEIGHBOR_TSF 1 +#define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2 +#define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3 +#define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4 +#define WNM_NEIGHBOR_BEARING 5 +#define WNM_NEIGHBOR_MEASUREMENT_PILOT 66 +#define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70 +#define WNM_NEIGHBOR_MULTIPLE_BSSID 71 + +/* QoS action */ +enum qos_action { + QOS_ADDTS_REQ = 0, + QOS_ADDTS_RESP = 1, + QOS_DELTS = 2, + QOS_SCHEDULE = 3, + QOS_QOS_MAP_CONFIG = 4, +}; + +/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */ +#define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0) +#define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1) +#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ BIT(2) +#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ BIT(3) +#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT BIT(4) + +struct ieee80211_2040_bss_coex_ie { + u8 element_id; + u8 length; + u8 coex_param; +} STRUCT_PACKED; + +struct ieee80211_2040_intol_chan_report { + u8 element_id; + u8 length; + u8 op_class; + u8 variable[0]; /* Channel List */ +} STRUCT_PACKED; + +/* IEEE 802.11v - WNM-Sleep Mode element */ +struct wnm_sleep_element { + u8 eid; /* WLAN_EID_WNMSLEEP */ + u8 len; + u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */ + u8 status; + le16 intval; +} STRUCT_PACKED; + +#define WNM_SLEEP_MODE_ENTER 0 +#define WNM_SLEEP_MODE_EXIT 1 + +enum wnm_sleep_mode_response_status { + WNM_STATUS_SLEEP_ACCEPT = 0, + WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1, + WNM_STATUS_DENIED_ACTION = 2, + WNM_STATUS_DENIED_TMP = 3, + WNM_STATUS_DENIED_KEY = 4, + WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5 +}; + +/* WNM-Sleep Mode subelement IDs */ +enum wnm_sleep_mode_subelement_id { + WNM_SLEEP_SUBELEM_GTK = 0, + WNM_SLEEP_SUBELEM_IGTK = 1 +}; + +/* Channel Switch modes (802.11h) */ +#define CHAN_SWITCH_MODE_ALLOW_TX 0 +#define CHAN_SWITCH_MODE_BLOCK_TX 1 + +struct tpc_report { + u8 eid; + u8 len; + u8 tx_power; + u8 link_margin; +} STRUCT_PACKED; + +/* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */ +struct rrm_link_measurement_request { + u8 dialog_token; + s8 tx_power; + s8 max_tp; + u8 variable[0]; +} STRUCT_PACKED; + +/* IEEE Std 802.11-2012, 8.5.7.5 - Link Measurement Report frame format */ +struct rrm_link_measurement_report { + u8 dialog_token; + struct tpc_report tpc; + u8 rx_ant_id; + u8 tx_ant_id; + u8 rcpi; + u8 rsni; + u8 variable[0]; +} STRUCT_PACKED; + +#define SSID_MAX_LEN 32 + +/* IEEE Std 802.11ad-2012 - Multi-band element */ +struct multi_band_ie { + u8 eid; /* WLAN_EID_MULTI_BAND */ + u8 len; + u8 mb_ctrl; + u8 band_id; + u8 op_class; + u8 chan; + u8 bssid[ETH_ALEN]; + le16 beacon_int; + u8 tsf_offs[8]; + u8 mb_connection_capability; + u8 fst_session_tmout; + /* Optional: + * STA MAC Address + * Pairwise Cipher Suite Count + * Pairwise Cipher Suite List + */ + u8 variable[0]; +} STRUCT_PACKED; + +enum mb_ctrl_sta_role { + MB_STA_ROLE_AP = 0, + MB_STA_ROLE_TDLS_STA = 1, + MB_STA_ROLE_IBSS_STA = 2, + MB_STA_ROLE_PCP = 3, + MB_STA_ROLE_NON_PCP_NON_AP = 4 +}; + +#define MB_CTRL_ROLE_MASK (BIT(0) | BIT(1) | BIT(2)) +#define MB_CTRL_ROLE(ctrl) ((u8) ((ctrl) & MB_CTRL_ROLE_MASK)) +#define MB_CTRL_STA_MAC_PRESENT ((u8) (BIT(3))) +#define MB_CTRL_PAIRWISE_CIPHER_SUITE_PRESENT ((u8) (BIT(4))) + +enum mb_band_id { + MB_BAND_ID_WIFI_2_4GHZ = 2, /* 2.4 GHz */ + MB_BAND_ID_WIFI_5GHZ = 4, /* 4.9 and 5 GHz */ + MB_BAND_ID_WIFI_60GHZ = 5, /* 60 GHz */ +}; + +#define MB_CONNECTION_CAPABILITY_AP ((u8) (BIT(0))) +#define MB_CONNECTION_CAPABILITY_PCP ((u8) (BIT(1))) +#define MB_CONNECTION_CAPABILITY_DLS ((u8) (BIT(2))) +#define MB_CONNECTION_CAPABILITY_TDLS ((u8) (BIT(3))) +#define MB_CONNECTION_CAPABILITY_IBSS ((u8) (BIT(4))) + +/* IEEE Std 802.11ad-2014 - FST Action field */ +enum fst_action { + FST_ACTION_SETUP_REQUEST = 0, + FST_ACTION_SETUP_RESPONSE = 1, + FST_ACTION_TEAR_DOWN = 2, + FST_ACTION_ACK_REQUEST = 3, + FST_ACTION_ACK_RESPONSE = 4, + FST_ACTION_ON_CHANNEL_TUNNEL = 5, +}; + +#endif /* IEEE802_11_DEFS_H */ diff --git a/freebsd/contrib/wpa/src/common/qca-vendor.h b/freebsd/contrib/wpa/src/common/qca-vendor.h new file mode 100644 index 00000000..28985f51 --- /dev/null +++ b/freebsd/contrib/wpa/src/common/qca-vendor.h @@ -0,0 +1,357 @@ +/* + * Qualcomm Atheros OUI and vendor specific assignments + * Copyright (c) 2014-2015, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef QCA_VENDOR_H +#define QCA_VENDOR_H + +/* + * This file is a registry of identifier assignments from the Qualcomm Atheros + * OUI 00:13:74 for purposes other than MAC address assignment. New identifiers + * can be assigned through normal review process for changes to the upstream + * hostap.git repository. + */ + +#define OUI_QCA 0x001374 + +/** + * enum qca_radiotap_vendor_ids - QCA radiotap vendor namespace IDs + */ +enum qca_radiotap_vendor_ids { + QCA_RADIOTAP_VID_WLANTEST = 0, +}; + +/** + * enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers + * + * @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0 + * + * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event + * + * @QCA_NL80211_VENDOR_SUBCMD_ROAMING: Set roaming policy for drivers that use + * internal BSS-selection. This command uses + * @QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY to specify the new roaming policy + * for the current connection (i.e., changes policy set by the nl80211 + * Connect command). @QCA_WLAN_VENDOR_ATTR_MAC_ADDR may optionally be + * included to indicate which BSS to use in case roaming is disabled. + * + * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency + * ranges to avoid to reduce issues due to interference or internal + * co-existence information in the driver. The event data structure is + * defined in struct qca_avoid_freq_list. + * + * @QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: Command to check driver support + * for DFS offloading. + * + * @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass + * NAN Request/Response and NAN Indication messages. These messages are + * interpreted between the framework and the firmware component. + * + * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be + * used to configure PMK to the driver even when not connected. This can + * be used to request offloading of key management operations. Only used + * if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD. + * + * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: An extended version of + * NL80211_CMD_ROAM event with optional attributes including information + * from offloaded key management operation. Uses + * enum qca_wlan_vendor_attr_roam_auth attributes. Only used + * if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD. + * + * @QCA_NL80211_VENDOR_SUBCMD_DO_ACS: ACS command/event which is used to + * invoke the ACS function in device and pass selected channels to + * hostapd. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features + * supported by the driver. enum qca_wlan_vendor_features defines + * the possible features. + * + * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: Event used by driver, + * which supports DFS offloading, to indicate a channel availability check + * start. + * + * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: Event used by driver, + * which supports DFS offloading, to indicate a channel availability check + * completion. + * + * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: Event used by driver, + * which supports DFS offloading, to indicate that the channel availability + * check aborted, no change to the channel status. + * + * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: Event used by + * driver, which supports DFS offloading, to indicate that the + * Non-Occupancy Period for this channel is over, channel becomes usable. + * + * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: Event used by driver, + * which supports DFS offloading, to indicate a radar pattern has been + * detected. The channel is now unusable. + */ +enum qca_nl80211_vendor_subcmds { + QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, + QCA_NL80211_VENDOR_SUBCMD_TEST = 1, + /* subcmds 2..8 not yet allocated */ + QCA_NL80211_VENDOR_SUBCMD_ROAMING = 9, + QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10, + QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY = 11, + QCA_NL80211_VENDOR_SUBCMD_NAN = 12, + QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS = 17, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS = 18, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS = 19, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_START = 20, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_STOP = 21, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS = 22, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES = 23, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS = 24, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE = 25, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT = 26, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT = 27, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND = 28, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_BSSID_HOTLIST = 29, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_BSSID_HOTLIST = 30, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE = 31, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SIGNIFICANT_CHANGE = 32, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SIGNIFICANT_CHANGE = 33, + QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE = 34, + QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE = 35, + QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS = 36, + QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE = 37, + QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES = 38, + QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI = 39, + QCA_NL80211_VENDOR_SUBCMD_NO_DFS_FLAG = 40, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_LOST = 41, + QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX = 42, + /* 43..49 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50, + QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51, + QCA_NL80211_VENDOR_SUBCMD_APFIND = 52, + /* 53 - reserved - was used by QCA, but not in use anymore */ + QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54, + QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55, + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED = 56, + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED = 57, + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58, + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59, + QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60, + /* 61-90 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91, + QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92, + QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93, + QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT = 94, + QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT = 95, + QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER = 96, + QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS = 97, + QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS = 98, + QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL = 99, + QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT = 100, + QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES = 101, + QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG = 102, + QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103, + QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104, + QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105, +}; + + +enum qca_wlan_vendor_attr { + QCA_WLAN_VENDOR_ATTR_INVALID = 0, + /* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */ + QCA_WLAN_VENDOR_ATTR_DFS = 1, + /* used by QCA_NL80211_VENDOR_SUBCMD_NAN */ + QCA_WLAN_VENDOR_ATTR_NAN = 2, + /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */ + QCA_WLAN_VENDOR_ATTR_STATS_EXT = 3, + /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */ + QCA_WLAN_VENDOR_ATTR_IFINDEX = 4, + /* used by QCA_NL80211_VENDOR_SUBCMD_ROAMING, u32 with values defined + * by enum qca_roaming_policy. */ + QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5, + QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6, + /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ + QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7, + QCA_WLAN_VENDOR_ATTR_TEST = 8, + /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ + /* Unsigned 32-bit value. */ + QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA = 9, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11, + /* Unsigned 32-bit value from enum qca_set_band. */ + QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, +}; + + +enum qca_roaming_policy { + QCA_ROAMING_NOT_ALLOWED, + QCA_ROAMING_ALLOWED_WITHIN_ESS, +}; + +enum qca_wlan_vendor_attr_roam_auth { + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX = + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1 +}; + +enum qca_wlan_vendor_attr_acs_offload { + QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL, + QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL, + QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, + QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED, + QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED, + QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH, + QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL, + QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL, + QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ACS_MAX = + QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST - 1 +}; + +enum qca_wlan_vendor_acs_hw_mode { + QCA_ACS_MODE_IEEE80211B, + QCA_ACS_MODE_IEEE80211G, + QCA_ACS_MODE_IEEE80211A, + QCA_ACS_MODE_IEEE80211AD, + QCA_ACS_MODE_IEEE80211ANY, +}; + +/** + * enum qca_wlan_vendor_features - Vendor device/driver feature flags + * + * @QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD: Device supports key + * management offload, a mechanism where the station's firmware + * does the exchange with the AP to establish the temporal keys + * after roaming, rather than having the user space wpa_supplicant do it. + * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic + * band selection based on channel selection results. + * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits + */ +enum qca_wlan_vendor_features { + QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0, + QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1, + NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ +}; + +/** + * enum qca_wlan_vendor_attr_data_offload_ind - Vendor Data Offload Indication + * + * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION: Session corresponding to + * the offloaded data. + * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL: Protocol of the offloaded + * data. + * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT: Event type for the data offload + * indication. + */ +enum qca_wlan_vendor_attr_data_offload_ind { + QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION, + QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL, + QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_MAX = + QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1 +}; + +enum qca_vendor_attr_get_preferred_freq_list { + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID, + /* A 32-unsigned value; the interface type/mode for which the preferred + * frequency list is requested (see enum qca_iface_type for possible + * values); used in GET_PREFERRED_FREQ_LIST command from user-space to + * kernel and in the kernel response back to user-space. + */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE, + /* An array of 32-unsigned values; values are frequency (MHz); sent + * from kernel space to user space. + */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX = + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST - 1 +}; + +enum qca_vendor_attr_probable_oper_channel { + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_INVALID, + /* 32-bit unsigned value; indicates the connection/iface type likely to + * come on this channel (see enum qca_iface_type). + */ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE, + /* 32-bit unsigned value; the frequency (MHz) of the probable channel */ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX = + QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST - 1 +}; + +enum qca_iface_type { + QCA_IFACE_TYPE_STA, + QCA_IFACE_TYPE_AP, + QCA_IFACE_TYPE_P2P_CLIENT, + QCA_IFACE_TYPE_P2P_GO, + QCA_IFACE_TYPE_IBSS, + QCA_IFACE_TYPE_TDLS, +}; + +enum qca_set_band { + QCA_SETBAND_AUTO, + QCA_SETBAND_5G, + QCA_SETBAND_2G, +}; + +/* IEEE 802.11 Vendor Specific elements */ + +/** + * enum qca_vendor_element_id - QCA Vendor Specific element types + * + * These values are used to identify QCA Vendor Specific elements. The + * payload of the element starts with the three octet OUI (OUI_QCA) and + * is followed by a single octet type which is defined by this enum. + * + * @QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: P2P preferred channel list. + * This element can be used to specify preference order for supported + * channels. The channels in this list are in preference order (the first + * one has the highest preference) and are described as a pair of + * (global) Operating Class and Channel Number (each one octet) fields. + * + * This extends the standard P2P functionality by providing option to have + * more than one preferred operating channel. When this element is present, + * it replaces the preference indicated in the Operating Channel attribute. + * For supporting other implementations, the Operating Channel attribute is + * expected to be used with the highest preference channel. Similarly, all + * the channels included in this Preferred channel list element are + * expected to be included in the Channel List attribute. + * + * This vendor element may be included in GO Negotiation Request, P2P + * Invitation Request, and Provision Discovery Request frames. + */ +enum qca_vendor_element_id { + QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0, +}; + +#endif /* QCA_VENDOR_H */ diff --git a/freebsd/contrib/wpa/src/common/sae.h b/freebsd/contrib/wpa/src/common/sae.h new file mode 100644 index 00000000..c07026cd --- /dev/null +++ b/freebsd/contrib/wpa/src/common/sae.h @@ -0,0 +1,70 @@ +/* + * Simultaneous authentication of equals + * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SAE_H +#define SAE_H + +#define SAE_KCK_LEN 32 +#define SAE_PMK_LEN 32 +#define SAE_PMKID_LEN 16 +#define SAE_KEYSEED_KEY_LEN 32 +#define SAE_MAX_PRIME_LEN 512 +#define SAE_MAX_ECC_PRIME_LEN 66 +#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN) +#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN) + +/* Special value returned by sae_parse_commit() */ +#define SAE_SILENTLY_DISCARD 65535 + +struct sae_temporary_data { + u8 kck[SAE_KCK_LEN]; + struct crypto_bignum *own_commit_scalar; + struct crypto_bignum *own_commit_element_ffc; + struct crypto_ec_point *own_commit_element_ecc; + struct crypto_bignum *peer_commit_element_ffc; + struct crypto_ec_point *peer_commit_element_ecc; + struct crypto_ec_point *pwe_ecc; + struct crypto_bignum *pwe_ffc; + struct crypto_bignum *sae_rand; + struct crypto_ec *ec; + int prime_len; + const struct dh_group *dh; + const struct crypto_bignum *prime; + const struct crypto_bignum *order; + struct crypto_bignum *prime_buf; + struct crypto_bignum *order_buf; + struct wpabuf *anti_clogging_token; +}; + +struct sae_data { + enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state; + u16 send_confirm; + u8 pmk[SAE_PMK_LEN]; + struct crypto_bignum *peer_commit_scalar; + int group; + int sync; + struct sae_temporary_data *tmp; +}; + +int sae_set_group(struct sae_data *sae, int group); +void sae_clear_temp_data(struct sae_data *sae); +void sae_clear_data(struct sae_data *sae); + +int sae_prepare_commit(const u8 *addr1, const u8 *addr2, + const u8 *password, size_t password_len, + struct sae_data *sae); +int sae_process_commit(struct sae_data *sae); +void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, + const struct wpabuf *token); +u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, + const u8 **token, size_t *token_len, int *allowed_groups); +void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf); +int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len); +u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group); + +#endif /* SAE_H */ diff --git a/freebsd/contrib/wpa/src/common/version.h b/freebsd/contrib/wpa/src/common/version.h new file mode 100644 index 00000000..a5cc5b7b --- /dev/null +++ b/freebsd/contrib/wpa/src/common/version.h @@ -0,0 +1,10 @@ +#ifndef VERSION_H +#define VERSION_H + +#ifndef VERSION_STR_POSTFIX +#define VERSION_STR_POSTFIX "" +#endif /* VERSION_STR_POSTFIX */ + +#define VERSION_STR "2.5" VERSION_STR_POSTFIX + +#endif /* VERSION_H */ diff --git a/freebsd/contrib/wpa/src/common/wpa_common.c b/freebsd/contrib/wpa/src/common/wpa_common.c new file mode 100644 index 00000000..f2c2d56d --- /dev/null +++ b/freebsd/contrib/wpa/src/common/wpa_common.c @@ -0,0 +1,1666 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * WPA/RSN - Shared functions for supplicant and authenticator + * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/sha384.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "ieee802_11_defs.h" +#include "defs.h" +#include "wpa_common.h" + + +static unsigned int wpa_kck_len(int akmp) +{ + if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + return 24; + return 16; +} + + +static unsigned int wpa_kek_len(int akmp) +{ + if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + return 32; + return 16; +} + + +unsigned int wpa_mic_len(int akmp) +{ + if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + return 24; + return 16; +} + + +/** + * wpa_eapol_key_mic - Calculate EAPOL-Key MIC + * @key: EAPOL-Key Key Confirmation Key (KCK) + * @key_len: KCK length in octets + * @akmp: WPA_KEY_MGMT_* used in key derivation + * @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, size_t key_len, int akmp, int ver, + const u8 *buf, size_t len, u8 *mic) +{ + u8 hash[SHA384_MAC_LEN]; + + switch (ver) { +#ifndef CONFIG_FIPS + case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: + return hmac_md5(key, key_len, buf, len, mic); +#endif /* CONFIG_FIPS */ + case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: + if (hmac_sha1(key, key_len, 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 */ + case WPA_KEY_INFO_TYPE_AKM_DEFINED: + switch (akmp) { +#ifdef CONFIG_HS20 + case WPA_KEY_MGMT_OSEN: + return omac1_aes_128(key, buf, len, mic); +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_SUITEB + case WPA_KEY_MGMT_IEEE8021X_SUITE_B: + if (hmac_sha256(key, key_len, buf, len, hash)) + return -1; + os_memcpy(mic, hash, MD5_MAC_LEN); + break; +#endif /* CONFIG_SUITEB */ +#ifdef CONFIG_SUITEB192 + case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + if (hmac_sha384(key, key_len, buf, len, hash)) + return -1; + os_memcpy(mic, hash, 24); + break; +#endif /* CONFIG_SUITEB192 */ + default: + return -1; + } + break; + 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 + * @akmp: Negotiated AKM + * @cipher: Negotiated pairwise cipher + * Returns: 0 on success, -1 on failure + * + * 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)) + */ +int 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, + struct wpa_ptk *ptk, int akmp, int cipher) +{ + u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; + u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; + size_t ptk_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); + } + + ptk->kck_len = wpa_kck_len(akmp); + ptk->kek_len = wpa_kek_len(akmp); + ptk->tk_len = wpa_cipher_key_len(cipher); + ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len; + +#ifdef CONFIG_SUITEB192 + if (wpa_key_mgmt_sha384(akmp)) + sha384_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len); + else +#endif /* CONFIG_SUITEB192 */ +#ifdef CONFIG_IEEE80211W + if (wpa_key_mgmt_sha256(akmp)) + sha256_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len); + else +#endif /* CONFIG_IEEE80211W */ + sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, 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", tmp, ptk_len); + + os_memcpy(ptk->kck, tmp, ptk->kck_len); + wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", ptk->kck, ptk->kck_len); + + os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len); + wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len); + + os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len); + wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len); + + os_memset(tmp, 0, sizeof(tmp)); + return 0; +} + + +#ifdef CONFIG_IEEE80211R +int wpa_ft_mic(const u8 *kck, size_t kck_len, 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) +{ + const u8 *addr[9]; + size_t len[9]; + size_t i, num_elem = 0; + u8 zero_mic[16]; + + if (kck_len != 16) { + wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u", + (unsigned int) kck_len); + return -1; + } + + addr[num_elem] = sta_addr; + len[num_elem] = ETH_ALEN; + num_elem++; + + addr[num_elem] = ap_addr; + len[num_elem] = ETH_ALEN; + num_elem++; + + addr[num_elem] = &transaction_seqnum; + len[num_elem] = 1; + num_elem++; + + if (rsnie) { + addr[num_elem] = rsnie; + len[num_elem] = rsnie_len; + num_elem++; + } + if (mdie) { + addr[num_elem] = mdie; + len[num_elem] = mdie_len; + num_elem++; + } + if (ftie) { + if (ftie_len < 2 + sizeof(struct rsn_ftie)) + return -1; + + /* IE hdr and mic_control */ + addr[num_elem] = ftie; + len[num_elem] = 2 + 2; + num_elem++; + + /* MIC field with all zeros */ + os_memset(zero_mic, 0, sizeof(zero_mic)); + addr[num_elem] = zero_mic; + len[num_elem] = sizeof(zero_mic); + num_elem++; + + /* Rest of FTIE */ + addr[num_elem] = ftie + 2 + 2 + 16; + len[num_elem] = ftie_len - (2 + 2 + 16); + num_elem++; + } + if (ric) { + addr[num_elem] = ric; + len[num_elem] = ric_len; + num_elem++; + } + + for (i = 0; i < num_elem; i++) + wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]); + if (omac1_aes_128_vector(kck, num_elem, addr, len, mic)) + return -1; + + return 0; +} + + +static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + + parse->ftie = ie; + parse->ftie_len = ie_len; + + pos = ie + sizeof(struct rsn_ftie); + end = ie + ie_len; + + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case FTIE_SUBELEM_R1KH_ID: + if (pos[1] != FT_R1KH_ID_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r1kh_id = pos + 2; + break; + case FTIE_SUBELEM_GTK: + parse->gtk = pos + 2; + parse->gtk_len = pos[1]; + break; + case FTIE_SUBELEM_R0KH_ID: + if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r0kh_id = pos + 2; + parse->r0kh_id_len = pos[1]; + break; +#ifdef CONFIG_IEEE80211W + case FTIE_SUBELEM_IGTK: + parse->igtk = pos + 2; + parse->igtk_len = pos[1]; + break; +#endif /* CONFIG_IEEE80211W */ + } + + pos += 2 + pos[1]; + } + + return 0; +} + + +int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + struct wpa_ie_data data; + int ret; + const struct rsn_ftie *ftie; + int prot_ie_count = 0; + + os_memset(parse, 0, sizeof(*parse)); + if (ies == NULL) + return 0; + + pos = ies; + end = ies + ies_len; + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case WLAN_EID_RSN: + parse->rsn = pos + 2; + parse->rsn_len = pos[1]; + ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, + parse->rsn_len + 2, + &data); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse " + "RSN IE: %d", ret); + return -1; + } + if (data.num_pmkid == 1 && data.pmkid) + parse->rsn_pmkid = data.pmkid; + break; + case WLAN_EID_MOBILITY_DOMAIN: + if (pos[1] < sizeof(struct rsn_mdie)) + return -1; + 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: + if (pos[1] != 5) + break; + parse->tie = pos + 2; + parse->tie_len = pos[1]; + break; + case WLAN_EID_RIC_DATA: + if (parse->ric == NULL) + parse->ric = pos; + break; + } + + pos += 2 + pos[1]; + } + + if (prot_ie_count == 0) + return 0; /* no MIC */ + + /* + * Check that the protected IE count matches with IEs included in the + * frame. + */ + if (parse->rsn) + prot_ie_count--; + if (parse->mdie) + prot_ie_count--; + if (parse->ftie) + prot_ie_count--; + if (prot_ie_count < 0) { + wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in " + "the protected IE count"); + return -1; + } + + if (prot_ie_count == 0 && parse->ric) { + wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not " + "included in protected IE count"); + return -1; + } + + /* Determine the end of the RIC IE(s) */ + pos = parse->ric; + while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && + prot_ie_count) { + prot_ie_count--; + pos += 2 + pos[1]; + } + parse->ric_len = pos - parse->ric; + if (prot_ie_count) { + wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " + "frame", (int) prot_ie_count); + return -1; + } + + return 0; +} +#endif /* CONFIG_IEEE80211R */ + + +static int rsn_selector_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) + return WPA_CIPHER_NONE; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP) + return WPA_CIPHER_TKIP; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP) + return WPA_CIPHER_CCMP; +#ifdef CONFIG_IEEE80211W + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) + return WPA_CIPHER_AES_128_CMAC; +#endif /* CONFIG_IEEE80211W */ + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP) + return WPA_CIPHER_GCMP; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP_256) + return WPA_CIPHER_CCMP_256; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP_256) + return WPA_CIPHER_GCMP_256; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_128) + return WPA_CIPHER_BIP_GMAC_128; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_256) + return WPA_CIPHER_BIP_GMAC_256; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_CMAC_256) + return WPA_CIPHER_BIP_CMAC_256; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED) + return WPA_CIPHER_GTK_NOT_USED; + return 0; +} + + +static int rsn_key_mgmt_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X) + return WPA_KEY_MGMT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X) + return WPA_KEY_MGMT_PSK; +#ifdef CONFIG_IEEE80211R + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X) + return WPA_KEY_MGMT_FT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK) + return WPA_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256) + return WPA_KEY_MGMT_IEEE8021X_SHA256; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256) + return WPA_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE) + return WPA_KEY_MGMT_SAE; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE) + return WPA_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B) + return WPA_KEY_MGMT_IEEE8021X_SUITE_B; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192) + return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN) + return WPA_KEY_MGMT_OSEN; + return 0; +} + + +int wpa_cipher_valid_group(int cipher) +{ + return wpa_cipher_valid_pairwise(cipher) || + cipher == WPA_CIPHER_GTK_NOT_USED; +} + + +#ifdef CONFIG_IEEE80211W +int wpa_cipher_valid_mgmt_group(int cipher) +{ + return cipher == WPA_CIPHER_AES_128_CMAC || + cipher == WPA_CIPHER_BIP_GMAC_128 || + cipher == WPA_CIPHER_BIP_GMAC_256 || + cipher == WPA_CIPHER_BIP_CMAC_256; +} +#endif /* CONFIG_IEEE80211W */ + + +/** + * wpa_parse_wpa_ie_rsn - Parse RSN IE + * @rsn_ie: Buffer containing RSN IE + * @rsn_ie_len: RSN IE buffer length (including IE number and length octets) + * @data: Pointer to structure that will be filled in with parsed data + * Returns: 0 on success, <0 on failure + */ +int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, + struct wpa_ie_data *data) +{ + const 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; + } + + if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 && + rsn_ie[1] == rsn_ie_len - 2 && + WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) { + pos = rsn_ie + 6; + left = rsn_ie_len - 6; + + data->proto = WPA_PROTO_OSEN; + } else { + const struct rsn_ie_hdr *hdr; + + 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); + if (!wpa_cipher_valid_group(data->group_cipher)) { + wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x", + __func__, data->group_cipher); + return -1; + } + 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 || count > left / 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 || count > left / 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) { + u16 num_pmkid = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (num_pmkid > (unsigned int) left / PMKID_LEN) { + wpa_printf(MSG_DEBUG, "%s: PMKID underflow " + "(num_pmkid=%u left=%d)", + __func__, num_pmkid, left); + data->num_pmkid = 0; + return -9; + } else { + data->num_pmkid = num_pmkid; + 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 (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) { + 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_hexdump(MSG_DEBUG, + "wpa_parse_wpa_ie_rsn: ignore trailing bytes", + pos, left); + } + + return 0; +} + + +static int wpa_selector_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) + return WPA_CIPHER_NONE; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) + return WPA_CIPHER_TKIP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) + return WPA_CIPHER_CCMP; + 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 < 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 || count > left / 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 || count > left / 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_hexdump(MSG_DEBUG, + "wpa_parse_wpa_ie_wpa: ignore trailing bytes", + pos, 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 + SSID_MAX_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 > SSID_MAX_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 + */ +int 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, + struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher) +{ + u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN]; + u8 *pos, hash[32]; + const u8 *addr[6]; + size_t len[6]; + u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; + size_t ptk_len; + + /* + * 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; + + ptk->kck_len = wpa_kck_len(akmp); + ptk->kek_len = wpa_kek_len(akmp); + ptk->tk_len = wpa_cipher_key_len(cipher); + ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len; + + sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, 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); + + os_memcpy(ptk->kck, tmp, ptk->kck_len); + os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len); + os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len); + + wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len); + wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len); + wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + os_memset(tmp, 0, sizeof(tmp)); + + return 0; +} + +#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); +} + + +#ifdef CONFIG_SUITEB +/** + * rsn_pmkid_suite_b - Calculate PMK identifier for Suite B AKM + * @kck: Key confirmation key + * @kck_len: Length of kck in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @pmkid: Buffer for PMKID + * Returns: 0 on success, -1 on failure + * + * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy + * PMKID = Truncate(HMAC-SHA-256(KCK, "PMK Name" || AA || SPA)) + */ +int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, u8 *pmkid) +{ + char *title = "PMK Name"; + const u8 *addr[3]; + const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA256_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = aa; + addr[2] = spa; + + if (hmac_sha256_vector(kck, kck_len, 3, addr, len, hash) < 0) + return -1; + os_memcpy(pmkid, hash, PMKID_LEN); + return 0; +} +#endif /* CONFIG_SUITEB */ + + +#ifdef CONFIG_SUITEB192 +/** + * rsn_pmkid_suite_b_192 - Calculate PMK identifier for Suite B AKM + * @kck: Key confirmation key + * @kck_len: Length of kck in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @pmkid: Buffer for PMKID + * Returns: 0 on success, -1 on failure + * + * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy + * PMKID = Truncate(HMAC-SHA-384(KCK, "PMK Name" || AA || SPA)) + */ +int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, u8 *pmkid) +{ + char *title = "PMK Name"; + const u8 *addr[3]; + const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA384_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = aa; + addr[2] = spa; + + if (hmac_sha384_vector(kck, kck_len, 3, addr, len, hash) < 0) + return -1; + os_memcpy(pmkid, hash, PMKID_LEN); + return 0; +} +#endif /* CONFIG_SUITEB192 */ + + +/** + * wpa_cipher_txt - Convert cipher suite to a text string + * @cipher: Cipher suite (WPA_CIPHER_* enum) + * Returns: Pointer to a text string of the cipher suite name + */ +const char * wpa_cipher_txt(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_NONE: + return "NONE"; + case WPA_CIPHER_WEP40: + return "WEP-40"; + case WPA_CIPHER_WEP104: + return "WEP-104"; + case WPA_CIPHER_TKIP: + return "TKIP"; + case WPA_CIPHER_CCMP: + return "CCMP"; + case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP: + return "CCMP+TKIP"; + case WPA_CIPHER_GCMP: + return "GCMP"; + case WPA_CIPHER_GCMP_256: + return "GCMP-256"; + case WPA_CIPHER_CCMP_256: + return "CCMP-256"; + case WPA_CIPHER_GTK_NOT_USED: + return "GTK_NOT_USED"; + default: + return "UNKNOWN"; + } +} + + +/** + * wpa_key_mgmt_txt - Convert key management suite to a text string + * @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum) + * @proto: WPA/WPA2 version (WPA_PROTO_*) + * Returns: Pointer to a text string of the key management suite name + */ +const char * wpa_key_mgmt_txt(int key_mgmt, int proto) +{ + switch (key_mgmt) { + case WPA_KEY_MGMT_IEEE8021X: + if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA)) + return "WPA2+WPA/IEEE 802.1X/EAP"; + return proto == WPA_PROTO_RSN ? + "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP"; + case WPA_KEY_MGMT_PSK: + if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA)) + return "WPA2-PSK+WPA-PSK"; + return proto == WPA_PROTO_RSN ? + "WPA2-PSK" : "WPA-PSK"; + case WPA_KEY_MGMT_NONE: + return "NONE"; + case WPA_KEY_MGMT_IEEE8021X_NO_WPA: + return "IEEE 802.1X (no WPA)"; +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_IEEE8021X: + return "FT-EAP"; + case WPA_KEY_MGMT_FT_PSK: + return "FT-PSK"; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + case WPA_KEY_MGMT_IEEE8021X_SHA256: + return "WPA2-EAP-SHA256"; + case WPA_KEY_MGMT_PSK_SHA256: + return "WPA2-PSK-SHA256"; +#endif /* CONFIG_IEEE80211W */ + case WPA_KEY_MGMT_WPS: + return "WPS"; + case WPA_KEY_MGMT_SAE: + return "SAE"; + case WPA_KEY_MGMT_FT_SAE: + return "FT-SAE"; + case WPA_KEY_MGMT_OSEN: + return "OSEN"; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B: + return "WPA2-EAP-SUITE-B"; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + return "WPA2-EAP-SUITE-B-192"; + default: + return "UNKNOWN"; + } +} + + +u32 wpa_akm_to_suite(int akm) +{ + if (akm & WPA_KEY_MGMT_FT_IEEE8021X) + return WLAN_AKM_SUITE_FT_8021X; + if (akm & WPA_KEY_MGMT_FT_PSK) + return WLAN_AKM_SUITE_FT_PSK; + if (akm & WPA_KEY_MGMT_IEEE8021X) + return WLAN_AKM_SUITE_8021X; + if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256) + return WLAN_AKM_SUITE_8021X_SHA256; + if (akm & WPA_KEY_MGMT_IEEE8021X) + return WLAN_AKM_SUITE_8021X; + if (akm & WPA_KEY_MGMT_PSK_SHA256) + return WLAN_AKM_SUITE_PSK_SHA256; + if (akm & WPA_KEY_MGMT_PSK) + return WLAN_AKM_SUITE_PSK; + if (akm & WPA_KEY_MGMT_CCKM) + return WLAN_AKM_SUITE_CCKM; + if (akm & WPA_KEY_MGMT_OSEN) + return WLAN_AKM_SUITE_OSEN; + if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B) + return WLAN_AKM_SUITE_8021X_SUITE_B; + if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + return WLAN_AKM_SUITE_8021X_SUITE_B_192; + return 0; +} + + +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; + added += 2; + start[1] += 2; + rend = rpos; + } 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 + added - 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 + added - rpos); + os_memcpy(rpos, pmkid, PMKID_LEN); + added += PMKID_LEN; + start[1] += PMKID_LEN; + } + + wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification " + "(PMKID inserted)", start, 2 + start[1]); + + return added; +} +#endif /* CONFIG_IEEE80211R */ + + +int wpa_cipher_key_len(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP_256: + case WPA_CIPHER_GCMP_256: + case WPA_CIPHER_BIP_GMAC_256: + case WPA_CIPHER_BIP_CMAC_256: + return 32; + case WPA_CIPHER_CCMP: + case WPA_CIPHER_GCMP: + case WPA_CIPHER_AES_128_CMAC: + case WPA_CIPHER_BIP_GMAC_128: + return 16; + case WPA_CIPHER_TKIP: + return 32; + } + + return 0; +} + + +int wpa_cipher_rsc_len(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP_256: + case WPA_CIPHER_GCMP_256: + case WPA_CIPHER_CCMP: + case WPA_CIPHER_GCMP: + case WPA_CIPHER_TKIP: + return 6; + } + + return 0; +} + + +int wpa_cipher_to_alg(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP_256: + return WPA_ALG_CCMP_256; + case WPA_CIPHER_GCMP_256: + return WPA_ALG_GCMP_256; + case WPA_CIPHER_CCMP: + return WPA_ALG_CCMP; + case WPA_CIPHER_GCMP: + return WPA_ALG_GCMP; + case WPA_CIPHER_TKIP: + return WPA_ALG_TKIP; + case WPA_CIPHER_AES_128_CMAC: + return WPA_ALG_IGTK; + case WPA_CIPHER_BIP_GMAC_128: + return WPA_ALG_BIP_GMAC_128; + case WPA_CIPHER_BIP_GMAC_256: + return WPA_ALG_BIP_GMAC_256; + case WPA_CIPHER_BIP_CMAC_256: + return WPA_ALG_BIP_CMAC_256; + } + return WPA_ALG_NONE; +} + + +int wpa_cipher_valid_pairwise(int cipher) +{ + return cipher == WPA_CIPHER_CCMP_256 || + cipher == WPA_CIPHER_GCMP_256 || + cipher == WPA_CIPHER_CCMP || + cipher == WPA_CIPHER_GCMP || + cipher == WPA_CIPHER_TKIP; +} + + +u32 wpa_cipher_to_suite(int proto, int cipher) +{ + if (cipher & WPA_CIPHER_CCMP_256) + return RSN_CIPHER_SUITE_CCMP_256; + if (cipher & WPA_CIPHER_GCMP_256) + return RSN_CIPHER_SUITE_GCMP_256; + if (cipher & WPA_CIPHER_CCMP) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP); + if (cipher & WPA_CIPHER_GCMP) + return RSN_CIPHER_SUITE_GCMP; + if (cipher & WPA_CIPHER_TKIP) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); + if (cipher & WPA_CIPHER_NONE) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); + if (cipher & WPA_CIPHER_GTK_NOT_USED) + return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED; + if (cipher & WPA_CIPHER_AES_128_CMAC) + return RSN_CIPHER_SUITE_AES_128_CMAC; + if (cipher & WPA_CIPHER_BIP_GMAC_128) + return RSN_CIPHER_SUITE_BIP_GMAC_128; + if (cipher & WPA_CIPHER_BIP_GMAC_256) + return RSN_CIPHER_SUITE_BIP_GMAC_256; + if (cipher & WPA_CIPHER_BIP_CMAC_256) + return RSN_CIPHER_SUITE_BIP_CMAC_256; + return 0; +} + + +int rsn_cipher_put_suites(u8 *start, int ciphers) +{ + u8 *pos = start; + + if (ciphers & WPA_CIPHER_CCMP_256) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP_256); + pos += RSN_SELECTOR_LEN; + } + if (ciphers & WPA_CIPHER_GCMP_256) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP_256); + pos += RSN_SELECTOR_LEN; + } + if (ciphers & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + } + if (ciphers & WPA_CIPHER_GCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); + pos += RSN_SELECTOR_LEN; + } + if (ciphers & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + pos += RSN_SELECTOR_LEN; + } + if (ciphers & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); + pos += RSN_SELECTOR_LEN; + } + + return (pos - start) / RSN_SELECTOR_LEN; +} + + +int wpa_cipher_put_suites(u8 *start, int ciphers) +{ + u8 *pos = start; + + if (ciphers & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + pos += WPA_SELECTOR_LEN; + } + if (ciphers & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + pos += WPA_SELECTOR_LEN; + } + if (ciphers & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); + pos += WPA_SELECTOR_LEN; + } + + return (pos - start) / RSN_SELECTOR_LEN; +} + + +int wpa_pick_pairwise_cipher(int ciphers, int none_allowed) +{ + if (ciphers & WPA_CIPHER_CCMP_256) + return WPA_CIPHER_CCMP_256; + if (ciphers & WPA_CIPHER_GCMP_256) + return WPA_CIPHER_GCMP_256; + if (ciphers & WPA_CIPHER_CCMP) + return WPA_CIPHER_CCMP; + if (ciphers & WPA_CIPHER_GCMP) + return WPA_CIPHER_GCMP; + if (ciphers & WPA_CIPHER_TKIP) + return WPA_CIPHER_TKIP; + if (none_allowed && (ciphers & WPA_CIPHER_NONE)) + return WPA_CIPHER_NONE; + return -1; +} + + +int wpa_pick_group_cipher(int ciphers) +{ + if (ciphers & WPA_CIPHER_CCMP_256) + return WPA_CIPHER_CCMP_256; + if (ciphers & WPA_CIPHER_GCMP_256) + return WPA_CIPHER_GCMP_256; + if (ciphers & WPA_CIPHER_CCMP) + return WPA_CIPHER_CCMP; + if (ciphers & WPA_CIPHER_GCMP) + return WPA_CIPHER_GCMP; + if (ciphers & WPA_CIPHER_GTK_NOT_USED) + return WPA_CIPHER_GTK_NOT_USED; + if (ciphers & WPA_CIPHER_TKIP) + return WPA_CIPHER_TKIP; + return -1; +} + + +int wpa_parse_cipher(const char *value) +{ + int val = 0, last; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "CCMP-256") == 0) + val |= WPA_CIPHER_CCMP_256; + else if (os_strcmp(start, "GCMP-256") == 0) + val |= WPA_CIPHER_GCMP_256; + else if (os_strcmp(start, "CCMP") == 0) + val |= WPA_CIPHER_CCMP; + else if (os_strcmp(start, "GCMP") == 0) + val |= WPA_CIPHER_GCMP; + else if (os_strcmp(start, "TKIP") == 0) + val |= WPA_CIPHER_TKIP; + else if (os_strcmp(start, "WEP104") == 0) + val |= WPA_CIPHER_WEP104; + else if (os_strcmp(start, "WEP40") == 0) + val |= WPA_CIPHER_WEP40; + else if (os_strcmp(start, "NONE") == 0) + val |= WPA_CIPHER_NONE; + else if (os_strcmp(start, "GTK_NOT_USED") == 0) + val |= WPA_CIPHER_GTK_NOT_USED; + else { + os_free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + return val; +} + + +int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim) +{ + char *pos = start; + int ret; + + if (ciphers & WPA_CIPHER_CCMP_256) { + ret = os_snprintf(pos, end - pos, "%sCCMP-256", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_GCMP_256) { + ret = os_snprintf(pos, end - pos, "%sGCMP-256", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_CCMP) { + ret = os_snprintf(pos, end - pos, "%sCCMP", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_GCMP) { + ret = os_snprintf(pos, end - pos, "%sGCMP", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_TKIP) { + ret = os_snprintf(pos, end - pos, "%sTKIP", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_NONE) { + ret = os_snprintf(pos, end - pos, "%sNONE", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + + return pos - start; +} + + +int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise) +{ + int pairwise = 0; + + /* Select group cipher based on the enabled pairwise cipher suites */ + if (wpa & 1) + pairwise |= wpa_pairwise; + if (wpa & 2) + pairwise |= rsn_pairwise; + + if (pairwise & WPA_CIPHER_TKIP) + return WPA_CIPHER_TKIP; + if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP) + return WPA_CIPHER_GCMP; + if ((pairwise & (WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP | + WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP_256) + return WPA_CIPHER_GCMP_256; + if ((pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP | + WPA_CIPHER_GCMP)) == WPA_CIPHER_CCMP_256) + return WPA_CIPHER_CCMP_256; + return WPA_CIPHER_CCMP; +} diff --git a/freebsd/contrib/wpa/src/common/wpa_common.h b/freebsd/contrib/wpa/src/common/wpa_common.h new file mode 100644 index 00000000..c08f6514 --- /dev/null +++ b/freebsd/contrib/wpa/src/common/wpa_common.h @@ -0,0 +1,451 @@ +/* + * WPA definitions shared between hostapd and wpa_supplicant + * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_COMMON_H +#define WPA_COMMON_H + +/* IEEE 802.11i */ +#define PMKID_LEN 16 +#define PMK_LEN 32 +#define WPA_REPLAY_COUNTER_LEN 8 +#define WPA_NONCE_LEN 32 +#define WPA_KEY_RSC_LEN 8 +#define WPA_GMK_LEN 32 +#define WPA_GTK_MAX_LEN 32 + +#define WPA_ALLOWED_PAIRWISE_CIPHERS \ +(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \ +WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) +#define WPA_ALLOWED_GROUP_CIPHERS \ +(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \ +WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \ +WPA_CIPHER_GTK_NOT_USED) + +#define WPA_SELECTOR_LEN 4 +#define WPA_VERSION 1 +#define RSN_SELECTOR_LEN 4 +#define RSN_VERSION 1 + +#define RSN_SELECTOR(a, b, c, d) \ + ((((u32) (a)) << 24) | (((u32) (b)) << 16) | (((u32) (c)) << 8) | \ + (u32) (d)) + +#define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) +#define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1) +#define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2) +#define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0) +#define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) +#define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2) +#define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4) + + +#define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1) +#define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2) +#ifdef CONFIG_IEEE80211R +#define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3) +#define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#endif /* CONFIG_IEEE80211R */ +#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) +#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8) +#define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9) +#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11) +#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) +#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \ +RSN_SELECTOR(0x00, 0x0f, 0xac, 13) +#define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00) +#define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01) + +#define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) +#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_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) +#define RSN_CIPHER_SUITE_GCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 9) +#define RSN_CIPHER_SUITE_CCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 10) +#define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11) +#define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) +#define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13) + +/* EAPOL-Key Key Data Encapsulation + * GroupKey and PeerKey require encryption, otherwise, encryption is optional. + */ +#define RSN_KEY_DATA_GROUPKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 1) +#if 0 +#define RSN_KEY_DATA_STAKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 2) +#endif +#define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3) +#define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#ifdef CONFIG_PEERKEY +#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5) +#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8) +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W +#define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) +#endif /* CONFIG_IEEE80211W */ +#define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10) +#define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11) +#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12) + +#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4) +#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5) + +#define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1) + +#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 +#define WPA_IGTK_MAX_LEN 32 +#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_AKM_DEFINED 0 +#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_eapol_key_192 { + 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[24]; + u8 key_data_length[2]; /* big endian */ + /* followed by key_data_length bytes of key_data */ +} STRUCT_PACKED; + +#define WPA_EAPOL_KEY_MIC_MAX_LEN 24 +#define WPA_KCK_MAX_LEN 24 +#define WPA_KEK_MAX_LEN 32 +#define WPA_TK_MAX_LEN 32 + +/** + * struct wpa_ptk - WPA Pairwise Transient Key + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + */ +struct wpa_ptk { + u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */ + u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */ + u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */ + size_t kck_len; + size_t kek_len; + size_t tk_len; +}; + + +/* 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 +#define WPA_IGTK_KDE_PREFIX_LEN (2 + 6) +struct wpa_igtk_kde { + u8 keyid[2]; + u8 pn[6]; + u8 igtk[WPA_IGTK_MAX_LEN]; +} STRUCT_PACKED; +#endif /* CONFIG_IEEE80211W */ + +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; + + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, + const u8 *buf, size_t len, u8 *mic); +int 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, + struct wpa_ptk *ptk, int akmp, int cipher); + +#ifdef CONFIG_IEEE80211R +int wpa_ft_mic(const u8 *kck, size_t kck_len, 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); +int 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, + struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher); +#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); +#ifdef CONFIG_SUITEB +int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, u8 *pmkid); +#else /* CONFIG_SUITEB */ +static inline int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, u8 *pmkid) +{ + return -1; +} +#endif /* CONFIG_SUITEB */ +#ifdef CONFIG_SUITEB192 +int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, u8 *pmkid); +#else /* CONFIG_SUITEB192 */ +static inline int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, u8 *pmkid) +{ + return -1; +} +#endif /* CONFIG_SUITEB192 */ + +const char * wpa_cipher_txt(int cipher); +const char * wpa_key_mgmt_txt(int key_mgmt, int proto); +u32 wpa_akm_to_suite(int akm); +int wpa_compare_rsn_ie(int ft_initial_assoc, + const u8 *ie1, size_t ie1len, + const u8 *ie2, size_t ie2len); +int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid); + +struct wpa_ft_ies { + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *r1kh_id; + const u8 *gtk; + size_t gtk_len; + const u8 *r0kh_id; + size_t r0kh_id_len; + const u8 *rsn; + size_t rsn_len; + const u8 *rsn_pmkid; + const u8 *tie; + size_t tie_len; + const u8 *igtk; + size_t igtk_len; + const u8 *ric; + size_t ric_len; +}; + +int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse); + +int wpa_cipher_key_len(int cipher); +int wpa_cipher_rsc_len(int cipher); +int wpa_cipher_to_alg(int cipher); +int wpa_cipher_valid_group(int cipher); +int wpa_cipher_valid_pairwise(int cipher); +int wpa_cipher_valid_mgmt_group(int cipher); +u32 wpa_cipher_to_suite(int proto, int cipher); +int rsn_cipher_put_suites(u8 *pos, int ciphers); +int wpa_cipher_put_suites(u8 *pos, int ciphers); +int wpa_pick_pairwise_cipher(int ciphers, int none_allowed); +int wpa_pick_group_cipher(int ciphers); +int wpa_parse_cipher(const char *value); +int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim); +int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise); +unsigned int wpa_mic_len(int akmp); + +#endif /* WPA_COMMON_H */ diff --git a/freebsd/contrib/wpa/src/common/wpa_ctrl.h b/freebsd/contrib/wpa/src/common/wpa_ctrl.h new file mode 100644 index 00000000..3de46823 --- /dev/null +++ b/freebsd/contrib/wpa/src/common/wpa_ctrl.h @@ -0,0 +1,472 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_CTRL_H +#define WPA_CTRL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* wpa_supplicant control interface - fixed message prefixes */ + +/** Interactive request for identity/password/pin */ +#define WPA_CTRL_REQ "CTRL-REQ-" + +/** Response to identity/password/pin request */ +#define WPA_CTRL_RSP "CTRL-RSP-" + +/* Event messages with fixed prefix */ +/** Authentication completed successfully and data connection enabled */ +#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED " +/** Disconnected, data connection is not available */ +#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " +/** Association rejected during connection attempt */ +#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT " +/** Authentication rejected during connection attempt */ +#define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-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 peer certificate alternative subject name component from TLS */ +#define WPA_EVENT_EAP_PEER_ALT "CTRL-EVENT-EAP-PEER-ALT " +/** EAP TLS certificate chain validation error */ +#define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR " +/** EAP status */ +#define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS " +/** EAP authentication completed successfully */ +#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " +/** EAP authentication failed (EAP-Failure received) */ +#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " +/** Network block temporarily disabled (e.g., due to authentication failure) */ +#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED " +/** Temporarily disabled network block re-enabled */ +#define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED " +/** New scan started */ +#define WPA_EVENT_SCAN_STARTED "CTRL-EVENT-SCAN-STARTED " +/** New scan results available */ +#define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " +/** Scan command failed */ +#define WPA_EVENT_SCAN_FAILED "CTRL-EVENT-SCAN-FAILED " +/** 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 " +/** No suitable network was found */ +#define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND " +/** Change in the signal level was reported by the driver */ +#define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE " +/** Regulatory domain channel */ +#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE " + +/** RSN IBSS 4-way handshakes completed with specified peer */ +#define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED " + +/** Notification of frequency conflict due to a concurrent operation. + * + * The indicated network is disabled and needs to be re-enabled before it can + * be used again. + */ +#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT " +/** Frequency ranges that the driver recommends to avoid */ +#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ " +/** WPS overlap detected in PBC mode */ +#define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED " +/** Available WPS AP with active PBC found in scan results */ +#define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC " +/** Available WPS AP with our address as authorized in scan results */ +#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH " +/** Available WPS AP with recently selected PIN registrar found in scan results + */ +#define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN " +/** Available WPS AP found in scan results */ +#define WPS_EVENT_AP_AVAILABLE "WPS-AP-AVAILABLE " +/** A new credential received */ +#define WPS_EVENT_CRED_RECEIVED "WPS-CRED-RECEIVED " +/** M2D received */ +#define WPS_EVENT_M2D "WPS-M2D " +/** WPS registration failed after M2/M2D */ +#define WPS_EVENT_FAIL "WPS-FAIL " +/** WPS registration completed successfully */ +#define WPS_EVENT_SUCCESS "WPS-SUCCESS " +/** WPS enrollment attempt timed out and was terminated */ +#define WPS_EVENT_TIMEOUT "WPS-TIMEOUT " +/* PBC mode was activated */ +#define WPS_EVENT_ACTIVE "WPS-PBC-ACTIVE " +/* PBC mode was disabled */ +#define WPS_EVENT_DISABLE "WPS-PBC-DISABLE " + +#define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN " + +#define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK " + +/* WPS ER events */ +#define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD " +#define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE " +#define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD " +#define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE " +#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS " +#define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG " + +/* MESH events */ +#define MESH_GROUP_STARTED "MESH-GROUP-STARTED " +#define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED " +#define MESH_PEER_CONNECTED "MESH-PEER-CONNECTED " +#define MESH_PEER_DISCONNECTED "MESH-PEER-DISCONNECTED " +/** Mesh SAE authentication failure. Wrong password suspected. */ +#define MESH_SAE_AUTH_FAILURE "MESH-SAE-AUTH-FAILURE " +#define MESH_SAE_AUTH_BLOCKED "MESH-SAE-AUTH-BLOCKED " + +/* WMM AC events */ +#define WMM_AC_EVENT_TSPEC_ADDED "TSPEC-ADDED " +#define WMM_AC_EVENT_TSPEC_REMOVED "TSPEC-REMOVED " +#define WMM_AC_EVENT_TSPEC_REQ_FAILED "TSPEC-REQ-FAILED " + +/** P2P device found */ +#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND " + +/** P2P device lost */ +#define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST " + +/** A P2P device requested GO negotiation, but we were not ready to start the + * negotiation */ +#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST " +#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS " +#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE " +#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS " +#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE " +#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED " +#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED " +#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE " +#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE " +/* parameters: <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: <peer address> <status> */ +#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE" +/* 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_SERV_ASP_RESP "P2P-SERV-ASP-RESP " +#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED " +#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT " +#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED " +#define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id=" +#define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE " +#define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO " +#define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT " +#define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT " +#define P2P_EVENT_FALLBACK_TO_GO_NEG "P2P-FALLBACK-TO-GO-NEG " +#define P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED "P2P-FALLBACK-TO-GO-NEG-ENABLED " + +/* parameters: <PMF enabled> <timeout in ms> <Session Information URL> */ +#define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT " +#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP " + +#define P2P_EVENT_P2PS_PROVISION_START "P2PS-PROV-START " +#define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE " + +#define INTERWORKING_AP "INTERWORKING-AP " +#define INTERWORKING_BLACKLISTED "INTERWORKING-BLACKLISTED " +#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " +#define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED " +#define INTERWORKING_SELECTED "INTERWORKING-SELECTED " + +/* Credential block added; parameters: <id> */ +#define CRED_ADDED "CRED-ADDED " +/* Credential block modified; parameters: <id> <field> */ +#define CRED_MODIFIED "CRED-MODIFIED " +/* Credential block removed; parameters: <id> */ +#define CRED_REMOVED "CRED-REMOVED " + +#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO " +/* parameters: <addr> <dialog_token> <freq> */ +#define GAS_QUERY_START "GAS-QUERY-START " +/* parameters: <addr> <dialog_token> <freq> <status_code> <result> */ +#define GAS_QUERY_DONE "GAS-QUERY-DONE " + +/* parameters: <addr> <result> */ +#define ANQP_QUERY_DONE "ANQP-QUERY-DONE " + +#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION " +#define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE " + +#define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START " +#define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT " + +#define RRM_EVENT_NEIGHBOR_REP_RXED "RRM-NEIGHBOR-REP-RECEIVED " +#define RRM_EVENT_NEIGHBOR_REP_FAILED "RRM-NEIGHBOR-REP-REQUEST-FAILED " + +/* hostapd control interface - fixed message prefixes */ +#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED " +#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS " +#define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS " +#define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED " +#define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED " +#define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED " +#define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED " +#define AP_STA_CONNECTED "AP-STA-CONNECTED " +#define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED " +#define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH " + +#define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA " +#define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA " + +#define AP_EVENT_ENABLED "AP-ENABLED " +#define AP_EVENT_DISABLED "AP-DISABLED " + +#define INTERFACE_ENABLED "INTERFACE-ENABLED " +#define INTERFACE_DISABLED "INTERFACE-DISABLED " + +#define ACS_EVENT_STARTED "ACS-STARTED " +#define ACS_EVENT_COMPLETED "ACS-COMPLETED " +#define ACS_EVENT_FAILED "ACS-FAILED " + +#define DFS_EVENT_RADAR_DETECTED "DFS-RADAR-DETECTED " +#define DFS_EVENT_NEW_CHANNEL "DFS-NEW-CHANNEL " +#define DFS_EVENT_CAC_START "DFS-CAC-START " +#define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED " +#define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED " + +#define AP_CSA_FINISHED "AP-CSA-FINISHED " + +/* BSS Transition Management Response frame received */ +#define BSS_TM_RESP "BSS-TM-RESP " + +/* BSS command information masks */ + +#define WPA_BSS_MASK_ALL 0xFFFDFFFF +#define WPA_BSS_MASK_ID BIT(0) +#define WPA_BSS_MASK_BSSID BIT(1) +#define WPA_BSS_MASK_FREQ BIT(2) +#define WPA_BSS_MASK_BEACON_INT BIT(3) +#define WPA_BSS_MASK_CAPABILITIES BIT(4) +#define WPA_BSS_MASK_QUAL BIT(5) +#define WPA_BSS_MASK_NOISE BIT(6) +#define WPA_BSS_MASK_LEVEL BIT(7) +#define WPA_BSS_MASK_TSF BIT(8) +#define WPA_BSS_MASK_AGE BIT(9) +#define WPA_BSS_MASK_IE BIT(10) +#define WPA_BSS_MASK_FLAGS BIT(11) +#define WPA_BSS_MASK_SSID BIT(12) +#define WPA_BSS_MASK_WPS_SCAN BIT(13) +#define WPA_BSS_MASK_P2P_SCAN BIT(14) +#define WPA_BSS_MASK_INTERNETW BIT(15) +#define WPA_BSS_MASK_WIFI_DISPLAY BIT(16) +#define WPA_BSS_MASK_DELIM BIT(17) +#define WPA_BSS_MASK_MESH_SCAN BIT(18) +#define WPA_BSS_MASK_SNR BIT(19) +#define WPA_BSS_MASK_EST_THROUGHPUT BIT(20) +#define WPA_BSS_MASK_FST BIT(21) + + +/* VENDOR_ELEM_* frame id values */ +enum wpa_vendor_elem_frame { + VENDOR_ELEM_PROBE_REQ_P2P = 0, + VENDOR_ELEM_PROBE_RESP_P2P = 1, + VENDOR_ELEM_PROBE_RESP_P2P_GO = 2, + VENDOR_ELEM_BEACON_P2P_GO = 3, + VENDOR_ELEM_P2P_PD_REQ = 4, + VENDOR_ELEM_P2P_PD_RESP = 5, + VENDOR_ELEM_P2P_GO_NEG_REQ = 6, + VENDOR_ELEM_P2P_GO_NEG_RESP = 7, + VENDOR_ELEM_P2P_GO_NEG_CONF = 8, + VENDOR_ELEM_P2P_INV_REQ = 9, + VENDOR_ELEM_P2P_INV_RESP = 10, + VENDOR_ELEM_P2P_ASSOC_REQ = 11, + VENDOR_ELEM_P2P_ASSOC_RESP = 12, + VENDOR_ELEM_ASSOC_REQ = 13, + NUM_VENDOR_ELEM_FRAMES +}; + + +/* 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_open2 - Open a control interface to wpa_supplicant/hostapd + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. + * @cli_path: Path for client UNIX domain sockets; ignored if UDP socket + * is 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 + * when the socket path for client need to be specified explicitly. Default + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd and client + * socket path is /tmp. + */ +struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_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. 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 ANDROID +/** + * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that + * may be left over from clients that were previously connected to + * wpa_supplicant. This keeps these files from being orphaned in the + * event of crashes that prevented them from being removed as part + * of the normal orderly shutdown. + */ +void wpa_ctrl_cleanup(void); +#endif /* ANDROID */ + +#ifdef CONFIG_CTRL_IFACE_UDP +/* Port range for multiple wpa_supplicant instances and multiple VIFs */ +#define WPA_CTRL_IFACE_PORT 9877 +#define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */ +#define WPA_GLOBAL_CTRL_IFACE_PORT 9878 +#define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */ + +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl); +#endif /* CONFIG_CTRL_IFACE_UDP */ + + +#ifdef __cplusplus +} +#endif + +#endif /* WPA_CTRL_H */ diff --git a/freebsd/contrib/wpa/src/crypto/aes-ctr.c b/freebsd/contrib/wpa/src/crypto/aes-ctr.c new file mode 100644 index 00000000..f92be022 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/aes-ctr.c @@ -0,0 +1,57 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * AES-128 CTR + * + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +/** + * aes_128_ctr_encrypt - AES-128 CTR mode encryption + * @key: Key for encryption (16 bytes) + * @nonce: Nonce for counter mode (16 bytes) + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * Returns: 0 on success, -1 on failure + */ +int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len) +{ + void *ctx; + size_t j, len, left = data_len; + int i; + u8 *pos = data; + u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; + + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + os_memcpy(counter, nonce, AES_BLOCK_SIZE); + + while (left > 0) { + aes_encrypt(ctx, counter, buf); + + len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; + for (j = 0; j < len; j++) + pos[j] ^= buf[j]; + pos += len; + left -= len; + + for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { + counter[i]++; + if (counter[i]) + break; + } + } + aes_encrypt_deinit(ctx); + return 0; +} diff --git a/freebsd/contrib/wpa/src/crypto/aes-eax.c b/freebsd/contrib/wpa/src/crypto/aes-eax.c new file mode 100644 index 00000000..db3cf089 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/aes-eax.c @@ -0,0 +1,147 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * AES-128 EAX + * + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +/** + * aes_128_eax_encrypt - AES-128 EAX mode encryption + * @key: Key for encryption (16 bytes) + * @nonce: Nonce for counter mode + * @nonce_len: Nonce length in bytes + * @hdr: Header data to be authenticity protected + * @hdr_len: Length of the header data bytes + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * @tag: 16-byte tag value + * Returns: 0 on success, -1 on failure + */ +int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, u8 *tag) +{ + u8 *buf; + size_t buf_len; + u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE], + data_mac[AES_BLOCK_SIZE]; + int i, ret = -1; + + if (nonce_len > data_len) + buf_len = nonce_len; + else + buf_len = data_len; + if (hdr_len > buf_len) + buf_len = hdr_len; + buf_len += 16; + + buf = os_malloc(buf_len); + if (buf == NULL) + return -1; + + os_memset(buf, 0, 15); + + buf[15] = 0; + os_memcpy(buf + 16, nonce, nonce_len); + if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) + goto fail; + + buf[15] = 1; + os_memcpy(buf + 16, hdr, hdr_len); + if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) + goto fail; + + if (aes_128_ctr_encrypt(key, nonce_mac, data, data_len)) + goto fail; + buf[15] = 2; + os_memcpy(buf + 16, data, data_len); + if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) + goto fail; + + for (i = 0; i < AES_BLOCK_SIZE; i++) + tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]; + + ret = 0; +fail: + bin_clear_free(buf, buf_len); + + 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/freebsd/contrib/wpa/src/crypto/aes-encblock.c b/freebsd/contrib/wpa/src/crypto/aes-encblock.c new file mode 100644 index 00000000..059f1e2d --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/aes-encblock.c @@ -0,0 +1,34 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * AES encrypt_block + * + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +/** + * aes_128_encrypt_block - Perform one AES 128-bit block operation + * @key: Key for AES + * @in: Input data (16 bytes) + * @out: Output of the AES block operation (16 bytes) + * Returns: 0 on success, -1 on failure + */ +int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) +{ + void *ctx; + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + aes_encrypt(ctx, in, out); + aes_encrypt_deinit(ctx); + return 0; +} diff --git a/freebsd/contrib/wpa/src/crypto/aes-omac1.c b/freebsd/contrib/wpa/src/crypto/aes-omac1.c new file mode 100644 index 00000000..7e62ef85 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/aes-omac1.c @@ -0,0 +1,172 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * One-key CBC MAC (OMAC1) hash with AES + * + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +static void gf_mulx(u8 *pad) +{ + int i, carry; + + carry = pad[0] & 0x80; + for (i = 0; i < AES_BLOCK_SIZE - 1; i++) + pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); + pad[AES_BLOCK_SIZE - 1] <<= 1; + if (carry) + pad[AES_BLOCK_SIZE - 1] ^= 0x87; +} + + +/** + * omac1_aes_vector - One-Key CBC MAC (OMAC1) hash with AES + * @key: Key for the hash operation + * @key_len: Key length in octets + * @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_vector(const u8 *key, size_t key_len, 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, key_len); + 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) { + /* + * Stop if there are no more bytes to process + * since there are no more entries in the array. + */ + if (i + 1 == AES_BLOCK_SIZE && + left == AES_BLOCK_SIZE) + break; + 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) { + /* + * Stop if there are no more bytes to process + * since there are no more entries in the array. + */ + if (i + 1 == left) + break; + 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_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) +{ + return omac1_aes_vector(key, 16, num_elem, addr, len, mac); +} + + +/** + * 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); +} + + +/** + * omac1_aes_256 - One-Key CBC MAC (OMAC1) hash with AES-256 (aka AES-CMAC) + * @key: 256-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_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_vector(key, 32, 1, &data, &data_len, mac); +} diff --git a/freebsd/contrib/wpa/src/crypto/aes-unwrap.c b/freebsd/contrib/wpa/src/crypto/aes-unwrap.c new file mode 100644 index 00000000..63e6c34e --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/aes-unwrap.c @@ -0,0 +1,82 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * AES key unwrap (RFC3394) + * + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +/** + * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (RFC3394) + * @kek: Key encryption key (KEK) + * @kek_len: Length of KEK in octets + * @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, size_t kek_len, int n, const u8 *cipher, + u8 *plain) +{ + u8 a[8], *r, b[AES_BLOCK_SIZE]; + int i, j; + void *ctx; + unsigned int t; + + /* 1) Initialize variables. */ + os_memcpy(a, cipher, 8); + r = plain; + os_memcpy(r, cipher + 8, 8 * n); + + ctx = aes_decrypt_init(kek, kek_len); + 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); + t = n * j + i; + b[7] ^= t; + b[6] ^= t >> 8; + b[5] ^= t >> 16; + b[4] ^= t >> 24; + + 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/freebsd/contrib/wpa/src/crypto/aes.h b/freebsd/contrib/wpa/src/crypto/aes.h new file mode 100644 index 00000000..2de59e04 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/aes.h @@ -0,0 +1,21 @@ +/* + * AES functions + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef AES_H +#define AES_H + +#define AES_BLOCK_SIZE 16 + +void * aes_encrypt_init(const u8 *key, size_t len); +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); +void aes_encrypt_deinit(void *ctx); +void * aes_decrypt_init(const u8 *key, size_t len); +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); +void aes_decrypt_deinit(void *ctx); + +#endif /* AES_H */ diff --git a/freebsd/contrib/wpa/src/crypto/aes_wrap.h b/freebsd/contrib/wpa/src/crypto/aes_wrap.h new file mode 100644 index 00000000..4a142093 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/aes_wrap.h @@ -0,0 +1,71 @@ +/* + * AES-based functions + * + * - AES Key Wrap Algorithm (RFC3394) + * - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256 + * - AES-128 CTR mode encryption + * - AES-128 EAX mode encryption/decryption + * - AES-128 CBC + * - AES-GCM + * - AES-CCM + * + * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef AES_WRAP_H +#define AES_WRAP_H + +int __must_check aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, + u8 *cipher); +int __must_check aes_unwrap(const u8 *kek, size_t kek_len, int n, + const u8 *cipher, u8 *plain); +int __must_check omac1_aes_vector(const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac); +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 omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, + u8 *mac); +int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); +int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len); +int __must_check aes_128_eax_encrypt(const u8 *key, + const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, u8 *tag); +int __must_check aes_128_eax_decrypt(const u8 *key, + const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, const u8 *tag); +int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +int __must_check aes_gcm_ae(const u8 *key, size_t key_len, + const u8 *iv, size_t iv_len, + const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, + u8 *crypt, u8 *tag); +int __must_check aes_gcm_ad(const u8 *key, size_t key_len, + const u8 *iv, size_t iv_len, + const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *tag, + u8 *plain); +int __must_check aes_gmac(const u8 *key, size_t key_len, + const u8 *iv, size_t iv_len, + const u8 *aad, size_t aad_len, u8 *tag); +int __must_check aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth); +int __must_check aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *auth, + u8 *plain); + +#endif /* AES_WRAP_H */ diff --git a/freebsd/contrib/wpa/src/crypto/crypto.h b/freebsd/contrib/wpa/src/crypto/crypto.h new file mode 100644 index 00000000..534c4bd7 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/crypto.h @@ -0,0 +1,809 @@ +/* + * Wrapper functions for crypto libraries + * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This file defines the cryptographic functions that need to be implemented + * for wpa_supplicant and hostapd. When TLS is not used, internal + * implementation of MD5, SHA1, and AES is used and no external libraries are + * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the + * crypto library used by the TLS implementation is expected to be used for + * non-TLS needs, too, in order to save space by not implementing these + * functions twice. + * + * Wrapper code for using each crypto library is in its own file (crypto*.c) + * and one of these files is build and linked in to provide the functions + * defined here. + */ + +#ifndef CRYPTO_H +#define CRYPTO_H + +/** + * md4_vector - MD4 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); + +/** + * md5_vector - MD5 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); + + +/** + * sha1_vector - SHA-1 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** + * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF + * @seed: Seed/key for the PRF + * @seed_len: Seed length in bytes + * @x: Buffer for PRF output + * @xlen: Output length in bytes + * Returns: 0 on success, -1 on failure + * + * This function implements random number generation specified in NIST FIPS + * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to + * SHA-1, but has different message padding. + */ +int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, + size_t xlen); + +/** + * sha256_vector - SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** + * des_encrypt - Encrypt one block with DES + * @clear: 8 octets (in) + * @key: 7 octets (in) (no parity bits included) + * @cypher: 8 octets (out) + */ +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); + +/** + * aes_encrypt_init - Initialize AES for encryption + * @key: Encryption key + * @len: Key length in bytes (usually 16, i.e., 128 bits) + * Returns: Pointer to context data or %NULL on failure + */ +void * aes_encrypt_init(const u8 *key, size_t len); + +/** + * aes_encrypt - Encrypt one AES block + * @ctx: Context pointer from aes_encrypt_init() + * @plain: Plaintext data to be encrypted (16 bytes) + * @crypt: Buffer for the encrypted data (16 bytes) + */ +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); + +/** + * aes_encrypt_deinit - Deinitialize AES encryption + * @ctx: Context pointer from aes_encrypt_init() + */ +void aes_encrypt_deinit(void *ctx); + +/** + * aes_decrypt_init - Initialize AES for decryption + * @key: Decryption key + * @len: Key length in bytes (usually 16, i.e., 128 bits) + * Returns: Pointer to context data or %NULL on failure + */ +void * aes_decrypt_init(const u8 *key, size_t len); + +/** + * aes_decrypt - Decrypt one AES block + * @ctx: Context pointer from aes_encrypt_init() + * @crypt: Encrypted data (16 bytes) + * @plain: Buffer for the decrypted data (16 bytes) + */ +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); + +/** + * aes_decrypt_deinit - Deinitialize AES decryption + * @ctx: Context pointer from aes_encrypt_init() + */ +void aes_decrypt_deinit(void *ctx); + + +enum crypto_hash_alg { + CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1, + CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1, + CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256 +}; + +struct crypto_hash; + +/** + * crypto_hash_init - Initialize hash/HMAC function + * @alg: Hash algorithm + * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed + * @key_len: Length of the key in bytes + * Returns: Pointer to hash context to use with other hash functions or %NULL + * on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len); + +/** + * crypto_hash_update - Add data to hash calculation + * @ctx: Context pointer from crypto_hash_init() + * @data: Data buffer to add + * @len: Length of the buffer + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len); + +/** + * crypto_hash_finish - Complete hash calculation + * @ctx: Context pointer from crypto_hash_init() + * @hash: Buffer for hash value or %NULL if caller is just freeing the hash + * context + * @len: Pointer to length of the buffer or %NULL if caller is just freeing the + * hash context; on return, this is set to the actual length of the hash value + * Returns: 0 on success, -1 if buffer is too small (len set to needed length), + * or -2 on other failures (including failed crypto_hash_update() operations) + * + * This function calculates the hash value and frees the context buffer that + * was used for hash calculation. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len); + + +enum crypto_cipher_alg { + CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES, + CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4 +}; + +struct crypto_cipher; + +/** + * crypto_cipher_init - Initialize block/stream cipher function + * @alg: Cipher algorithm + * @iv: Initialization vector for block ciphers or %NULL for stream ciphers + * @key: Cipher key + * @key_len: Length of key in bytes + * Returns: Pointer to cipher context to use with other cipher functions or + * %NULL on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len); + +/** + * crypto_cipher_encrypt - Cipher encrypt + * @ctx: Context pointer from crypto_cipher_init() + * @plain: Plaintext to cipher + * @crypt: Resulting ciphertext + * @len: Length of the plaintext + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx, + const u8 *plain, u8 *crypt, size_t len); + +/** + * crypto_cipher_decrypt - Cipher decrypt + * @ctx: Context pointer from crypto_cipher_init() + * @crypt: Ciphertext to decrypt + * @plain: Resulting plaintext + * @len: Length of the cipher text + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx, + const u8 *crypt, u8 *plain, size_t len); + +/** + * crypto_cipher_decrypt - Free cipher context + * @ctx: Context pointer from crypto_cipher_init() + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_cipher_deinit(struct crypto_cipher *ctx); + + +struct crypto_public_key; +struct crypto_private_key; + +/** + * crypto_public_key_import - Import an RSA public key + * @key: Key buffer (DER encoded RSA public key) + * @len: Key buffer length in bytes + * Returns: Pointer to the public key or %NULL on failure + * + * This function can just return %NULL if the crypto library supports X.509 + * parsing. In that case, crypto_public_key_from_cert() is used to import the + * public key from a certificate. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len); + +struct crypto_public_key * +crypto_public_key_import_parts(const u8 *n, size_t n_len, + const u8 *e, size_t e_len); + +/** + * crypto_private_key_import - Import an RSA private key + * @key: Key buffer (DER encoded RSA private key) + * @len: Key buffer length in bytes + * @passwd: Key encryption password or %NULL if key is not encrypted + * Returns: Pointer to the private key or %NULL on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_private_key * crypto_private_key_import(const u8 *key, + size_t len, + const char *passwd); + +/** + * crypto_public_key_from_cert - Import an RSA public key from a certificate + * @buf: DER encoded X.509 certificate + * @len: Certificate buffer length in bytes + * Returns: Pointer to public key or %NULL on failure + * + * This function can just return %NULL if the crypto library does not support + * X.509 parsing. In that case, internal code will be used to parse the + * certificate and public key is imported using crypto_public_key_import(). + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, + size_t len); + +/** + * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5) + * @key: Public key + * @in: Plaintext buffer + * @inlen: Length of plaintext buffer in bytes + * @out: Output buffer for encrypted data + * @outlen: Length of output buffer in bytes; set to used length on success + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_public_key_encrypt_pkcs1_v15( + struct crypto_public_key *key, const u8 *in, size_t inlen, + u8 *out, size_t *outlen); + +/** + * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5) + * @key: Private key + * @in: Encrypted buffer + * @inlen: Length of encrypted buffer in bytes + * @out: Output buffer for encrypted data + * @outlen: Length of output buffer in bytes; set to used length on success + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_private_key_decrypt_pkcs1_v15( + struct crypto_private_key *key, const u8 *in, size_t inlen, + u8 *out, size_t *outlen); + +/** + * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1) + * @key: Private key from crypto_private_key_import() + * @in: Plaintext buffer + * @inlen: Length of plaintext buffer in bytes + * @out: Output buffer for encrypted (signed) data + * @outlen: Length of output buffer in bytes; set to used length on success + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen); + +/** + * crypto_public_key_free - Free public key + * @key: Public key + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_public_key_free(struct crypto_public_key *key); + +/** + * crypto_private_key_free - Free private key + * @key: Private key from crypto_private_key_import() + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_private_key_free(struct crypto_private_key *key); + +/** + * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature + * @key: Public key + * @crypt: Encrypted signature data (using the private key) + * @crypt_len: Encrypted signature data length + * @plain: Buffer for plaintext (at least crypt_len bytes) + * @plain_len: Plaintext length (max buffer size on input, real len on output); + * Returns: 0 on success, -1 on failure + */ +int __must_check crypto_public_key_decrypt_pkcs1( + struct crypto_public_key *key, const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len); + +/** + * crypto_global_init - Initialize crypto wrapper + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_global_init(void); + +/** + * crypto_global_deinit - Deinitialize crypto wrapper + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_global_deinit(void); + +/** + * crypto_mod_exp - Modular exponentiation of large integers + * @base: Base integer (big endian byte array) + * @base_len: Length of base integer in bytes + * @power: Power integer (big endian byte array) + * @power_len: Length of power integer in bytes + * @modulus: Modulus integer (big endian byte array) + * @modulus_len: Length of modulus integer in bytes + * @result: Buffer for the result + * @result_len: Result length (max buffer size on input, real len on output) + * Returns: 0 on success, -1 on failure + * + * This function calculates result = base ^ power mod modulus. modules_len is + * used as the maximum size of modulus buffer. It is set to the used size on + * success. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len); + +/** + * rc4_skip - XOR RC4 stream to given data with skip-stream-start + * @key: RC4 key + * @keylen: RC4 key length + * @skip: number of bytes to skip from the beginning of the RC4 stream + * @data: data to be XOR'ed with RC4 stream + * @data_len: buf length + * Returns: 0 on success, -1 on failure + * + * Generate RC4 pseudo random stream for the given key, skip beginning of the + * stream, and XOR the end result with the data buffer to perform RC4 + * encryption/decryption. + */ +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len); + +/** + * crypto_get_random - Generate cryptographically strong pseudy-random bytes + * @buf: Buffer for data + * @len: Number of bytes to generate + * Returns: 0 on success, -1 on failure + * + * If the PRNG does not have enough entropy to ensure unpredictable byte + * sequence, this functions must return -1. + */ +int crypto_get_random(void *buf, size_t len); + + +/** + * struct crypto_bignum - bignum + * + * Internal data structure for bignum implementation. The contents is specific + * to the used crypto library. + */ +struct crypto_bignum; + +/** + * crypto_bignum_init - Allocate memory for bignum + * Returns: Pointer to allocated bignum or %NULL on failure + */ +struct crypto_bignum * crypto_bignum_init(void); + +/** + * crypto_bignum_init_set - Allocate memory for bignum and set the value + * @buf: Buffer with unsigned binary value + * @len: Length of buf in octets + * Returns: Pointer to allocated bignum or %NULL on failure + */ +struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len); + +/** + * crypto_bignum_deinit - Free bignum + * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set() + * @clear: Whether to clear the value from memory + */ +void crypto_bignum_deinit(struct crypto_bignum *n, int clear); + +/** + * crypto_bignum_to_bin - Set binary buffer to unsigned bignum + * @a: Bignum + * @buf: Buffer for the binary number + * @len: Length of @buf in octets + * @padlen: Length in octets to pad the result to or 0 to indicate no padding + * Returns: Number of octets written on success, -1 on failure + */ +int crypto_bignum_to_bin(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen); + +/** + * crypto_bignum_add - c = a + b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a + b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_add(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_mod - c = a % b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a % b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_mod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c) + * @a: Bignum; base + * @b: Bignum; exponent + * @c: Bignum; modulus + * @d: Bignum; used to store the result of a^b (mod c) + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_exptmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d); + +/** + * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b) + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_inverse(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_sub - c = a - b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a - b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_sub(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_div - c = a / b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a / b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_div(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_mulmod - d = a * b (mod c) + * @a: Bignum + * @b: Bignum + * @c: Bignum + * @d: Bignum; used to store the result of (a * b) % c + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_mulmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d); + +/** + * crypto_bignum_cmp - Compare two bignums + * @a: Bignum + * @b: Bignum + * Returns: -1 if a < b, 0 if a == b, or 1 if a > b + */ +int crypto_bignum_cmp(const struct crypto_bignum *a, + const struct crypto_bignum *b); + +/** + * crypto_bignum_bits - Get size of a bignum in bits + * @a: Bignum + * Returns: Number of bits in the bignum + */ +int crypto_bignum_bits(const struct crypto_bignum *a); + +/** + * crypto_bignum_is_zero - Is the given bignum zero + * @a: Bignum + * Returns: 1 if @a is zero or 0 if not + */ +int crypto_bignum_is_zero(const struct crypto_bignum *a); + +/** + * crypto_bignum_is_one - Is the given bignum one + * @a: Bignum + * Returns: 1 if @a is one or 0 if not + */ +int crypto_bignum_is_one(const struct crypto_bignum *a); + +/** + * crypto_bignum_legendre - Compute the Legendre symbol (a/p) + * @a: Bignum + * @p: Bignum + * Returns: Legendre symbol -1,0,1 on success; -2 on calculation failure + */ +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p); + +/** + * struct crypto_ec - Elliptic curve context + * + * Internal data structure for EC implementation. The contents is specific + * to the used crypto library. + */ +struct crypto_ec; + +/** + * crypto_ec_init - Initialize elliptic curve context + * @group: Identifying number for the ECC group (IANA "Group Description" + * attribute registrty for RFC 2409) + * Returns: Pointer to EC context or %NULL on failure + */ +struct crypto_ec * crypto_ec_init(int group); + +/** + * crypto_ec_deinit - Deinitialize elliptic curve context + * @e: EC context from crypto_ec_init() + */ +void crypto_ec_deinit(struct crypto_ec *e); + +/** + * crypto_ec_prime_len - Get length of the prime in octets + * @e: EC context from crypto_ec_init() + * Returns: Length of the prime defining the group + */ +size_t crypto_ec_prime_len(struct crypto_ec *e); + +/** + * crypto_ec_prime_len_bits - Get length of the prime in bits + * @e: EC context from crypto_ec_init() + * Returns: Length of the prime defining the group in bits + */ +size_t crypto_ec_prime_len_bits(struct crypto_ec *e); + +/** + * crypto_ec_get_prime - Get prime defining an EC group + * @e: EC context from crypto_ec_init() + * Returns: Prime (bignum) defining the group + */ +const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e); + +/** + * crypto_ec_get_order - Get order of an EC group + * @e: EC context from crypto_ec_init() + * Returns: Order (bignum) of the group + */ +const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e); + +/** + * struct crypto_ec_point - Elliptic curve point + * + * Internal data structure for EC implementation to represent a point. The + * contents is specific to the used crypto library. + */ +struct crypto_ec_point; + +/** + * crypto_ec_point_init - Initialize data for an EC point + * @e: EC context from crypto_ec_init() + * Returns: Pointer to EC point data or %NULL on failure + */ +struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e); + +/** + * crypto_ec_point_deinit - Deinitialize EC point data + * @p: EC point data from crypto_ec_point_init() + * @clear: Whether to clear the EC point value from memory + */ +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear); + +/** + * crypto_ec_point_to_bin - Write EC point value as binary data + * @e: EC context from crypto_ec_init() + * @p: EC point data from crypto_ec_point_init() + * @x: Buffer for writing the binary data for x coordinate or %NULL if not used + * @y: Buffer for writing the binary data for y coordinate or %NULL if not used + * Returns: 0 on success, -1 on failure + * + * This function can be used to write an EC point as binary data in a format + * that has the x and y coordinates in big endian byte order fields padded to + * the length of the prime defining the group. + */ +int crypto_ec_point_to_bin(struct crypto_ec *e, + const struct crypto_ec_point *point, u8 *x, u8 *y); + +/** + * crypto_ec_point_from_bin - Create EC point from binary data + * @e: EC context from crypto_ec_init() + * @val: Binary data to read the EC point from + * Returns: Pointer to EC point data or %NULL on failure + * + * This function readers x and y coordinates of the EC point from the provided + * buffer assuming the values are in big endian byte order with fields padded to + * the length of the prime defining the group. + */ +struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, + const u8 *val); + +/** + * crypto_bignum_add - c = a + b + * @e: EC context from crypto_ec_init() + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a + b + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, + const struct crypto_ec_point *b, + struct crypto_ec_point *c); + +/** + * crypto_bignum_mul - res = b * p + * @e: EC context from crypto_ec_init() + * @p: EC point + * @b: Bignum + * @res: EC point; used to store the result of b * p + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, + const struct crypto_bignum *b, + struct crypto_ec_point *res); + +/** + * crypto_ec_point_invert - Compute inverse of an EC point + * @e: EC context from crypto_ec_init() + * @p: EC point to invert (and result of the operation) + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p); + +/** + * crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate + * @e: EC context from crypto_ec_init() + * @p: EC point to use for the returning the result + * @x: x coordinate + * @y_bit: y-bit (0 or 1) for selecting the y value to use + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_solve_y_coord(struct crypto_ec *e, + struct crypto_ec_point *p, + const struct crypto_bignum *x, int y_bit); + +/** + * crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b + * @e: EC context from crypto_ec_init() + * @x: x coordinate + * Returns: y^2 on success, %NULL failure + */ +struct crypto_bignum * +crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x); + +/** + * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element + * @e: EC context from crypto_ec_init() + * @p: EC point + * Returns: 1 if the specified EC point is the neutral element of the group or + * 0 if not + */ +int crypto_ec_point_is_at_infinity(struct crypto_ec *e, + const struct crypto_ec_point *p); + +/** + * crypto_ec_point_is_on_curve - Check whether EC point is on curve + * @e: EC context from crypto_ec_init() + * @p: EC point + * Returns: 1 if the specified EC point is on the curve or 0 if not + */ +int crypto_ec_point_is_on_curve(struct crypto_ec *e, + const struct crypto_ec_point *p); + +/** + * crypto_ec_point_cmp - Compare two EC points + * @e: EC context from crypto_ec_init() + * @a: EC point + * @b: EC point + * Returns: 0 on equal, non-zero otherwise + */ +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b); + +#endif /* CRYPTO_H */ diff --git a/freebsd/contrib/wpa/src/crypto/crypto_openssl.c b/freebsd/contrib/wpa/src/crypto/crypto_openssl.c new file mode 100644 index 00000000..83630422 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/crypto_openssl.c @@ -0,0 +1,1444 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Wrapper functions for OpenSSL libcrypto + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README 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 <openssl/hmac.h> +#include <openssl/rand.h> +#ifdef CONFIG_OPENSSL_CMAC +#include <openssl/cmac.h> +#endif /* CONFIG_OPENSSL_CMAC */ +#ifdef CONFIG_ECC +#include <openssl/ec.h> +#endif /* CONFIG_ECC */ + +#include "common.h" +#include "wpabuf.h" +#include "dh_group5.h" +#include "sha1.h" +#include "sha256.h" +#include "sha384.h" +#include "crypto.h" + +static BIGNUM * get_group5_prime(void) +{ +#ifdef OPENSSL_IS_BORINGSSL + 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_IS_BORINGSSL */ + return get_rfc3526_prime_1536(NULL); +#endif /* OPENSSL_IS_BORINGSSL */ +} + +#ifdef OPENSSL_NO_SHA256 +#define NO_SHA256_WRAPPER +#endif + +static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + EVP_MD_CTX ctx; + size_t i; + unsigned int mac_len; + + EVP_MD_CTX_init(&ctx); + if (!EVP_DigestInit_ex(&ctx, type, NULL)) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + for (i = 0; i < num_elem; i++) { + if (!EVP_DigestUpdate(&ctx, addr[i], len[i])) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate " + "failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + } + if (!EVP_DigestFinal(&ctx, mac, &mac_len)) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + return 0; +} + + +#ifndef CONFIG_FIPS +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac); +} +#endif /* CONFIG_FIPS */ + + +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((DES_cblock *) &pkey, &ks); + DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, + DES_ENCRYPT); +} + + +#ifndef CONFIG_NO_RC4 +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 */ +} +#endif /* CONFIG_NO_RC4 */ + + +#ifndef CONFIG_FIPS +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac); +} +#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(), num_elem, addr, len, mac); +} + + +#ifndef NO_SHA256_WRAPPER +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac); +} +#endif /* NO_SHA256_WRAPPER */ + + +static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen) +{ + switch (keylen) { + case 16: + return EVP_aes_128_ecb(); +#ifndef OPENSSL_IS_BORINGSSL + case 24: + return EVP_aes_192_ecb(); +#endif /* OPENSSL_IS_BORINGSSL */ + case 32: + return EVP_aes_256_ecb(); + } + + return NULL; +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *type; + + type = aes_get_evp_cipher(len); + if (type == NULL) + return NULL; + + ctx = os_malloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + EVP_CIPHER_CTX_init(ctx); + if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + os_free(ctx); + return NULL; + } + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ctx; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + EVP_CIPHER_CTX *c = ctx; + int clen = 16; + if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } +} + + +void aes_encrypt_deinit(void *ctx) +{ + EVP_CIPHER_CTX *c = ctx; + u8 buf[16]; + int len = sizeof(buf); + if (EVP_EncryptFinal_ex(c, buf, &len) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: " + "%s", ERR_error_string(ERR_get_error(), NULL)); + } + if (len != 0) { + wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " + "in AES encrypt", len); + } + EVP_CIPHER_CTX_cleanup(c); + bin_clear_free(c, sizeof(*c)); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *type; + + type = aes_get_evp_cipher(len); + if (type == NULL) + return NULL; + + ctx = os_malloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + EVP_CIPHER_CTX_init(ctx); + if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + os_free(ctx); + return NULL; + } + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ctx; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + EVP_CIPHER_CTX *c = ctx; + int plen = 16; + if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } +} + + +void aes_decrypt_deinit(void *ctx) +{ + EVP_CIPHER_CTX *c = ctx; + u8 buf[16]; + int len = sizeof(buf); + if (EVP_DecryptFinal_ex(c, buf, &len) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: " + "%s", ERR_error_string(ERR_get_error(), NULL)); + } + if (len != 0) { + wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " + "in AES decrypt", len); + } + EVP_CIPHER_CTX_cleanup(c); + bin_clear_free(c, sizeof(*c)); +} + + +#ifndef CONFIG_FIPS +#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP + +int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) +{ + AES_KEY actx; + int res; + + if (AES_set_encrypt_key(kek, kek_len << 3, &actx)) + return -1; + res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8); + OPENSSL_cleanse(&actx, sizeof(actx)); + return res <= 0 ? -1 : 0; +} + + +int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, + u8 *plain) +{ + AES_KEY actx; + int res; + + if (AES_set_decrypt_key(kek, kek_len << 3, &actx)) + return -1; + res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8); + OPENSSL_cleanse(&actx, sizeof(actx)); + return res <= 0 ? -1 : 0; +} + +#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */ +#endif /* CONFIG_FIPS */ + + +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + EVP_CIPHER_CTX ctx; + int clen, len; + u8 buf[16]; + + EVP_CIPHER_CTX_init(&ctx); + if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + return -1; + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + clen = data_len; + if (EVP_EncryptUpdate(&ctx, data, &clen, data, data_len) != 1 || + clen != (int) data_len) + return -1; + + len = sizeof(buf); + if (EVP_EncryptFinal_ex(&ctx, buf, &len) != 1 || len != 0) + return -1; + EVP_CIPHER_CTX_cleanup(&ctx); + + return 0; +} + + +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + EVP_CIPHER_CTX ctx; + int plen, len; + u8 buf[16]; + + EVP_CIPHER_CTX_init(&ctx); + if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + return -1; + EVP_CIPHER_CTX_set_padding(&ctx, 0); + + plen = data_len; + if (EVP_DecryptUpdate(&ctx, data, &plen, data, data_len) != 1 || + plen != (int) data_len) + return -1; + + len = sizeof(buf); + if (EVP_DecryptFinal_ex(&ctx, buf, &len) != 1 || len != 0) + return -1; + EVP_CIPHER_CTX_cleanup(&ctx); + + return 0; +} + + +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_clear_free(bn_base); + BN_clear_free(bn_exp); + BN_clear_free(bn_modulus); + BN_clear_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 CONFIG_NO_RC4 +#ifndef OPENSSL_NO_RC4 + case CRYPTO_CIPHER_ALG_RC4: + cipher = EVP_rc4(); + break; +#endif /* OPENSSL_NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ +#ifndef OPENSSL_NO_AES + case CRYPTO_CIPHER_ALG_AES: + switch (key_len) { + case 16: + cipher = EVP_aes_128_cbc(); + break; +#ifndef OPENSSL_IS_BORINGSSL + case 24: + cipher = EVP_aes_192_cbc(); + break; +#endif /* OPENSSL_IS_BORINGSSL */ + 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_clear_free(pubkey); + wpabuf_clear_free(privkey); + DH_free(dh); + return NULL; +} + + +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) +{ + DH *dh; + + dh = DH_new(); + if (dh == NULL) + return NULL; + + dh->g = BN_new(); + if (dh->g == NULL || BN_set_word(dh->g, 2) != 1) + goto err; + + dh->p = get_group5_prime(); + if (dh->p == NULL) + goto err; + + dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL); + if (dh->priv_key == NULL) + goto err; + + dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL); + if (dh->pub_key == NULL) + goto err; + + if (DH_generate_key(dh) != 1) + goto err; + + return dh; + +err: + DH_free(dh); + return NULL; +} + + +struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, + const struct wpabuf *own_private) +{ + BIGNUM *pub_key; + struct wpabuf *res = NULL; + size_t rlen; + DH *dh = ctx; + int keylen; + + if (ctx == NULL) + return NULL; + + pub_key = BN_bin2bn(wpabuf_head(peer_public), wpabuf_len(peer_public), + NULL); + if (pub_key == NULL) + return NULL; + + rlen = DH_size(dh); + res = wpabuf_alloc(rlen); + if (res == NULL) + goto err; + + keylen = DH_compute_key(wpabuf_mhead(res), pub_key, dh); + if (keylen < 0) + goto err; + wpabuf_put(res, keylen); + BN_clear_free(pub_key); + + return res; + +err: + BN_clear_free(pub_key); + wpabuf_clear_free(res); + return NULL; +} + + +void dh5_free(void *ctx) +{ + DH *dh; + if (ctx == NULL) + return; + dh = ctx; + DH_free(dh); +} + + +struct crypto_hash { + HMAC_CTX ctx; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + const EVP_MD *md; + + switch (alg) { +#ifndef OPENSSL_NO_MD5 + case CRYPTO_HASH_ALG_HMAC_MD5: + md = EVP_md5(); + break; +#endif /* OPENSSL_NO_MD5 */ +#ifndef OPENSSL_NO_SHA + case CRYPTO_HASH_ALG_HMAC_SHA1: + md = EVP_sha1(); + break; +#endif /* OPENSSL_NO_SHA */ +#ifndef OPENSSL_NO_SHA256 +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + md = EVP_sha256(); + break; +#endif /* CONFIG_SHA256 */ +#endif /* OPENSSL_NO_SHA256 */ + default: + return NULL; + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + HMAC_CTX_init(&ctx->ctx); + +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) { + bin_clear_free(ctx, sizeof(*ctx)); + return NULL; + } +#endif /* openssl < 0.9.9 */ + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (ctx == NULL) + return; + HMAC_Update(&ctx->ctx, data, len); +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + unsigned int mdlen; + int res; + + if (ctx == NULL) + return -2; + + if (mac == NULL || len == NULL) { + bin_clear_free(ctx, sizeof(*ctx)); + return 0; + } + + mdlen = *len; +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx->ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx->ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx->ctx); + bin_clear_free(ctx, sizeof(*ctx)); + + if (res == 1) { + *len = mdlen; + return 0; + } + + return -1; +} + + +static int openssl_hmac_vector(const EVP_MD *type, const u8 *key, + size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac, + unsigned int mdlen) +{ + HMAC_CTX ctx; + size_t i; + int res; + + HMAC_CTX_init(&ctx); +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx, key, key_len, type, NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx, key, key_len, type, NULL) != 1) + return -1; +#endif /* openssl < 0.9.9 */ + + for (i = 0; i < num_elem; i++) + HMAC_Update(&ctx, addr[i], len[i]); + +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx); + + return res == 1 ? 0 : -1; +} + + +#ifndef CONFIG_FIPS + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_hmac_vector(EVP_md5(), key ,key_len, num_elem, addr, len, + mac, 16); +} + + +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); +} + +#endif /* CONFIG_FIPS */ + + +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ + if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid, + ssid_len, iterations, buflen, buf) != 1) + return -1; + return 0; +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_hmac_vector(EVP_sha1(), key, key_len, num_elem, addr, + len, mac, 20); +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_hmac_vector(EVP_sha256(), key, key_len, num_elem, addr, + len, mac, 32); +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +#ifdef CONFIG_SHA384 + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr, + len, mac, 32); +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA384 */ + + +int crypto_get_random(void *buf, size_t len) +{ + if (RAND_bytes(buf, len) != 1) + return -1; + return 0; +} + + +#ifdef CONFIG_OPENSSL_CMAC +int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + CMAC_CTX *ctx; + int ret = -1; + size_t outlen, i; + + ctx = CMAC_CTX_new(); + if (ctx == NULL) + return -1; + + if (key_len == 32) { + if (!CMAC_Init(ctx, key, 32, EVP_aes_256_cbc(), NULL)) + goto fail; + } else if (key_len == 16) { + if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL)) + goto fail; + } else { + goto fail; + } + for (i = 0; i < num_elem; i++) { + if (!CMAC_Update(ctx, addr[i], len[i])) + goto fail; + } + if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16) + goto fail; + + ret = 0; +fail: + CMAC_CTX_free(ctx); + return ret; +} + + +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return omac1_aes_vector(key, 16, num_elem, addr, len, mac); +} + + +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); +} + + +int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_vector(key, 32, 1, &data, &data_len, mac); +} +#endif /* CONFIG_OPENSSL_CMAC */ + + +struct crypto_bignum * crypto_bignum_init(void) +{ + return (struct crypto_bignum *) BN_new(); +} + + +struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len) +{ + BIGNUM *bn = BN_bin2bn(buf, len, NULL); + return (struct crypto_bignum *) bn; +} + + +void crypto_bignum_deinit(struct crypto_bignum *n, int clear) +{ + if (clear) + BN_clear_free((BIGNUM *) n); + else + BN_free((BIGNUM *) n); +} + + +int crypto_bignum_to_bin(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen) +{ + int num_bytes, offset; + + if (padlen > buflen) + return -1; + + num_bytes = BN_num_bytes((const BIGNUM *) a); + if ((size_t) num_bytes > buflen) + return -1; + if (padlen > (size_t) num_bytes) + offset = padlen - num_bytes; + else + offset = 0; + + os_memset(buf, 0, offset); + BN_bn2bin((const BIGNUM *) a, buf + offset); + + return num_bytes + offset; +} + + +int crypto_bignum_add(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + return BN_add((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ? + 0 : -1; +} + + +int crypto_bignum_mod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + int res; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_mod((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b, + bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_exptmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d) +{ + int res; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b, + (const BIGNUM *) c, bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_inverse(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + BIGNUM *res; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a, + (const BIGNUM *) b, bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_sub(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ? + 0 : -1; +} + + +int crypto_bignum_div(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + int res; + + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a, + (const BIGNUM *) b, bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_mulmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d) +{ + int res; + + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_mod_mul((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b, + (const BIGNUM *) c, bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_cmp(const struct crypto_bignum *a, + const struct crypto_bignum *b) +{ + return BN_cmp((const BIGNUM *) a, (const BIGNUM *) b); +} + + +int crypto_bignum_bits(const struct crypto_bignum *a) +{ + return BN_num_bits((const BIGNUM *) a); +} + + +int crypto_bignum_is_zero(const struct crypto_bignum *a) +{ + return BN_is_zero((const BIGNUM *) a); +} + + +int crypto_bignum_is_one(const struct crypto_bignum *a) +{ + return BN_is_one((const BIGNUM *) a); +} + + +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p) +{ + BN_CTX *bnctx; + BIGNUM *exp = NULL, *tmp = NULL; + int res = -2; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -2; + + exp = BN_new(); + tmp = BN_new(); + if (!exp || !tmp || + /* exp = (p-1) / 2 */ + !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) || + !BN_rshift1(exp, exp) || + !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p, + bnctx)) + goto fail; + + if (BN_is_word(tmp, 1)) + res = 1; + else if (BN_is_zero(tmp)) + res = 0; + else + res = -1; + +fail: + BN_clear_free(tmp); + BN_clear_free(exp); + BN_CTX_free(bnctx); + return res; +} + + +#ifdef CONFIG_ECC + +struct crypto_ec { + EC_GROUP *group; + BN_CTX *bnctx; + BIGNUM *prime; + BIGNUM *order; + BIGNUM *a; + BIGNUM *b; +}; + +struct crypto_ec * crypto_ec_init(int group) +{ + struct crypto_ec *e; + int nid; + + /* Map from IANA registry for IKE D-H groups to OpenSSL NID */ + switch (group) { + case 19: + nid = NID_X9_62_prime256v1; + break; + case 20: + nid = NID_secp384r1; + break; + case 21: + nid = NID_secp521r1; + break; + case 25: + nid = NID_X9_62_prime192v1; + break; + case 26: + nid = NID_secp224r1; + break; +#ifdef NID_brainpoolP224r1 + case 27: + nid = NID_brainpoolP224r1; + break; +#endif /* NID_brainpoolP224r1 */ +#ifdef NID_brainpoolP256r1 + case 28: + nid = NID_brainpoolP256r1; + break; +#endif /* NID_brainpoolP256r1 */ +#ifdef NID_brainpoolP384r1 + case 29: + nid = NID_brainpoolP384r1; + break; +#endif /* NID_brainpoolP384r1 */ +#ifdef NID_brainpoolP512r1 + case 30: + nid = NID_brainpoolP512r1; + break; +#endif /* NID_brainpoolP512r1 */ + default: + return NULL; + } + + e = os_zalloc(sizeof(*e)); + if (e == NULL) + return NULL; + + e->bnctx = BN_CTX_new(); + e->group = EC_GROUP_new_by_curve_name(nid); + e->prime = BN_new(); + e->order = BN_new(); + e->a = BN_new(); + e->b = BN_new(); + if (e->group == NULL || e->bnctx == NULL || e->prime == NULL || + e->order == NULL || e->a == NULL || e->b == NULL || + !EC_GROUP_get_curve_GFp(e->group, e->prime, e->a, e->b, e->bnctx) || + !EC_GROUP_get_order(e->group, e->order, e->bnctx)) { + crypto_ec_deinit(e); + e = NULL; + } + + return e; +} + + +void crypto_ec_deinit(struct crypto_ec *e) +{ + if (e == NULL) + return; + BN_clear_free(e->b); + BN_clear_free(e->a); + BN_clear_free(e->order); + BN_clear_free(e->prime); + EC_GROUP_free(e->group); + BN_CTX_free(e->bnctx); + os_free(e); +} + + +struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) +{ + if (e == NULL) + return NULL; + return (struct crypto_ec_point *) EC_POINT_new(e->group); +} + + +size_t crypto_ec_prime_len(struct crypto_ec *e) +{ + return BN_num_bytes(e->prime); +} + + +size_t crypto_ec_prime_len_bits(struct crypto_ec *e) +{ + return BN_num_bits(e->prime); +} + + +const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) e->prime; +} + + +const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) e->order; +} + + +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) +{ + if (clear) + EC_POINT_clear_free((EC_POINT *) p); + else + EC_POINT_free((EC_POINT *) p); +} + + +int crypto_ec_point_to_bin(struct crypto_ec *e, + const struct crypto_ec_point *point, u8 *x, u8 *y) +{ + BIGNUM *x_bn, *y_bn; + int ret = -1; + int len = BN_num_bytes(e->prime); + + x_bn = BN_new(); + y_bn = BN_new(); + + if (x_bn && y_bn && + EC_POINT_get_affine_coordinates_GFp(e->group, (EC_POINT *) point, + x_bn, y_bn, e->bnctx)) { + if (x) { + crypto_bignum_to_bin((struct crypto_bignum *) x_bn, + x, len, len); + } + if (y) { + crypto_bignum_to_bin((struct crypto_bignum *) y_bn, + y, len, len); + } + ret = 0; + } + + BN_clear_free(x_bn); + BN_clear_free(y_bn); + return ret; +} + + +struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, + const u8 *val) +{ + BIGNUM *x, *y; + EC_POINT *elem; + int len = BN_num_bytes(e->prime); + + x = BN_bin2bn(val, len, NULL); + y = BN_bin2bn(val + len, len, NULL); + elem = EC_POINT_new(e->group); + if (x == NULL || y == NULL || elem == NULL) { + BN_clear_free(x); + BN_clear_free(y); + EC_POINT_clear_free(elem); + return NULL; + } + + if (!EC_POINT_set_affine_coordinates_GFp(e->group, elem, x, y, + e->bnctx)) { + EC_POINT_clear_free(elem); + elem = NULL; + } + + BN_clear_free(x); + BN_clear_free(y); + + return (struct crypto_ec_point *) elem; +} + + +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, + const struct crypto_ec_point *b, + struct crypto_ec_point *c) +{ + return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a, + (const EC_POINT *) b, e->bnctx) ? 0 : -1; +} + + +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, + const struct crypto_bignum *b, + struct crypto_ec_point *res) +{ + return EC_POINT_mul(e->group, (EC_POINT *) res, NULL, + (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx) + ? 0 : -1; +} + + +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) +{ + return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1; +} + + +int crypto_ec_point_solve_y_coord(struct crypto_ec *e, + struct crypto_ec_point *p, + const struct crypto_bignum *x, int y_bit) +{ + if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p, + (const BIGNUM *) x, y_bit, + e->bnctx) || + !EC_POINT_is_on_curve(e->group, (EC_POINT *) p, e->bnctx)) + return -1; + return 0; +} + + +struct crypto_bignum * +crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x) +{ + BIGNUM *tmp, *tmp2, *y_sqr = NULL; + + tmp = BN_new(); + tmp2 = BN_new(); + + /* y^2 = x^3 + ax + b */ + if (tmp && tmp2 && + BN_mod_sqr(tmp, (const BIGNUM *) x, e->prime, e->bnctx) && + BN_mod_mul(tmp, tmp, (const BIGNUM *) x, e->prime, e->bnctx) && + BN_mod_mul(tmp2, e->a, (const BIGNUM *) x, e->prime, e->bnctx) && + BN_mod_add_quick(tmp2, tmp2, tmp, e->prime) && + BN_mod_add_quick(tmp2, tmp2, e->b, e->prime)) { + y_sqr = tmp2; + tmp2 = NULL; + } + + BN_clear_free(tmp); + BN_clear_free(tmp2); + + return (struct crypto_bignum *) y_sqr; +} + + +int crypto_ec_point_is_at_infinity(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return EC_POINT_is_at_infinity(e->group, (const EC_POINT *) p); +} + + +int crypto_ec_point_is_on_curve(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, + e->bnctx) == 1; +} + + +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b) +{ + return EC_POINT_cmp(e->group, (const EC_POINT *) a, + (const EC_POINT *) b, e->bnctx); +} + +#endif /* CONFIG_ECC */ diff --git a/freebsd/contrib/wpa/src/crypto/dh_group5.h b/freebsd/contrib/wpa/src/crypto/dh_group5.h new file mode 100644 index 00000000..abee8eaa --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/dh_group5.h @@ -0,0 +1,18 @@ +/* + * Diffie-Hellman group 5 operations + * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DH_GROUP5_H +#define DH_GROUP5_H + +void * dh5_init(struct wpabuf **priv, struct wpabuf **publ); +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ); +struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, + const struct wpabuf *own_private); +void dh5_free(void *ctx); + +#endif /* DH_GROUP5_H */ diff --git a/freebsd/contrib/wpa/src/crypto/md5.h b/freebsd/contrib/wpa/src/crypto/md5.h new file mode 100644 index 00000000..33f8426c --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/md5.h @@ -0,0 +1,19 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MD5_H +#define MD5_H + +#define MD5_MAC_LEN 16 + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); + +#endif /* MD5_H */ diff --git a/freebsd/contrib/wpa/src/crypto/ms_funcs.c b/freebsd/contrib/wpa/src/crypto/ms_funcs.c new file mode 100644 index 00000000..dda2ba7c --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/ms_funcs.c @@ -0,0 +1,526 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "ms_funcs.h" +#include "crypto.h" + +/** + * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding + * @utf8_string: UTF-8 string (IN) + * @utf8_string_len: Length of utf8_string (IN) + * @ucs2_buffer: UCS-2 buffer (OUT) + * @ucs2_buffer_size: Length of UCS-2 buffer (IN) + * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string + * Returns: 0 on success, -1 on failure + */ +static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, + u8 *ucs2_buffer, size_t ucs2_buffer_size, + size_t *ucs2_string_size) +{ + size_t i, j; + + for (i = 0, j = 0; i < utf8_string_len; i++) { + u8 c = utf8_string[i]; + if (j >= ucs2_buffer_size) { + /* input too long */ + return -1; + } + if (c <= 0x7F) { + WPA_PUT_LE16(ucs2_buffer + j, c); + j += 2; + } else if (i == utf8_string_len - 1 || + j >= ucs2_buffer_size - 1) { + /* incomplete surrogate */ + return -1; + } else { + u8 c2 = utf8_string[++i]; + if ((c & 0xE0) == 0xC0) { + /* two-byte encoding */ + WPA_PUT_LE16(ucs2_buffer + j, + ((c & 0x1F) << 6) | (c2 & 0x3F)); + j += 2; + } else if (i == utf8_string_len || + j >= ucs2_buffer_size - 1) { + /* incomplete surrogate */ + return -1; + } else { + /* three-byte encoding */ + u8 c3 = utf8_string[++i]; + WPA_PUT_LE16(ucs2_buffer + j, + ((c & 0xF) << 12) | + ((c2 & 0x3F) << 6) | (c3 & 0x3F)); + j += 2; + } + } + } + + if (ucs2_string_size) + *ucs2_string_size = j / 2; + return 0; +} + + +/** + * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2 + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @challenge: 8-octet Challenge (OUT) + * Returns: 0 on success, -1 on failure + */ +int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, u8 *challenge) +{ + u8 hash[SHA1_MAC_LEN]; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = peer_challenge; + len[0] = 16; + addr[1] = auth_challenge; + len[1] = 16; + addr[2] = username; + len[2] = username_len; + + if (sha1_vector(3, addr, len, hash)) + return -1; + os_memcpy(challenge, hash, 8); + return 0; +} + + +/** + * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3 + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @password_hash: 16-octet PasswordHash (OUT) + * Returns: 0 on success, -1 on failure + */ +int nt_password_hash(const u8 *password, size_t password_len, + u8 *password_hash) +{ + u8 buf[512], *pos; + size_t len, max_len; + + max_len = sizeof(buf); + if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0) + return -1; + + len *= 2; + pos = buf; + return md4_vector(1, (const u8 **) &pos, &len, password_hash); +} + + +/** + * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4 + * @password_hash: 16-octet PasswordHash (IN) + * @password_hash_hash: 16-octet PasswordHashHash (OUT) + * Returns: 0 on success, -1 on failure + */ +int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) +{ + size_t len = 16; + return md4_vector(1, &password_hash, &len, password_hash_hash); +} + + +/** + * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5 + * @challenge: 8-octet Challenge (IN) + * @password_hash: 16-octet PasswordHash (IN) + * @response: 24-octet Response (OUT) + */ +void challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response) +{ + u8 zpwd[7]; + des_encrypt(challenge, password_hash, response); + des_encrypt(challenge, password_hash + 7, response + 8); + zpwd[0] = password_hash[14]; + zpwd[1] = password_hash[15]; + os_memset(zpwd + 2, 0, 5); + des_encrypt(challenge, zpwd, response + 16); +} + + +/** + * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure + */ +int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password, size_t password_len, + u8 *response) +{ + u8 challenge[8]; + u8 password_hash[16]; + + if (challenge_hash(peer_challenge, auth_challenge, username, + username_len, challenge) || + 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) || + sha1_vector(3, addr1, len1, response) || + challenge_hash(peer_challenge, auth_challenge, username, + username_len, challenge)) + return -1; + return sha1_vector(3, addr2, len2, response); +} + + +/** + * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @nt_response: 24-octet NT-Response (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually + * encoded as a 42-octet ASCII string (S=hexdump_of_response) + * Returns: 0 on success, -1 on failure + */ +int generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response) +{ + u8 password_hash[16]; + if (nt_password_hash(password, password_len, password_hash)) + return -1; + return generate_authenticator_response_pwhash( + password_hash, peer_challenge, auth_challenge, + username, username_len, nt_response, response); +} + + +/** + * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5 + * @challenge: 8-octet Challenge (IN) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure + */ +int nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response) +{ + u8 password_hash[16]; + if (nt_password_hash(password, password_len, password_hash)) + return -1; + challenge_response(challenge, password_hash, response); + return 0; +} + + +/** + * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4 + * @password_hash_hash: 16-octet PasswordHashHash (IN) + * @nt_response: 24-octet NTResponse (IN) + * @master_key: 16-octet MasterKey (OUT) + * Returns: 0 on success, -1 on failure + */ +int get_master_key(const u8 *password_hash_hash, const u8 *nt_response, + u8 *master_key) +{ + static const u8 magic1[27] = { + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 + }; + const unsigned char *addr[3]; + const size_t len[3] = { 16, 24, sizeof(magic1) }; + u8 hash[SHA1_MAC_LEN]; + + addr[0] = password_hash_hash; + addr[1] = nt_response; + addr[2] = magic1; + + if (sha1_vector(3, addr, len, hash)) + return -1; + os_memcpy(master_key, hash, 16); + return 0; +} + + +/** + * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4 + * @master_key: 16-octet MasterKey (IN) + * @session_key: 8-to-16 octet SessionKey (OUT) + * @session_key_len: SessionKeyLength (Length of session_key) (IN) + * @is_send: IsSend (IN, BOOLEAN) + * @is_server: IsServer (IN, BOOLEAN) + * Returns: 0 on success, -1 on failure + */ +int get_asymetric_start_key(const u8 *master_key, u8 *session_key, + size_t session_key_len, int is_send, + int is_server) +{ + static const u8 magic2[84] = { + 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, + 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, + 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x2e + }; + static const u8 magic3[84] = { + 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, + 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, + 0x6b, 0x65, 0x79, 0x2e + }; + static const u8 shs_pad1[40] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const u8 shs_pad2[40] = { + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 + }; + u8 digest[SHA1_MAC_LEN]; + const unsigned char *addr[4]; + const size_t len[4] = { 16, 40, 84, 40 }; + + addr[0] = master_key; + addr[1] = shs_pad1; + if (is_send) { + addr[2] = is_server ? magic3 : magic2; + } else { + addr[2] = is_server ? magic2 : magic3; + } + addr[3] = shs_pad2; + + if (sha1_vector(4, addr, len, digest)) + return -1; + + if (session_key_len > SHA1_MAC_LEN) + session_key_len = SHA1_MAC_LEN; + os_memcpy(session_key, digest, session_key_len); + return 0; +} + + +#ifndef CONFIG_NO_RC4 + +#define PWBLOCK_LEN 516 + +/** + * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10 + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @password_hash: 16-octet PasswordHash (IN) + * @pw_block: 516-byte PwBlock (OUT) + * Returns: 0 on success, -1 on failure + */ +int encrypt_pw_block_with_password_hash( + const u8 *password, size_t password_len, + const u8 *password_hash, u8 *pw_block) +{ + size_t ucs2_len, offset; + u8 *pos; + + os_memset(pw_block, 0, PWBLOCK_LEN); + + if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0 + || ucs2_len > 256) + return -1; + + offset = (256 - ucs2_len) * 2; + if (offset != 0) { + os_memmove(pw_block + offset, pw_block, ucs2_len * 2); + if (os_get_random(pw_block, offset) < 0) + return -1; + } + /* + * PasswordLength is 4 octets, but since the maximum password length is + * 256, only first two (in little endian byte order) can be non-zero. + */ + pos = &pw_block[2 * 256]; + WPA_PUT_LE16(pos, password_len * 2); + rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN); + return 0; +} + + +/** + * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9 + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8) + * @new_password_len: Length of new_password + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8) + * @old_password_len: Length of old_password + * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT) + * Returns: 0 on success, -1 on failure + */ +int new_password_encrypted_with_old_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_pw_block) +{ + u8 password_hash[16]; + + if (nt_password_hash(old_password, old_password_len, password_hash)) + return -1; + if (encrypt_pw_block_with_password_hash(new_password, new_password_len, + password_hash, + encrypted_pw_block)) + return -1; + return 0; +} + +#endif /* CONFIG_NO_RC4 */ + + +/** + * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13 + * @password_hash: 16-octer PasswordHash (IN) + * @block: 16-octet Block (IN) + * @cypher: 16-octer Cypher (OUT) + */ +void nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, u8 *cypher) +{ + des_encrypt(password_hash, block, cypher); + des_encrypt(password_hash + 8, block + 7, cypher + 8); +} + + +/** + * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12 + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8) + * @new_password_len: Length of new_password + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8) + * @old_password_len: Length of old_password + * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT) + * Returns: 0 on success, -1 on failure + */ +int old_nt_password_hash_encrypted_with_new_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_password_hash) +{ + u8 old_password_hash[16], new_password_hash[16]; + + if (nt_password_hash(old_password, old_password_len, + old_password_hash) || + nt_password_hash(new_password, new_password_len, + new_password_hash)) + return -1; + nt_password_hash_encrypted_with_block(old_password_hash, + new_password_hash, + encrypted_password_hash); + return 0; +} diff --git a/freebsd/contrib/wpa/src/crypto/ms_funcs.h b/freebsd/contrib/wpa/src/crypto/ms_funcs.h new file mode 100644 index 00000000..b5b5918e --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/ms_funcs.h @@ -0,0 +1,60 @@ +/* + * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MS_FUNCS_H +#define MS_FUNCS_H + +int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password, size_t password_len, + u8 *response); +int generate_nt_response_pwhash(const u8 *auth_challenge, + const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password_hash, + u8 *response); +int generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response); +int generate_authenticator_response_pwhash( + const u8 *password_hash, + const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response); +int nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response); + +void challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response); +int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, u8 *challenge); +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/freebsd/contrib/wpa/src/crypto/random.c b/freebsd/contrib/wpa/src/crypto/random.c new file mode 100644 index 00000000..57880e31 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/random.c @@ -0,0 +1,441 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Random number generator + * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This random number generator is used to provide additional entropy to the + * one provided by the operating system (os_get_random()) for session key + * generation. The os_get_random() output is expected to be secure and the + * implementation here is expected to provide only limited protection against + * cases where os_get_random() cannot provide strong randomness. This + * implementation shall not be assumed to be secure as the sole source of + * randomness. The random_get_bytes() function mixes in randomness from + * os_get_random() and as such, calls to os_get_random() can be replaced with + * calls to random_get_bytes() without reducing security. + * + * The design here follows partially the design used in the Linux + * drivers/char/random.c, but the implementation here is simpler and not as + * strong. This is a compromise to reduce duplicated CPU effort and to avoid + * extra code/memory size. As pointed out above, os_get_random() needs to be + * guaranteed to be secure for any of the security assumptions to hold. + */ + +#include "utils/includes.h" +#ifdef __linux__ +#include <fcntl.h> +#endif /* __linux__ */ + +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/crypto.h" +#include "sha1.h" +#include "random.h" + +#define POOL_WORDS 32 +#define POOL_WORDS_MASK (POOL_WORDS - 1) +#define POOL_TAP1 26 +#define POOL_TAP2 20 +#define POOL_TAP3 14 +#define POOL_TAP4 7 +#define POOL_TAP5 1 +#define EXTRACT_LEN 16 +#define MIN_READY_MARK 2 + +static u32 pool[POOL_WORDS]; +static unsigned int input_rotate = 0; +static unsigned int pool_pos = 0; +static u8 dummy_key[20]; +#ifdef __linux__ +static size_t dummy_key_avail = 0; +static int random_fd = -1; +#endif /* __linux__ */ +static unsigned int own_pool_ready = 0; +#define RANDOM_ENTROPY_SIZE 20 +static char *random_entropy_file = NULL; +static int random_entropy_file_read = 0; + +#define MIN_COLLECT_ENTROPY 1000 +static unsigned int entropy = 0; +static unsigned int total_collected = 0; + + +static void random_write_entropy(void); + + +static u32 __ROL32(u32 x, u32 y) +{ + return (x << (y & 31)) | (x >> (32 - (y & 31))); +} + + +static void random_mix_pool(const void *buf, size_t len) +{ + static const u32 twist[8] = { + 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158, + 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 + }; + const u8 *pos = buf; + u32 w; + + wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len); + + while (len--) { + w = __ROL32(*pos++, input_rotate & 31); + input_rotate += pool_pos ? 7 : 14; + pool_pos = (pool_pos - 1) & POOL_WORDS_MASK; + w ^= pool[pool_pos]; + w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK]; + pool[pool_pos] = (w >> 3) ^ twist[w & 7]; + } +} + + +static void random_extract(u8 *out) +{ + unsigned int i; + u8 hash[SHA1_MAC_LEN]; + u32 *hash_ptr; + u32 buf[POOL_WORDS / 2]; + + /* First, add hash back to pool to make backtracking more difficult. */ + hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) pool, + sizeof(pool), hash); + random_mix_pool(hash, sizeof(hash)); + /* Hash half the pool to extra data */ + for (i = 0; i < POOL_WORDS / 2; i++) + buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK]; + hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) buf, + sizeof(buf), hash); + /* + * Fold the hash to further reduce any potential output pattern. + * Though, compromise this to reduce CPU use for the most common output + * length (32) and return 16 bytes from instead of only half. + */ + hash_ptr = (u32 *) hash; + hash_ptr[0] ^= hash_ptr[4]; + os_memcpy(out, hash, EXTRACT_LEN); +} + + +void random_add_randomness(const void *buf, size_t len) +{ + struct os_time t; + static unsigned int count = 0; + + count++; + if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) { + /* + * No need to add more entropy at this point, so save CPU and + * skip the update. + */ + return; + } + wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u", + count, entropy); + + os_get_time(&t); + wpa_hexdump_key(MSG_EXCESSIVE, "random pool", + (const u8 *) pool, sizeof(pool)); + random_mix_pool(&t, sizeof(t)); + random_mix_pool(buf, len); + wpa_hexdump_key(MSG_EXCESSIVE, "random pool", + (const u8 *) pool, sizeof(pool)); + entropy++; + total_collected++; +} + + +int random_get_bytes(void *buf, size_t len) +{ + int ret; + u8 *bytes = buf; + size_t left; + + wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u", + (unsigned int) len, entropy); + + /* Start with assumed strong randomness from OS */ + ret = os_get_random(buf, len); + wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random", + buf, len); + + /* Mix in additional entropy extracted from the internal pool */ + left = len; + while (left) { + size_t siz, i; + u8 tmp[EXTRACT_LEN]; + random_extract(tmp); + wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool", + tmp, sizeof(tmp)); + siz = left > EXTRACT_LEN ? EXTRACT_LEN : left; + for (i = 0; i < siz; i++) + *bytes++ ^= tmp[i]; + left -= siz; + } + +#ifdef CONFIG_FIPS + /* Mix in additional entropy from the crypto module */ + bytes = buf; + left = len; + while (left) { + size_t siz, i; + u8 tmp[EXTRACT_LEN]; + if (crypto_get_random(tmp, sizeof(tmp)) < 0) { + wpa_printf(MSG_ERROR, "random: No entropy available " + "for generating strong random bytes"); + return -1; + } + wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module", + tmp, sizeof(tmp)); + siz = left > EXTRACT_LEN ? EXTRACT_LEN : left; + for (i = 0; i < siz; i++) + *bytes++ ^= tmp[i]; + left -= siz; + } +#endif /* CONFIG_FIPS */ + + wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len); + + if (entropy < len) + entropy = 0; + else + entropy -= len; + + return ret; +} + + +int random_pool_ready(void) +{ +#ifdef __linux__ + int fd; + ssize_t res; + + /* + * Make sure that there is reasonable entropy available before allowing + * some key derivation operations to proceed. + */ + + if (dummy_key_avail == sizeof(dummy_key)) + return 1; /* Already initialized - good to continue */ + + /* + * Try to fetch some more data from the kernel high quality + * /dev/random. There may not be enough data available at this point, + * so use non-blocking read to avoid blocking the application + * completely. + */ + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd < 0) { + wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", + strerror(errno)); + return -1; + } + + res = read(fd, dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail); + if (res < 0) { + wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " + "%s", strerror(errno)); + res = 0; + } + wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from " + "/dev/random", (unsigned) res, + (unsigned) (sizeof(dummy_key) - dummy_key_avail)); + dummy_key_avail += res; + close(fd); + + if (dummy_key_avail == sizeof(dummy_key)) { + if (own_pool_ready < MIN_READY_MARK) + own_pool_ready = MIN_READY_MARK; + random_write_entropy(); + return 1; + } + + wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong " + "random data available from /dev/random", + (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key)); + + if (own_pool_ready >= MIN_READY_MARK || + total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) { + wpa_printf(MSG_INFO, "random: Allow operation to proceed " + "based on internal entropy"); + return 1; + } + + wpa_printf(MSG_INFO, "random: Not enough entropy pool available for " + "secure operations"); + return 0; +#else /* __linux__ */ + /* TODO: could do similar checks on non-Linux platforms */ + return 1; +#endif /* __linux__ */ +} + + +void random_mark_pool_ready(void) +{ + own_pool_ready++; + wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be " + "ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK); + random_write_entropy(); +} + + +#ifdef __linux__ + +static void random_close_fd(void) +{ + if (random_fd >= 0) { + eloop_unregister_read_sock(random_fd); + close(random_fd); + random_fd = -1; + } +} + + +static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx) +{ + ssize_t res; + + if (dummy_key_avail == sizeof(dummy_key)) { + random_close_fd(); + return; + } + + res = read(sock, dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail); + if (res < 0) { + wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " + "%s", strerror(errno)); + return; + } + + wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random", + (unsigned) res, + (unsigned) (sizeof(dummy_key) - dummy_key_avail)); + dummy_key_avail += res; + + if (dummy_key_avail == sizeof(dummy_key)) { + random_close_fd(); + if (own_pool_ready < MIN_READY_MARK) + own_pool_ready = MIN_READY_MARK; + random_write_entropy(); + } +} + +#endif /* __linux__ */ + + +static void random_read_entropy(void) +{ + char *buf; + size_t len; + + if (!random_entropy_file) + return; + + buf = os_readfile(random_entropy_file, &len); + if (buf == NULL) + return; /* entropy file not yet available */ + + if (len != 1 + RANDOM_ENTROPY_SIZE) { + wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s", + random_entropy_file); + os_free(buf); + return; + } + + own_pool_ready = (u8) buf[0]; + random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE); + random_entropy_file_read = 1; + os_free(buf); + wpa_printf(MSG_DEBUG, "random: Added entropy from %s " + "(own_pool_ready=%u)", + random_entropy_file, own_pool_ready); +} + + +static void random_write_entropy(void) +{ + char buf[RANDOM_ENTROPY_SIZE]; + FILE *f; + u8 opr; + int fail = 0; + + if (!random_entropy_file) + return; + + if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0) + return; + + f = fopen(random_entropy_file, "wb"); + if (f == NULL) { + wpa_printf(MSG_ERROR, "random: Could not open entropy file %s " + "for writing", random_entropy_file); + return; + } + + opr = own_pool_ready > 0xff ? 0xff : own_pool_ready; + if (fwrite(&opr, 1, 1, f) != 1 || + fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1) + fail = 1; + fclose(f); + if (fail) { + wpa_printf(MSG_ERROR, "random: Could not write entropy data " + "to %s", random_entropy_file); + return; + } + + wpa_printf(MSG_DEBUG, "random: Updated entropy file %s " + "(own_pool_ready=%u)", + random_entropy_file, own_pool_ready); +} + + +void random_init(const char *entropy_file) +{ + os_free(random_entropy_file); + if (entropy_file) + random_entropy_file = os_strdup(entropy_file); + else + random_entropy_file = NULL; + random_read_entropy(); + +#ifdef __linux__ + if (random_fd >= 0) + return; + + random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (random_fd < 0) { + wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", + strerror(errno)); + return; + } + wpa_printf(MSG_DEBUG, "random: Trying to read entropy from " + "/dev/random"); + + eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL); +#endif /* __linux__ */ + + random_write_entropy(); +} + + +void random_deinit(void) +{ +#ifdef __linux__ + random_close_fd(); +#endif /* __linux__ */ + random_write_entropy(); + os_free(random_entropy_file); + random_entropy_file = NULL; +} diff --git a/freebsd/contrib/wpa/src/crypto/random.h b/freebsd/contrib/wpa/src/crypto/random.h new file mode 100644 index 00000000..d13e1c49 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/random.h @@ -0,0 +1,28 @@ +/* + * Random number generator + * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RANDOM_H +#define RANDOM_H + +#ifdef CONFIG_NO_RANDOM_POOL +#define random_init(e) do { } while (0) +#define random_deinit() do { } while (0) +#define random_add_randomness(b, l) do { } while (0) +#define random_get_bytes(b, l) os_get_random((b), (l)) +#define random_pool_ready() 1 +#define random_mark_pool_ready() do { } while (0) +#else /* CONFIG_NO_RANDOM_POOL */ +void random_init(const char *entropy_file); +void random_deinit(void); +void random_add_randomness(const void *buf, size_t len); +int random_get_bytes(void *buf, size_t len); +int random_pool_ready(void); +void random_mark_pool_ready(void); +#endif /* CONFIG_NO_RANDOM_POOL */ + +#endif /* RANDOM_H */ diff --git a/freebsd/contrib/wpa/src/crypto/rc4.c b/freebsd/contrib/wpa/src/crypto/rc4.c new file mode 100644 index 00000000..061160ea --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/rc4.c @@ -0,0 +1,56 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * RC4 stream cipher + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" + +#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) +{ + u32 i, j, k; + u8 S[256], *pos; + size_t kpos; + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + kpos = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[kpos]) & 0xff; + kpos++; + if (kpos >= keylen) + kpos = 0; + S_SWAP(i, j); + } + + /* Skip the start of the stream */ + i = j = 0; + for (k = 0; k < skip; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + } + + /* Apply RC4 to data */ + pos = data; + for (k = 0; k < data_len; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + + return 0; +} diff --git a/freebsd/contrib/wpa/src/crypto/sha1-pbkdf2.c b/freebsd/contrib/wpa/src/crypto/sha1-pbkdf2.c new file mode 100644 index 00000000..121b642e --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/sha1-pbkdf2.c @@ -0,0 +1,94 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * SHA1-based key derivation function (PBKDF2) for IEEE 802.11i + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" + +static int pbkdf2_sha1_f(const char *passphrase, const u8 *ssid, + size_t ssid_len, int iterations, unsigned int count, + u8 *digest) +{ + unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN]; + int i, j; + unsigned char count_buf[4]; + const u8 *addr[2]; + size_t len[2]; + size_t passphrase_len = os_strlen(passphrase); + + addr[0] = ssid; + len[0] = ssid_len; + addr[1] = count_buf; + len[1] = 4; + + /* F(P, S, c, i) = U1 xor U2 xor ... Uc + * U1 = PRF(P, S || i) + * U2 = PRF(P, U1) + * Uc = PRF(P, Uc-1) + */ + + count_buf[0] = (count >> 24) & 0xff; + count_buf[1] = (count >> 16) & 0xff; + count_buf[2] = (count >> 8) & 0xff; + count_buf[3] = count & 0xff; + if (hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, + tmp)) + return -1; + os_memcpy(digest, tmp, SHA1_MAC_LEN); + + for (i = 1; i < iterations; i++) { + if (hmac_sha1((u8 *) passphrase, passphrase_len, tmp, + SHA1_MAC_LEN, tmp2)) + return -1; + os_memcpy(tmp, tmp2, SHA1_MAC_LEN); + for (j = 0; j < SHA1_MAC_LEN; j++) + digest[j] ^= tmp2[j]; + } + + return 0; +} + + +/** + * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i + * @passphrase: ASCII passphrase + * @ssid: SSID + * @ssid_len: SSID length in bytes + * @iterations: Number of iterations to run + * @buf: Buffer for the generated key + * @buflen: Length of the buffer in bytes + * Returns: 0 on success, -1 of failure + * + * This function is used to derive PSK for WPA-PSK. For this protocol, + * iterations is set to 4096 and buflen to 32. This function is described in + * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0. + */ +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ + unsigned int count = 0; + unsigned char *pos = buf; + size_t left = buflen, plen; + unsigned char digest[SHA1_MAC_LEN]; + + while (left > 0) { + count++; + if (pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations, + count, digest)) + return -1; + plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left; + os_memcpy(pos, digest, plen); + pos += plen; + left -= plen; + } + + return 0; +} diff --git a/freebsd/contrib/wpa/src/crypto/sha1-prf.c b/freebsd/contrib/wpa/src/crypto/sha1-prf.c new file mode 100644 index 00000000..004260d7 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/sha1-prf.c @@ -0,0 +1,69 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * SHA1-based PRF + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "crypto.h" + + +/** + * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 of failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key (e.g., PMK in IEEE 802.11i). + */ +int sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + u8 counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label) + 1; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = (u8 *) label; + len[0] = label_len; + addr[1] = data; + len[1] = data_len; + addr[2] = &counter; + len[2] = 1; + + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + if (plen >= SHA1_MAC_LEN) { + if (hmac_sha1_vector(key, key_len, 3, addr, len, + &buf[pos])) + return -1; + pos += SHA1_MAC_LEN; + } else { + if (hmac_sha1_vector(key, key_len, 3, addr, len, + hash)) + return -1; + os_memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } + os_memset(hash, 0, sizeof(hash)); + + return 0; +} diff --git a/freebsd/contrib/wpa/src/crypto/sha1.c b/freebsd/contrib/wpa/src/crypto/sha1.c new file mode 100644 index 00000000..de751fb9 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/sha1.c @@ -0,0 +1,109 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "crypto.h" + + +/** + * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (20 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ + unsigned char tk[20]; + const u8 *_addr[6]; + size_t _len[6], i; + int ret; + + 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; + ret = sha1_vector(2, _addr, _len, mac); + os_memset(k_pad, 0, sizeof(k_pad)); + return ret; +} + + +/** + * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (20 bytes) + * Returns: 0 on success, -1 of failure + */ +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} diff --git a/freebsd/contrib/wpa/src/crypto/sha1.h b/freebsd/contrib/wpa/src/crypto/sha1.h new file mode 100644 index 00000000..933cd81b --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/sha1.h @@ -0,0 +1,27 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA1_H +#define SHA1_H + +#define SHA1_MAC_LEN 20 + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); +int sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha1_t_prf(const u8 *key, size_t key_len, const char *label, + const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); +int __must_check tls_prf_sha1_md5(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, + size_t seed_len, u8 *out, size_t outlen); +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen); +#endif /* SHA1_H */ diff --git a/freebsd/contrib/wpa/src/crypto/sha256-internal.c b/freebsd/contrib/wpa/src/crypto/sha256-internal.c new file mode 100644 index 00000000..c2ef3154 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/sha256-internal.c @@ -0,0 +1,228 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * SHA-256 hash implementation and interface functions + * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" +#include "sha256_i.h" +#include "crypto.h" + + +/** + * sha256_vector - SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure + */ +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + struct sha256_state ctx; + size_t i; + + sha256_init(&ctx); + for (i = 0; i < num_elem; i++) + if (sha256_process(&ctx, addr[i], len[i])) + return -1; + if (sha256_done(&ctx, mac)) + return -1; + return 0; +} + + +/* ===== start - public domain SHA256 implementation ===== */ + +/* This is based on SHA256 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +/* the K array */ +static const unsigned long K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + + +/* Various logical functions */ +#define RORc(x, y) \ +( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \ + ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x), (n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +/* compress 512-bits */ +static int sha256_compress(struct sha256_state *md, unsigned char *buf) +{ + u32 S[8], W[64], t0, t1; + u32 t; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) + W[i] = WPA_GET_BE32(buf + (4 * i)); + + /* fill W[16..63] */ + for (i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + + W[i - 16]; + } + + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 64; ++i) { + RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + return 0; +} + + +/* Initialize the hash state */ +void sha256_init(struct sha256_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = 0x6A09E667UL; + md->state[1] = 0xBB67AE85UL; + md->state[2] = 0x3C6EF372UL; + md->state[3] = 0xA54FF53AUL; + md->state[4] = 0x510E527FUL; + md->state[5] = 0x9B05688CUL; + md->state[6] = 0x1F83D9ABUL; + md->state[7] = 0x5BE0CD19UL; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen) +{ + unsigned long n; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + while (inlen > 0) { + if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) { + if (sha256_compress(md, (unsigned char *) in) < 0) + return -1; + md->length += SHA256_BLOCK_SIZE * 8; + in += SHA256_BLOCK_SIZE; + inlen -= SHA256_BLOCK_SIZE; + } else { + n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen)); + os_memcpy(md->buf + md->curlen, in, n); + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == SHA256_BLOCK_SIZE) { + if (sha256_compress(md, md->buf) < 0) + return -1; + md->length += 8 * SHA256_BLOCK_SIZE; + md->curlen = 0; + } + } + } + + return 0; +} + + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (32 bytes) + @return CRYPT_OK if successful +*/ +int sha256_done(struct sha256_state *md, unsigned char *out) +{ + int i; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + /* increase the length of the message */ + md->length += md->curlen * 8; + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char) 0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 56) { + while (md->curlen < SHA256_BLOCK_SIZE) { + md->buf[md->curlen++] = (unsigned char) 0; + } + sha256_compress(md, md->buf); + md->curlen = 0; + } + + /* pad up to 56 bytes of zeroes */ + while (md->curlen < 56) { + md->buf[md->curlen++] = (unsigned char) 0; + } + + /* store length */ + WPA_PUT_BE64(md->buf + 56, md->length); + sha256_compress(md, md->buf); + + /* copy output */ + for (i = 0; i < 8; i++) + WPA_PUT_BE32(out + (4 * i), md->state[i]); + + return 0; +} + +/* ===== end - public domain SHA256 implementation ===== */ diff --git a/freebsd/contrib/wpa/src/crypto/sha256-prf.c b/freebsd/contrib/wpa/src/crypto/sha256-prf.c new file mode 100644 index 00000000..76496214 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/sha256-prf.c @@ -0,0 +1,102 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * SHA256-based PRF (IEEE 802.11r) + * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" +#include "crypto.h" + + +/** + * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. + */ +void sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); +} + + +/** + * sha256_prf_bits - IEEE Std 802.11-2012, 11.6.1.7.2 Key derivation function + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bits of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. If the requested buf_len is not divisible by eight, the least + * significant 1-7 bits of the last octet in the output are not part of the + * requested output. + */ +void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) +{ + u16 counter = 1; + size_t pos, plen; + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + u8 counter_le[2], length_le[2]; + size_t buf_len = (buf_len_bits + 7) / 8; + + addr[0] = counter_le; + len[0] = 2; + addr[1] = (u8 *) label; + len[1] = os_strlen(label); + addr[2] = data; + len[2] = data_len; + addr[3] = length_le; + len[3] = sizeof(length_le); + + WPA_PUT_LE16(length_le, buf_len_bits); + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + WPA_PUT_LE16(counter_le, counter); + if (plen >= SHA256_MAC_LEN) { + hmac_sha256_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA256_MAC_LEN; + } else { + hmac_sha256_vector(key, key_len, 4, addr, len, hash); + os_memcpy(&buf[pos], hash, plen); + pos += plen; + break; + } + counter++; + } + + /* + * Mask out unused bits in the last octet if it does not use all the + * bits. + */ + if (buf_len_bits % 8) { + u8 mask = 0xff << (8 - buf_len_bits % 8); + buf[pos - 1] &= mask; + } + + os_memset(hash, 0, sizeof(hash)); +} diff --git a/freebsd/contrib/wpa/src/crypto/sha256.h b/freebsd/contrib/wpa/src/crypto/sha256.h new file mode 100644 index 00000000..b15f5115 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/sha256.h @@ -0,0 +1,30 @@ +/* + * SHA256 hash implementation and interface functions + * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA256_H +#define SHA256_H + +#define SHA256_MAC_LEN 32 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); +void sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); +void tls_prf_sha256(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); +int hmac_sha256_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); + +#endif /* SHA256_H */ diff --git a/freebsd/contrib/wpa/src/crypto/sha256_i.h b/freebsd/contrib/wpa/src/crypto/sha256_i.h new file mode 100644 index 00000000..a502d2ba --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/sha256_i.h @@ -0,0 +1,25 @@ +/* + * SHA-256 internal definitions + * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA256_I_H +#define SHA256_I_H + +#define SHA256_BLOCK_SIZE 64 + +struct sha256_state { + u64 length; + u32 state[8], curlen; + u8 buf[SHA256_BLOCK_SIZE]; +}; + +void sha256_init(struct sha256_state *md); +int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen); +int sha256_done(struct sha256_state *md, unsigned char *out); + +#endif /* SHA256_I_H */ diff --git a/freebsd/contrib/wpa/src/crypto/sha384.h b/freebsd/contrib/wpa/src/crypto/sha384.h new file mode 100644 index 00000000..3deafa59 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/sha384.h @@ -0,0 +1,24 @@ +/* + * SHA384 hash implementation and interface functions + * Copyright (c) 2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA384_H +#define SHA384_H + +#define SHA384_MAC_LEN 48 + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); +void sha384_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); + +#endif /* SHA384_H */ diff --git a/freebsd/contrib/wpa/src/crypto/tls.h b/freebsd/contrib/wpa/src/crypto/tls.h new file mode 100644 index 00000000..2e562339 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/tls.h @@ -0,0 +1,588 @@ +/* + * SSL/TLS interface definition + * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLS_H +#define TLS_H + +struct tls_connection; + +struct tls_random { + const u8 *client_random; + size_t client_random_len; + const u8 *server_random; + size_t server_random_len; +}; + +enum tls_event { + TLS_CERT_CHAIN_SUCCESS, + TLS_CERT_CHAIN_FAILURE, + TLS_PEER_CERTIFICATE, + TLS_ALERT +}; + +/* + * Note: These are used as identifier with external programs and as such, the + * values must not be changed. + */ +enum tls_fail_reason { + TLS_FAIL_UNSPECIFIED = 0, + TLS_FAIL_UNTRUSTED = 1, + TLS_FAIL_REVOKED = 2, + TLS_FAIL_NOT_YET_VALID = 3, + TLS_FAIL_EXPIRED = 4, + TLS_FAIL_SUBJECT_MISMATCH = 5, + TLS_FAIL_ALTSUBJECT_MISMATCH = 6, + TLS_FAIL_BAD_CERTIFICATE = 7, + TLS_FAIL_SERVER_CHAIN_PROBE = 8, + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, + TLS_FAIL_DOMAIN_MISMATCH = 10, +}; + + +#define TLS_MAX_ALT_SUBJECT 10 + +union tls_event_data { + struct { + int depth; + const char *subject; + enum tls_fail_reason reason; + const char *reason_txt; + const struct wpabuf *cert; + } cert_fail; + + struct { + int depth; + const char *subject; + const struct wpabuf *cert; + const u8 *hash; + size_t hash_len; + const char *altsubject[TLS_MAX_ALT_SUBJECT]; + int num_altsubject; + } peer_cert; + + struct { + int is_local; + const char *type; + const char *description; + } alert; +}; + +struct tls_config { + const char *opensc_engine_path; + const char *pkcs11_engine_path; + const char *pkcs11_module_path; + int fips_mode; + int cert_in_cb; + const char *openssl_ciphers; + unsigned int tls_session_lifetime; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; +}; + +#define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0) +#define TLS_CONN_DISABLE_TIME_CHECKS BIT(1) +#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2) +#define TLS_CONN_REQUEST_OCSP BIT(3) +#define TLS_CONN_REQUIRE_OCSP BIT(4) +#define TLS_CONN_DISABLE_TLSv1_1 BIT(5) +#define TLS_CONN_DISABLE_TLSv1_2 BIT(6) +#define TLS_CONN_EAP_FAST BIT(7) +#define TLS_CONN_DISABLE_TLSv1_0 BIT(8) + +/** + * struct tls_connection_params - Parameters for TLS connection + * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER + * format + * @ca_cert_blob: ca_cert as inlined data or %NULL if not used + * @ca_cert_blob_len: ca_cert_blob length + * @ca_path: Path to CA certificates (OpenSSL specific) + * @subject_match: String to match in the subject of the peer certificate or + * %NULL to allow all subjects + * @altsubject_match: String to match in the alternative subject of the peer + * certificate or %NULL to allow all alternative subjects + * @suffix_match: String to suffix match in the dNSName or CN of the peer + * certificate or %NULL to allow all domain names. This may allow subdomains an + * wildcard certificates. Each domain name label must have a full match. + * @domain_match: String to match in the dNSName or CN of the peer + * certificate or %NULL to allow all domain names. This requires a full, + * case-insensitive match. + * @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 + * @openssl_ciphers: OpenSSL cipher configuration + * @flags: Parameter options (TLS_CONN_*) + * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response + * or %NULL if OCSP is not enabled + * + * TLS connection parameters to be configured with tls_connection_set_params() + * and tls_global_set_params(). + * + * Certificates and private key can be configured either as a reference name + * (file path or reference to certificate store) or by providing the same data + * as a pointer to the data in memory. Only one option will be used for each + * field. + */ +struct tls_connection_params { + const char *ca_cert; + const u8 *ca_cert_blob; + size_t ca_cert_blob_len; + const char *ca_path; + const char *subject_match; + const char *altsubject_match; + const char *suffix_match; + const char *domain_match; + const char *client_cert; + const u8 *client_cert_blob; + size_t client_cert_blob_len; + const char *private_key; + const u8 *private_key_blob; + size_t private_key_blob_len; + const char *private_key_passwd; + const char *dh_file; + const u8 *dh_blob; + size_t dh_blob_len; + + /* OpenSSL specific variables */ + int engine; + const char *engine_id; + const char *pin; + const char *key_id; + const char *cert_id; + const char *ca_cert_id; + const char *openssl_ciphers; + + unsigned int flags; + const char *ocsp_stapling_response; +}; + + +/** + * tls_init - Initialize TLS library + * @conf: Configuration data for TLS library + * Returns: Context data to be used as tls_ctx in calls to other functions, + * or %NULL on failure. + * + * Called once during program startup and once for each RSN pre-authentication + * session. In other words, there can be two concurrent TLS contexts. If global + * library initialization is needed (i.e., one that is shared between both + * authentication types), the TLS library wrapper should maintain a reference + * counter and do global initialization only when moving from 0 to 1 reference. + */ +void * tls_init(const struct tls_config *conf); + +/** + * tls_deinit - Deinitialize TLS library + * @tls_ctx: TLS context data from tls_init() + * + * Called once during program shutdown and once for each RSN pre-authentication + * session. If global library deinitialization is needed (i.e., one that is + * shared between both authentication types), the TLS library wrapper should + * maintain a reference counter and do global deinitialization only when moving + * from 1 to 0 references. + */ +void tls_deinit(void *tls_ctx); + +/** + * tls_get_errors - Process pending errors + * @tls_ctx: TLS context data from tls_init() + * Returns: Number of found error, 0 if no errors detected. + * + * Process all pending TLS errors. + */ +int tls_get_errors(void *tls_ctx); + +/** + * tls_connection_init - Initialize a new TLS connection + * @tls_ctx: TLS context data from tls_init() + * Returns: Connection context data, conn for other function calls + */ +struct tls_connection * tls_connection_init(void *tls_ctx); + +/** + * tls_connection_deinit - Free TLS connection data + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Release all resources allocated for TLS connection. + */ +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_established - Has the TLS connection been completed? + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 1 if TLS connection has been completed, 0 if not. + */ +int tls_connection_established(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_shutdown - Shutdown TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 0 on success, -1 on failure + * + * Shutdown current TLS connection without releasing all resources. New + * connection can be started by using the same conn without having to call + * tls_connection_init() or setting certificates etc. again. The new + * connection should try to use session resumption. + */ +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); + +enum { + TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN = -4, + 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 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, or + * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine + * failure. + */ +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 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, or + * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine + * failure. + */ +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 + * @flags: Connection flags (TLS_CONN_*) + * @session_ctx: Session caching context or %NULL to use default + * @session_ctx_len: Length of @session_ctx in bytes. + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_set_verify(void *tls_ctx, + struct tls_connection *conn, + int verify_peer, + unsigned int flags, + const u8 *session_ctx, + size_t session_ctx_len); + +/** + * tls_connection_get_random - Get random data from TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @data: Structure of client/server random data (filled on success) + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_get_random(void *tls_ctx, + struct tls_connection *conn, + struct tls_random *data); + +/** + * 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 + * @skip_keyblock: Skip TLS key block from the beginning of PRF output + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + * + * tls_connection_prf() is required so that further keying material can be + * derived from the master secret. Example implementation of this function is in + * tls_prf_sha1_md5() when it is called with seed set to + * client_random|server_random (or server_random|client_random). For TLSv1.2 and + * newer, a different PRF is needed, though. + */ +int __must_check tls_connection_prf(void *tls_ctx, + struct tls_connection *conn, + const char *label, + int server_random_first, + int skip_keyblock, + u8 *out, size_t out_len); + +/** + * tls_connection_handshake - Process TLS handshake (client side) + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Input data from TLS server + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * Returns: Output data, %NULL on failure + * + * The caller is responsible for freeing the returned output data. If the final + * handshake message includes application data, this is decrypted and + * appl_data (if not %NULL) is set to point this data. The caller is + * responsible for freeing appl_data. + * + * This function is used during TLS handshake. The first call is done with + * in_data == %NULL and the library is expected to return ClientHello packet. + * This packet is then send to the server and a response from server is given + * to TLS library by calling this function again with in_data pointing to the + * TLS message from the server. + * + * If the TLS handshake fails, this function may return %NULL. However, if the + * TLS library has a TLS alert to send out, that should be returned as the + * output data. In this case, tls_connection_get_failed() must return failure + * (> 0). + * + * tls_connection_established() should return 1 once the TLS handshake has been + * completed successfully. + */ +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data); + +struct wpabuf * tls_connection_handshake2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, + int *more_data_needed); + +/** + * tls_connection_server_handshake - Process TLS handshake (server side) + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Input data from TLS peer + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * Returns: Output data, %NULL on failure + * + * The caller is responsible for freeing the returned output data. + */ +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data); + +/** + * tls_connection_encrypt - Encrypt data into TLS tunnel + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Plaintext data to be encrypted + * Returns: Encrypted TLS data or %NULL on failure + * + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. The caller is responsible for freeing the + * returned output data. + */ +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data); + +/** + * tls_connection_decrypt - Decrypt data from TLS tunnel + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Encrypted TLS data + * Returns: Decrypted TLS data or %NULL on failure + * + * This function is used after TLS handshake has been completed successfully to + * receive data from the encrypted tunnel. The caller is responsible for + * freeing the returned output data. + */ +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data); + +struct wpabuf * tls_connection_decrypt2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + int *more_data_needed); + +/** + * tls_connection_resumed - Was session resumption used + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 1 if current session used session resumption, 0 if not + */ +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn); + +enum { + TLS_CIPHER_NONE, + TLS_CIPHER_RC4_SHA /* 0x0005 */, + TLS_CIPHER_AES128_SHA /* 0x002f */, + TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */, + TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */ +}; + +/** + * tls_connection_set_cipher_list - Configure acceptable cipher suites + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers + * (TLS_CIPHER_*). + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_set_cipher_list(void *tls_ctx, + struct tls_connection *conn, + u8 *ciphers); + +/** + * tls_get_version - Get the current TLS version number + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @buf: Buffer for returning the TLS version number + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the currently used TLS version number. + */ +int __must_check tls_get_version(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen); + +/** + * 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); + +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); + +void tls_connection_set_log_cb(struct tls_connection *conn, + void (*log_cb)(void *ctx, const char *msg), + void *ctx); + +#define TLS_BREAK_VERIFY_DATA BIT(0) +#define TLS_BREAK_SRV_KEY_X_HASH BIT(1) +#define TLS_BREAK_SRV_KEY_X_SIGNATURE BIT(2) +#define TLS_DHE_PRIME_511B BIT(3) +#define TLS_DHE_PRIME_767B BIT(4) +#define TLS_DHE_PRIME_15 BIT(5) +#define TLS_DHE_PRIME_58B BIT(6) +#define TLS_DHE_NON_PRIME BIT(7) + +void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags); + +int tls_get_library_version(char *buf, size_t buf_len); + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data); + +void tls_connection_set_success_data_resumed(struct tls_connection *conn); + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn); + +void tls_connection_remove_session(struct tls_connection *conn); + +#endif /* TLS_H */ diff --git a/freebsd/contrib/wpa/src/crypto/tls_internal.c b/freebsd/contrib/wpa/src/crypto/tls_internal.c new file mode 100644 index 00000000..6a91f497 --- /dev/null +++ b/freebsd/contrib/wpa/src/crypto/tls_internal.c @@ -0,0 +1,735 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * TLS interface functions and an internal TLS implementation + * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This file interface functions for hostapd/wpa_supplicant to use the + * integrated TLSv1 implementation. + */ + +#include "includes.h" + +#include "common.h" +#include "tls.h" +#include "tls/tlsv1_client.h" +#include "tls/tlsv1_server.h" + + +static int tls_ref_count = 0; + +struct tls_global { + int server; + struct tlsv1_credentials *server_cred; + int check_crl; +}; + +struct tls_connection { + struct tlsv1_client *client; + struct tlsv1_server *server; + struct tls_global *global; +}; + + +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; + conn->global = global; + +#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; +} + + +#ifdef CONFIG_TESTING_OPTIONS +#ifdef CONFIG_TLS_INTERNAL_SERVER +void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags) +{ + if (conn->server) + tlsv1_server_set_test_flags(conn->server, flags); +} +#endif /* CONFIG_TLS_INTERNAL_SERVER */ +#endif /* CONFIG_TESTING_OPTIONS */ + + +void tls_connection_set_log_cb(struct tls_connection *conn, + void (*log_cb)(void *ctx, const char *msg), + void *ctx) +{ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + tlsv1_server_set_log_cb(conn->server, log_cb, ctx); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ +} + + +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 (params->subject_match) { + wpa_printf(MSG_INFO, "TLS: subject_match not supported"); + tlsv1_cred_free(cred); + return -1; + } + + if (params->altsubject_match) { + wpa_printf(MSG_INFO, "TLS: altsubject_match not supported"); + tlsv1_cred_free(cred); + return -1; + } + + if (params->suffix_match) { + wpa_printf(MSG_INFO, "TLS: suffix_match not supported"); + tlsv1_cred_free(cred); + return -1; + } + + if (params->domain_match) { + wpa_printf(MSG_INFO, "TLS: domain_match not supported"); + tlsv1_cred_free(cred); + return -1; + } + + if (params->openssl_ciphers) { + wpa_printf(MSG_INFO, "TLS: openssl_ciphers not supported"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_set_ca_cert(cred, params->ca_cert, + params->ca_cert_blob, params->ca_cert_blob_len, + params->ca_path)) { + wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA " + "certificates"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_set_cert(cred, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to configure client " + "certificate"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_set_private_key(cred, params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob, + params->dh_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_client_set_cred(conn->client, cred) < 0) { + tlsv1_cred_free(cred); + return -1; + } + + tlsv1_client_set_time_checks( + conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS)); + + return 0; +#else /* CONFIG_TLS_INTERNAL_CLIENT */ + return -1; +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +} + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ +#ifdef CONFIG_TLS_INTERNAL_SERVER + struct tls_global *global = tls_ctx; + struct tlsv1_credentials *cred; + + /* Currently, global parameters are only set when running in server + * mode. */ + global->server = 1; + tlsv1_cred_free(global->server_cred); + global->server_cred = cred = tlsv1_cred_alloc(); + if (cred == NULL) + return -1; + + if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob, + params->ca_cert_blob_len, params->ca_path)) { + wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA " + "certificates"); + return -1; + } + + if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob, + params->client_cert_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to configure server " + "certificate"); + return -1; + } + + if (tlsv1_set_private_key(cred, params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key"); + return -1; + } + + if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob, + params->dh_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters"); + return -1; + } + + return 0; +#else /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +#endif /* CONFIG_TLS_INTERNAL_SERVER */ +} + + +int tls_global_set_verify(void *tls_ctx, int check_crl) +{ + struct tls_global *global = tls_ctx; + global->check_crl = check_crl; + return 0; +} + + +int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) +{ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_set_verify(conn->server, verify_peer); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, + struct tls_random *data) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_random(conn->client, data); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_random(conn->server, data); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +static int tls_get_keyblock_size(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; +} + + +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + int skip_keyblock, u8 *out, size_t out_len) +{ + int ret = -1, skip = 0; + u8 *tmp_out = NULL; + u8 *_out = out; + + if (skip_keyblock) { + skip = tls_get_keyblock_size(conn); + if (skip < 0) + return -1; + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; + } + +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) { + ret = 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) { + ret = tlsv1_server_prf(conn->server, label, + server_random_first, + _out, out_len); + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + if (ret == 0 && skip_keyblock) + os_memcpy(out, _out + skip, out_len); + bin_clear_free(tmp_out, skip); + + return ret; +} + + +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data, + NULL); +} + + +struct wpabuf * tls_connection_handshake2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, + int *need_more_data) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + u8 *res, *ad; + size_t res_len, ad_len; + struct wpabuf *out; + + if (conn->client == NULL) + return NULL; + + ad = NULL; + res = tlsv1_client_handshake(conn->client, + in_data ? wpabuf_head(in_data) : NULL, + in_data ? wpabuf_len(in_data) : 0, + &res_len, &ad, &ad_len, need_more_data); + if (res == NULL) + return NULL; + out = wpabuf_alloc_ext_data(res, res_len); + if (out == NULL) { + os_free(res); + os_free(ad); + return NULL; + } + if (appl_data) { + if (ad) { + *appl_data = wpabuf_alloc_ext_data(ad, ad_len); + if (*appl_data == NULL) + os_free(ad); + } else + *appl_data = NULL; + } else + os_free(ad); + + return out; +#else /* CONFIG_TLS_INTERNAL_CLIENT */ + return NULL; +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +} + + +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ +#ifdef CONFIG_TLS_INTERNAL_SERVER + u8 *res; + size_t res_len; + struct wpabuf *out; + + if (conn->server == NULL) + return NULL; + + if (appl_data) + *appl_data = NULL; + + res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data), + wpabuf_len(in_data), &res_len); + if (res == NULL && tlsv1_server_established(conn->server)) + return wpabuf_alloc(0); + if (res == NULL) + return NULL; + out = wpabuf_alloc_ext_data(res, res_len); + if (out == NULL) { + os_free(res); + return NULL; + } + + return out; +#else /* CONFIG_TLS_INTERNAL_SERVER */ + return NULL; +#endif /* CONFIG_TLS_INTERNAL_SERVER */ +} + + +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) { + struct wpabuf *buf; + int res; + buf = wpabuf_alloc(wpabuf_len(in_data) + 300); + if (buf == NULL) + return NULL; + res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data), + wpabuf_len(in_data), + wpabuf_mhead(buf), + wpabuf_size(buf)); + if (res < 0) { + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + return buf; + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) { + struct wpabuf *buf; + int res; + buf = wpabuf_alloc(wpabuf_len(in_data) + 300); + if (buf == NULL) + return NULL; + res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data), + wpabuf_len(in_data), + wpabuf_mhead(buf), + wpabuf_size(buf)); + if (res < 0) { + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + return buf; + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return NULL; +} + + +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL); +} + + +struct wpabuf * tls_connection_decrypt2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + int *need_more_data) +{ + if (need_more_data) + *need_more_data = 0; + +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) { + return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data), + wpabuf_len(in_data), + need_more_data); + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) { + struct wpabuf *buf; + int res; + buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); + if (buf == NULL) + return NULL; + res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data), + wpabuf_len(in_data), + wpabuf_mhead(buf), + wpabuf_size(buf)); + if (res < 0) { + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + return buf; + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return NULL; +} + + +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_resumed(conn->client); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_resumed(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_set_cipher_list(conn->client, ciphers); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_set_cipher_list(conn->server, ciphers); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_get_version(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + /* TODO */ + 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_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; +} + + +int tls_get_library_version(char *buf, size_t buf_len) +{ + return os_snprintf(buf, buf_len, "internal"); +} + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ +} diff --git a/freebsd/contrib/wpa/src/drivers/driver.h b/freebsd/contrib/wpa/src/drivers/driver.h new file mode 100644 index 00000000..3cdab5a7 --- /dev/null +++ b/freebsd/contrib/wpa/src/drivers/driver.h @@ -0,0 +1,4704 @@ +/* + * Driver interface definition + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This file defines a driver interface used by both %wpa_supplicant and + * hostapd. The first part of the file defines data structures used in various + * driver operations. This is followed by the struct wpa_driver_ops that each + * driver wrapper will beed to define with callback functions for requesting + * driver operations. After this, there are definitions for driver event + * reporting with wpa_supplicant_event() and some convenience helper functions + * that can be used to report events. + */ + +#ifndef DRIVER_H +#define DRIVER_H + +#define WPA_SUPPLICANT_DRIVER_VERSION 4 + +#include "common/defs.h" +#include "common/ieee802_11_defs.h" +#include "utils/list.h" + +#define HOSTAPD_CHAN_DISABLED 0x00000001 +#define HOSTAPD_CHAN_NO_IR 0x00000002 +#define HOSTAPD_CHAN_RADAR 0x00000008 +#define HOSTAPD_CHAN_HT40PLUS 0x00000010 +#define HOSTAPD_CHAN_HT40MINUS 0x00000020 +#define HOSTAPD_CHAN_HT40 0x00000040 +#define HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED 0x00000080 + +#define HOSTAPD_CHAN_DFS_UNKNOWN 0x00000000 +#define HOSTAPD_CHAN_DFS_USABLE 0x00000100 +#define HOSTAPD_CHAN_DFS_UNAVAILABLE 0x00000200 +#define HOSTAPD_CHAN_DFS_AVAILABLE 0x00000300 +#define HOSTAPD_CHAN_DFS_MASK 0x00000300 + +#define HOSTAPD_CHAN_VHT_10_70 0x00000800 +#define HOSTAPD_CHAN_VHT_30_50 0x00001000 +#define HOSTAPD_CHAN_VHT_50_30 0x00002000 +#define HOSTAPD_CHAN_VHT_70_10 0x00004000 + +#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000 +#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000 + +/** + * enum reg_change_initiator - Regulatory change initiator + */ +enum reg_change_initiator { + REGDOM_SET_BY_CORE, + REGDOM_SET_BY_USER, + REGDOM_SET_BY_DRIVER, + REGDOM_SET_BY_COUNTRY_IE, + REGDOM_BEACON_HINT, +}; + +/** + * enum reg_type - Regulatory change types + */ +enum reg_type { + REGDOM_TYPE_UNKNOWN, + REGDOM_TYPE_COUNTRY, + REGDOM_TYPE_WORLD, + REGDOM_TYPE_CUSTOM_WORLD, + REGDOM_TYPE_INTERSECTION, +}; + +/** + * struct hostapd_channel_data - Channel information + */ +struct hostapd_channel_data { + /** + * chan - Channel number (IEEE 802.11) + */ + short chan; + + /** + * freq - Frequency in MHz + */ + int freq; + + /** + * flag - Channel flags (HOSTAPD_CHAN_*) + */ + int flag; + + /** + * max_tx_power - Regulatory transmit power limit in dBm + */ + u8 max_tx_power; + + /** + * survey_list - Linked list of surveys (struct freq_survey) + */ + struct dl_list survey_list; + + /** + * min_nf - Minimum observed noise floor, in dBm, based on all + * surveyed channel data + */ + s8 min_nf; + +#ifdef CONFIG_ACS + /** + * interference_factor - Computed interference factor on this + * channel (used internally in src/ap/acs.c; driver wrappers do not + * need to set this) + */ + long double interference_factor; +#endif /* CONFIG_ACS */ + + /** + * dfs_cac_ms - DFS CAC time in milliseconds + */ + unsigned int dfs_cac_ms; +}; + +#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0) +#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1) + +/** + * struct hostapd_hw_modes - Supported hardware mode information + */ +struct hostapd_hw_modes { + /** + * mode - Hardware mode + */ + enum hostapd_hw_mode mode; + + /** + * num_channels - Number of entries in the channels array + */ + int num_channels; + + /** + * channels - Array of supported channels + */ + struct hostapd_channel_data *channels; + + /** + * num_rates - Number of entries in the rates array + */ + int num_rates; + + /** + * rates - Array of supported rates in 100 kbps units + */ + int *rates; + + /** + * ht_capab - HT (IEEE 802.11n) capabilities + */ + u16 ht_capab; + + /** + * mcs_set - MCS (IEEE 802.11n) rate parameters + */ + u8 mcs_set[16]; + + /** + * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters + */ + u8 a_mpdu_params; + + /** + * vht_capab - VHT (IEEE 802.11ac) capabilities + */ + u32 vht_capab; + + /** + * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters + */ + u8 vht_mcs_set[8]; + + unsigned int flags; /* HOSTAPD_MODE_FLAG_* */ +}; + + +#define IEEE80211_MODE_INFRA 0 +#define IEEE80211_MODE_IBSS 1 +#define IEEE80211_MODE_AP 2 +#define IEEE80211_MODE_MESH 5 + +#define IEEE80211_CAP_ESS 0x0001 +#define IEEE80211_CAP_IBSS 0x0002 +#define IEEE80211_CAP_PRIVACY 0x0010 +#define IEEE80211_CAP_RRM 0x1000 + +/* DMG (60 GHz) IEEE 802.11ad */ +/* type - bits 0..1 */ +#define IEEE80211_CAP_DMG_MASK 0x0003 +#define IEEE80211_CAP_DMG_IBSS 0x0001 /* Tx by: STA */ +#define IEEE80211_CAP_DMG_PBSS 0x0002 /* Tx by: PCP */ +#define IEEE80211_CAP_DMG_AP 0x0003 /* Tx by: AP */ + +#define WPA_SCAN_QUAL_INVALID BIT(0) +#define WPA_SCAN_NOISE_INVALID BIT(1) +#define WPA_SCAN_LEVEL_INVALID BIT(2) +#define WPA_SCAN_LEVEL_DBM BIT(3) +#define WPA_SCAN_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) + * @est_throughput: Estimated throughput in kbps (this is calculated during + * scan result processing if left zero by the driver wrapper) + * @snr: Signal-to-noise ratio in dB (calculated during scan result processing) + * @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. + * + * This structure data is 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_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; + unsigned int est_throughput; + int snr; + size_t ie_len; + size_t beacon_ie_len; + /* Followed by ie_len + beacon_ie_len octets of IE data */ +}; + +/** + * struct wpa_scan_results - Scan results + * @res: Array of pointers to allocated variable length scan result entries + * @num: Number of entries in the scan result array + * @fetch_time: Time when the results were fetched from the driver + */ +struct wpa_scan_results { + struct wpa_scan_res **res; + size_t num; + struct os_reltime fetch_time; +}; + +/** + * struct wpa_interface_info - Network interface information + * @next: Pointer to the next interface or NULL if this is the last one + * @ifname: Interface name that can be used with init() or init2() + * @desc: Human readable adapter description (e.g., vendor/model) or NULL if + * not available + * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one + * is not an allocated copy, i.e., get_interfaces() caller will not free + * this) + */ +struct wpa_interface_info { + struct wpa_interface_info *next; + char *ifname; + char *desc; + const char *drv_name; +}; + +#define WPAS_MAX_SCAN_SSIDS 16 + +/** + * struct wpa_driver_scan_params - Scan parameters + * Data for struct wpa_driver_ops::scan2(). + */ +struct wpa_driver_scan_params { + /** + * ssids - SSIDs to scan for + */ + struct wpa_driver_scan_ssid { + /** + * ssid - specific SSID to scan for (ProbeReq) + * %NULL or zero-length SSID is used to indicate active scan + * with wildcard SSID. + */ + const u8 *ssid; + /** + * ssid_len: Length of the SSID in octets + */ + size_t ssid_len; + } ssids[WPAS_MAX_SCAN_SSIDS]; + + /** + * num_ssids - Number of entries in ssids array + * Zero indicates a request for a passive scan. + */ + size_t num_ssids; + + /** + * extra_ies - Extra IE(s) to add into Probe Request or %NULL + */ + const u8 *extra_ies; + + /** + * extra_ies_len - Length of extra_ies in octets + */ + size_t extra_ies_len; + + /** + * freqs - Array of frequencies to scan or %NULL for all frequencies + * + * The frequency is set in MHz. The array is zero-terminated. + */ + int *freqs; + + /** + * filter_ssids - Filter for reporting SSIDs + * + * This optional parameter can be used to request the driver wrapper to + * filter scan results to include only the specified SSIDs. %NULL + * indicates that no filtering is to be done. This can be used to + * reduce memory needs for scan results in environments that have large + * number of APs with different SSIDs. + * + * The driver wrapper is allowed to take this allocated buffer into its + * own use by setting the pointer to %NULL. In that case, the driver + * wrapper is responsible for freeing the buffer with os_free() once it + * is not needed anymore. + */ + struct wpa_driver_scan_filter { + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; + } *filter_ssids; + + /** + * num_filter_ssids - Number of entries in filter_ssids array + */ + size_t num_filter_ssids; + + /** + * filter_rssi - Filter by RSSI + * + * The driver may filter scan results in firmware to reduce host + * wakeups and thereby save power. Specify the RSSI threshold in s32 + * dBm. + */ + s32 filter_rssi; + + /** + * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes + * + * When set, the driver is expected to remove rates 1, 2, 5.5, and 11 + * Mbps from the support rates element(s) in the Probe Request frames + * and not to transmit the frames at any of those rates. + */ + unsigned int p2p_probe:1; + + /** + * only_new_results - Request driver to report only new results + * + * This is used to request the driver to report only BSSes that have + * been detected after this scan request has been started, i.e., to + * flush old cached BSS entries. + */ + unsigned int only_new_results:1; + + /** + * low_priority - Requests driver to use a lower scan priority + * + * This is used to request the driver to use a lower scan priority + * if it supports such a thing. + */ + unsigned int low_priority:1; + + /** + * mac_addr_rand - Requests driver to randomize MAC address + */ + unsigned int mac_addr_rand:1; + + /** + * mac_addr - MAC address used with randomization. The address cannot be + * a multicast one, i.e., bit 0 of byte 0 should not be set. + */ + const u8 *mac_addr; + + /** + * mac_addr_mask - MAC address mask used with randomization. + * + * Bits that are 0 in the mask should be randomized. Bits that are 1 in + * the mask should be taken as is from mac_addr. The mask should not + * allow the generation of a multicast address, i.e., bit 0 of byte 0 + * must be set. + */ + const u8 *mac_addr_mask; + + /* + * NOTE: Whenever adding new parameters here, please make sure + * wpa_scan_clone_params() and wpa_scan_free_params() get updated with + * matching changes. + */ +}; + +/** + * struct wpa_driver_auth_params - Authentication parameters + * Data for struct wpa_driver_ops::authenticate(). + */ +struct wpa_driver_auth_params { + int freq; + const u8 *bssid; + const u8 *ssid; + size_t ssid_len; + int auth_alg; + const u8 *ie; + size_t ie_len; + const u8 *wep_key[4]; + size_t wep_key_len[4]; + int wep_tx_keyidx; + int local_state_change; + + /** + * p2p - Whether this connection is a P2P group + */ + int p2p; + + /** + * sae_data - SAE elements for Authentication frame + * + * This buffer starts with the Authentication transaction sequence + * number field. If SAE is not used, this pointer is %NULL. + */ + const u8 *sae_data; + + /** + * sae_data_len - Length of sae_data buffer in octets + */ + size_t sae_data_len; +}; + +/** + * enum wps_mode - WPS mode + */ +enum wps_mode { + /** + * WPS_MODE_NONE - No WPS provisioning being used + */ + WPS_MODE_NONE, + + /** + * WPS_MODE_OPEN - WPS provisioning with AP that is in open mode + */ + WPS_MODE_OPEN, + + /** + * WPS_MODE_PRIVACY - WPS provisioning with AP that is using protection + */ + WPS_MODE_PRIVACY +}; + +/** + * struct hostapd_freq_params - Channel parameters + */ +struct hostapd_freq_params { + /** + * mode - Mode/band (HOSTAPD_MODE_IEEE80211A, ..) + */ + enum hostapd_hw_mode mode; + + /** + * freq - Primary channel center frequency in MHz + */ + int freq; + + /** + * channel - Channel number + */ + int channel; + + /** + * ht_enabled - Whether HT is enabled + */ + int ht_enabled; + + /** + * sec_channel_offset - Secondary channel offset for HT40 + * + * 0 = HT40 disabled, + * -1 = HT40 enabled, secondary channel below primary, + * 1 = HT40 enabled, secondary channel above primary + */ + int sec_channel_offset; + + /** + * vht_enabled - Whether VHT is enabled + */ + int vht_enabled; + + /** + * center_freq1 - Segment 0 center frequency in MHz + * + * Valid for both HT and VHT. + */ + int center_freq1; + + /** + * center_freq2 - Segment 1 center frequency in MHz + * + * Non-zero only for bandwidth 80 and an 80+80 channel + */ + int center_freq2; + + /** + * bandwidth - Channel bandwidth in MHz (20, 40, 80, 160) + */ + int bandwidth; +}; + +/** + * 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; + + /** + * bssid_hint - BSSID of a proposed AP + * + * This indicates which BSS has been found a suitable candidate for + * initial association for drivers that use driver/firmwate-based BSS + * selection. Unlike the @bssid parameter, @bssid_hint does not limit + * the driver from selecting other BSSes in the ESS. + */ + const u8 *bssid_hint; + + /** + * ssid - The selected SSID + */ + const u8 *ssid; + + /** + * ssid_len - Length of the SSID (1..32) + */ + size_t ssid_len; + + /** + * freq - channel parameters + */ + struct hostapd_freq_params freq; + + /** + * freq_hint - Frequency of the channel the proposed AP is using + * + * This provides a channel on which a suitable BSS has been found as a + * hint for the driver. Unlike the @freq parameter, @freq_hint does not + * limit the driver from selecting other channels for + * driver/firmware-based BSS selection. + */ + int freq_hint; + + /** + * bg_scan_period - Background scan period in seconds, 0 to disable + * background scan, or -1 to indicate no change to default driver + * configuration + */ + int bg_scan_period; + + /** + * beacon_int - Beacon interval for IBSS or 0 to use driver default + */ + int beacon_int; + + /** + * wpa_ie - WPA information element for (Re)Association Request + * WPA information element to be included in (Re)Association + * Request (including information element id and length). Use + * of this WPA IE is optional. If the driver generates the WPA + * IE, it can use pairwise_suite, group_suite, and + * key_mgmt_suite to select proper algorithms. In this case, + * the driver has to notify wpa_supplicant about the used WPA + * IE by generating an event that the interface code will + * convert into EVENT_ASSOCINFO data (see below). + * + * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE + * instead. The driver can determine which version is used by + * looking at the first byte of the IE (0xdd for WPA, 0x30 for + * WPA2/RSN). + * + * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE. + */ + const u8 *wpa_ie; + + /** + * wpa_ie_len - length of the wpa_ie + */ + size_t wpa_ie_len; + + /** + * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2 + */ + unsigned int wpa_proto; + + /** + * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*) + * + * This is usually ignored if @wpa_ie is used. + */ + unsigned int pairwise_suite; + + /** + * group_suite - Selected group cipher suite (WPA_CIPHER_*) + * + * This is usually ignored if @wpa_ie is used. + */ + unsigned int group_suite; + + /** + * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*) + * + * This is usually ignored if @wpa_ie is used. + */ + unsigned int key_mgmt_suite; + + /** + * auth_alg - Allowed authentication algorithms + * Bit field of WPA_AUTH_ALG_* + */ + int auth_alg; + + /** + * mode - Operation mode (infra/ibss) IEEE80211_MODE_* + */ + int mode; + + /** + * wep_key - WEP keys for static WEP configuration + */ + const u8 *wep_key[4]; + + /** + * wep_key_len - WEP key length for static WEP configuration + */ + size_t wep_key_len[4]; + + /** + * wep_tx_keyidx - WEP TX key index for static WEP configuration + */ + int wep_tx_keyidx; + + /** + * mgmt_frame_protection - IEEE 802.11w management frame protection + */ + enum mfp_options mgmt_frame_protection; + + /** + * ft_ies - IEEE 802.11r / FT information elements + * If the supplicant is using IEEE 802.11r (FT) and has the needed keys + * for fast transition, this parameter is set to include the IEs that + * are to be sent in the next FT Authentication Request message. + * update_ft_ies() handler is called to update the IEs for further + * FT messages in the sequence. + * + * The driver should use these IEs only if the target AP is advertising + * the same mobility domain as the one included in the MDIE here. + * + * In ap_scan=2 mode, the driver can use these IEs when moving to a new + * AP after the initial association. These IEs can only be used if the + * target AP is advertising support for FT and is using the same MDIE + * and SSID as the current AP. + * + * The driver is responsible for reporting the FT IEs received from the + * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE + * type. update_ft_ies() handler will then be called with the FT IEs to + * include in the next frame in the authentication sequence. + */ + const u8 *ft_ies; + + /** + * ft_ies_len - Length of ft_ies in bytes + */ + size_t ft_ies_len; + + /** + * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies) + * + * This value is provided to allow the driver interface easier access + * to the current mobility domain. This value is set to %NULL if no + * mobility domain is currently active. + */ + const u8 *ft_md; + + /** + * passphrase - RSN passphrase for PSK + * + * This value is made available only for WPA/WPA2-Personal (PSK) and + * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is + * the 8..63 character ASCII passphrase, if available. Please note that + * this can be %NULL if passphrase was not used to generate the PSK. In + * that case, the psk field must be used to fetch the PSK. + */ + const char *passphrase; + + /** + * psk - RSN PSK (alternative for passphrase for PSK) + * + * This value is made available only for WPA/WPA2-Personal (PSK) and + * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is + * the 32-octet (256-bit) PSK, if available. The driver wrapper should + * be prepared to handle %NULL value as an error. + */ + const u8 *psk; + + /** + * drop_unencrypted - Enable/disable unencrypted frame filtering + * + * Configure the driver to drop all non-EAPOL frames (both receive and + * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must + * still be allowed for key negotiation. + */ + int drop_unencrypted; + + /** + * prev_bssid - Previously used BSSID in this ESS + * + * When not %NULL, this is a request to use reassociation instead of + * association. + */ + const u8 *prev_bssid; + + /** + * wps - WPS mode + * + * If the driver needs to do special configuration for WPS association, + * this variable provides more information on what type of association + * is being requested. Most drivers should not need ot use this. + */ + enum wps_mode wps; + + /** + * p2p - Whether this connection is a P2P group + */ + int p2p; + + /** + * uapsd - UAPSD parameters for the network + * -1 = do not change defaults + * AP mode: 1 = enabled, 0 = disabled + * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE + */ + int uapsd; + + /** + * fixed_bssid - Whether to force this BSSID in IBSS mode + * 1 = Fix this BSSID and prevent merges. + * 0 = Do not fix BSSID. + */ + int fixed_bssid; + + /** + * fixed_freq - Fix control channel in IBSS mode + * 0 = don't fix control channel (default) + * 1 = fix control channel; this prevents IBSS merging with another + * channel + */ + int fixed_freq; + + /** + * disable_ht - Disable HT (IEEE 802.11n) for this connection + */ + int disable_ht; + + /** + * htcaps - HT Capabilities over-rides + * + * Only bits set in the mask will be used, and not all values are used + * by the kernel anyway. Currently, MCS, MPDU and MSDU fields are used. + * + * Pointer to struct ieee80211_ht_capabilities. + */ + const u8 *htcaps; + + /** + * htcaps_mask - HT Capabilities over-rides mask + * + * Pointer to struct ieee80211_ht_capabilities. + */ + const u8 *htcaps_mask; + +#ifdef CONFIG_VHT_OVERRIDES + /** + * disable_vht - Disable VHT for this connection + */ + int disable_vht; + + /** + * VHT capability overrides. + */ + const struct ieee80211_vht_capabilities *vhtcaps; + const struct ieee80211_vht_capabilities *vhtcaps_mask; +#endif /* CONFIG_VHT_OVERRIDES */ + + /** + * req_key_mgmt_offload - Request key management offload for connection + * + * Request key management offload for this connection if the device + * supports it. + */ + int req_key_mgmt_offload; + + /** + * Flag for indicating whether this association includes support for + * RRM (Radio Resource Measurements) + */ + int rrm_used; +}; + +enum hide_ssid { + NO_SSID_HIDING, + HIDDEN_SSID_ZERO_LEN, + HIDDEN_SSID_ZERO_CONTENTS +}; + +struct wowlan_triggers { + u8 any; + u8 disconnect; + u8 magic_pkt; + u8 gtk_rekey_failure; + u8 eap_identity_req; + u8 four_way_handshake; + u8 rfkill_release; +}; + +struct wpa_driver_ap_params { + /** + * head - Beacon head from IEEE 802.11 header to IEs before TIM IE + */ + u8 *head; + + /** + * head_len - Length of the head buffer in octets + */ + size_t head_len; + + /** + * tail - Beacon tail following TIM IE + */ + u8 *tail; + + /** + * tail_len - Length of the tail buffer in octets + */ + size_t tail_len; + + /** + * dtim_period - DTIM period + */ + int dtim_period; + + /** + * beacon_int - Beacon interval + */ + int beacon_int; + + /** + * basic_rates: -1 terminated array of basic rates in 100 kbps + * + * This parameter can be used to set a specific basic rate set for the + * BSS. If %NULL, default basic rate set is used. + */ + int *basic_rates; + + /** + * proberesp - Probe Response template + * + * This is used by drivers that reply to Probe Requests internally in + * AP mode and require the full Probe Response template. + */ + u8 *proberesp; + + /** + * proberesp_len - Length of the proberesp buffer in octets + */ + size_t proberesp_len; + + /** + * ssid - The SSID to use in Beacon/Probe Response frames + */ + const u8 *ssid; + + /** + * ssid_len - Length of the SSID (1..32) + */ + size_t ssid_len; + + /** + * hide_ssid - Whether to hide the SSID + */ + enum hide_ssid hide_ssid; + + /** + * pairwise_ciphers - WPA_CIPHER_* bitfield + */ + unsigned int pairwise_ciphers; + + /** + * group_cipher - WPA_CIPHER_* + */ + unsigned int group_cipher; + + /** + * key_mgmt_suites - WPA_KEY_MGMT_* bitfield + */ + unsigned int key_mgmt_suites; + + /** + * auth_algs - WPA_AUTH_ALG_* bitfield + */ + unsigned int auth_algs; + + /** + * wpa_version - WPA_PROTO_* bitfield + */ + unsigned int wpa_version; + + /** + * privacy - Whether privacy is used in the BSS + */ + int privacy; + + /** + * beacon_ies - WPS/P2P IE(s) for Beacon frames + * + * This is used to add IEs like WPS IE and P2P IE by drivers that do + * not use the full Beacon template. + */ + const struct wpabuf *beacon_ies; + + /** + * proberesp_ies - P2P/WPS IE(s) for Probe Response frames + * + * This is used to add IEs like WPS IE and P2P IE by drivers that + * reply to Probe Request frames internally. + */ + const struct wpabuf *proberesp_ies; + + /** + * assocresp_ies - WPS IE(s) for (Re)Association Response frames + * + * This is used to add IEs like WPS IE by drivers that reply to + * (Re)Association Request frames internally. + */ + const struct wpabuf *assocresp_ies; + + /** + * isolate - Whether to isolate frames between associated stations + * + * If this is non-zero, the AP is requested to disable forwarding of + * frames between associated stations. + */ + int isolate; + + /** + * cts_protect - Whether CTS protection is enabled + */ + int cts_protect; + + /** + * preamble - Whether short preamble is enabled + */ + int preamble; + + /** + * short_slot_time - Whether short slot time is enabled + * + * 0 = short slot time disable, 1 = short slot time enabled, -1 = do + * not set (e.g., when 802.11g mode is not in use) + */ + int short_slot_time; + + /** + * ht_opmode - HT operation mode or -1 if HT not in use + */ + int ht_opmode; + + /** + * interworking - Whether Interworking is enabled + */ + int interworking; + + /** + * hessid - Homogeneous ESS identifier or %NULL if not set + */ + const u8 *hessid; + + /** + * access_network_type - Access Network Type (0..15) + * + * This is used for filtering Probe Request frames when Interworking is + * enabled. + */ + u8 access_network_type; + + /** + * ap_max_inactivity - Timeout in seconds to detect STA's inactivity + * + * This is used by driver which advertises this capability. + */ + int ap_max_inactivity; + + /** + * ctwindow - Client Traffic Window (in TUs) + */ + u8 p2p_go_ctwindow; + + /** + * smps_mode - SMPS mode + * + * SMPS mode to be used by the AP, specified as the relevant bits of + * ht_capab (i.e. HT_CAP_INFO_SMPS_*). + */ + unsigned int smps_mode; + + /** + * disable_dgaf - Whether group-addressed frames are disabled + */ + int disable_dgaf; + + /** + * osen - Whether OSEN security is enabled + */ + int osen; + + /** + * freq - Channel parameters for dynamic bandwidth changes + */ + struct hostapd_freq_params *freq; + + /** + * reenable - Whether this is to re-enable beaconing + */ + int reenable; +}; + +struct wpa_driver_mesh_bss_params { +#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS 0x00000001 + /* + * TODO: Other mesh configuration parameters would go here. + * See NL80211_MESHCONF_* for all the mesh config parameters. + */ + unsigned int flags; + int peer_link_timeout; +}; + +struct wpa_driver_mesh_join_params { + const u8 *meshid; + int meshid_len; + const int *basic_rates; + const u8 *ies; + int ie_len; + struct hostapd_freq_params freq; + int beacon_int; + int max_peer_links; + struct wpa_driver_mesh_bss_params conf; +#define WPA_DRIVER_MESH_FLAG_USER_MPM 0x00000001 +#define WPA_DRIVER_MESH_FLAG_DRIVER_MPM 0x00000002 +#define WPA_DRIVER_MESH_FLAG_SAE_AUTH 0x00000004 +#define WPA_DRIVER_MESH_FLAG_AMPE 0x00000008 + unsigned int flags; +}; + +/** + * struct wpa_driver_capa - Driver capability information + */ +struct wpa_driver_capa { +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA 0x00000001 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2 0x00000002 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK 0x00000004 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK 0x00000008 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010 +#define WPA_DRIVER_CAPA_KEY_MGMT_FT 0x00000020 +#define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK 0x00000040 +#define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080 +#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B 0x00000100 +#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 0x00000200 + /** Bitfield of supported key management suites */ + unsigned int key_mgmt; + +#define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001 +#define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002 +#define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004 +#define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008 +#define WPA_DRIVER_CAPA_ENC_WEP128 0x00000010 +#define WPA_DRIVER_CAPA_ENC_GCMP 0x00000020 +#define WPA_DRIVER_CAPA_ENC_GCMP_256 0x00000040 +#define WPA_DRIVER_CAPA_ENC_CCMP_256 0x00000080 +#define WPA_DRIVER_CAPA_ENC_BIP 0x00000100 +#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_128 0x00000200 +#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256 0x00000400 +#define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256 0x00000800 +#define WPA_DRIVER_CAPA_ENC_GTK_NOT_USED 0x00001000 + /** Bitfield of supported cipher suites */ + unsigned int enc; + +#define WPA_DRIVER_AUTH_OPEN 0x00000001 +#define WPA_DRIVER_AUTH_SHARED 0x00000002 +#define WPA_DRIVER_AUTH_LEAP 0x00000004 + /** Bitfield of supported IEEE 802.11 authentication algorithms */ + 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 +/** Driver takes care of all DFS operations */ +#define WPA_DRIVER_FLAGS_DFS_OFFLOAD 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 +/** Driver is for a wired Ethernet interface */ +#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 supports dynamic HT 20/40 MHz channel changes during BSS lifetime */ +#define WPA_DRIVER_FLAGS_HT_2040_COEX 0x00000100 +/** Driver supports concurrent P2P operations */ +#define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200 +/** + * Driver uses the initial interface as a dedicated management interface, i.e., + * it cannot be used for P2P group operations or non-P2P purposes. + */ +#define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400 +/** This interface is P2P capable (P2P GO or P2P Client) */ +#define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800 +/** Driver supports station and key removal when stopping an AP */ +#define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT 0x00001000 +/** + * Driver uses the initial interface for P2P management interface and non-P2P + * purposes (e.g., connect to infra AP), but this interface cannot be used for + * P2P group operations. + */ +#define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P 0x00002000 +/** + * Driver is known to use sane error codes, i.e., when it indicates that + * something (e.g., association) fails, there was indeed a failure and the + * operation does not end up getting completed successfully later. + */ +#define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000 +/** Driver supports off-channel TX */ +#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000 +/** Driver indicates TX status events for EAPOL Data frames */ +#define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS 0x00010000 +/** Driver indicates TX status events for Deauth/Disassoc frames */ +#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000 +/** Driver supports roaming (BSS selection) in firmware */ +#define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000 +/** Driver supports operating as a TDLS peer */ +#define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000 +/** Driver requires external TDLS setup/teardown/discovery */ +#define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000 +/** Driver indicates support for Probe Response offloading in AP mode */ +#define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD 0x00200000 +/** Driver supports U-APSD in AP mode */ +#define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000 +/** Driver supports inactivity timer in AP mode */ +#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000 +/** Driver expects user space implementation of MLME in AP mode */ +#define WPA_DRIVER_FLAGS_AP_MLME 0x01000000 +/** Driver supports SAE with user space SME */ +#define WPA_DRIVER_FLAGS_SAE 0x02000000 +/** Driver makes use of OBSS scan mechanism in wpa_supplicant */ +#define WPA_DRIVER_FLAGS_OBSS_SCAN 0x04000000 +/** Driver supports IBSS (Ad-hoc) mode */ +#define WPA_DRIVER_FLAGS_IBSS 0x08000000 +/** Driver supports radar detection */ +#define WPA_DRIVER_FLAGS_RADAR 0x10000000 +/** Driver supports a dedicated interface for P2P Device */ +#define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE 0x20000000 +/** Driver supports QoS Mapping */ +#define WPA_DRIVER_FLAGS_QOS_MAPPING 0x40000000 +/** Driver supports CSA in AP mode */ +#define WPA_DRIVER_FLAGS_AP_CSA 0x80000000 +/** Driver supports mesh */ +#define WPA_DRIVER_FLAGS_MESH 0x0000000100000000ULL +/** Driver support ACS offload */ +#define WPA_DRIVER_FLAGS_ACS_OFFLOAD 0x0000000200000000ULL +/** Driver supports key management offload */ +#define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD 0x0000000400000000ULL +/** Driver supports TDLS channel switching */ +#define WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH 0x0000000800000000ULL +/** Driver supports IBSS with HT datarates */ +#define WPA_DRIVER_FLAGS_HT_IBSS 0x0000001000000000ULL +/** Driver supports IBSS with VHT datarates */ +#define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL +/** Driver supports automatic band selection */ +#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL + u64 flags; + +#define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001 +#define WPA_DRIVER_SMPS_MODE_DYNAMIC 0x00000002 + unsigned int smps_modes; + + unsigned int wmm_ac_supported:1; + + unsigned int mac_addr_rand_scan_supported:1; + unsigned int mac_addr_rand_sched_scan_supported:1; + + /** Maximum number of supported active probe SSIDs */ + int max_scan_ssids; + + /** Maximum number of supported active probe SSIDs for sched_scan */ + int max_sched_scan_ssids; + + /** Whether sched_scan (offloaded scanning) is supported */ + int sched_scan_supported; + + /** Maximum number of supported match sets for sched_scan */ + int max_match_sets; + + /** + * max_remain_on_chan - Maximum remain-on-channel duration in msec + */ + unsigned int max_remain_on_chan; + + /** + * max_stations - Maximum number of associated stations the driver + * supports in AP mode + */ + unsigned int max_stations; + + /** + * probe_resp_offloads - Bitmap of supported protocols by the driver + * for Probe Response offloading. + */ +/** Driver Probe Response offloading support for WPS ver. 1 */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS 0x00000001 +/** Driver Probe Response offloading support for WPS ver. 2 */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2 0x00000002 +/** Driver Probe Response offloading support for P2P */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P 0x00000004 +/** Driver Probe Response offloading support for IEEE 802.11u (Interworking) */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING 0x00000008 + unsigned int probe_resp_offloads; + + unsigned int max_acl_mac_addrs; + + /** + * Number of supported concurrent channels + */ + unsigned int num_multichan_concurrent; + + /** + * extended_capa - extended capabilities in driver/device + * + * Must be allocated and freed by driver and the pointers must be + * valid for the lifetime of the driver, i.e., freed in deinit() + */ + const u8 *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; + + struct wowlan_triggers wowlan_triggers; + +/** Driver adds the DS Params Set IE in Probe Request frames */ +#define WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES 0x00000001 +/** Driver adds the WFA TPC IE in Probe Request frames */ +#define WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES 0x00000002 +/** Driver handles quiet period requests */ +#define WPA_DRIVER_FLAGS_QUIET 0x00000004 +/** + * Driver is capable of inserting the current TX power value into the body of + * transmitted frames. + * Background: Some Action frames include a TPC Report IE. This IE contains a + * TX power field, which has to be updated by lower layers. One such Action + * frame is Link Measurement Report (part of RRM). Another is TPC Report (part + * of spectrum management). Note that this insertion takes place at a fixed + * offset, namely the 6th byte in the Action frame body. + */ +#define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008 + u32 rrm_flags; + + /* Driver concurrency capabilities */ + unsigned int conc_capab; + /* Maximum number of concurrent channels on 2.4 GHz */ + unsigned int max_conc_chan_2_4; + /* Maximum number of concurrent channels on 5 GHz */ + unsigned int max_conc_chan_5_0; +}; + + +struct hostapd_data; + +struct hostap_sta_driver_data { + unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes; + unsigned long current_tx_rate; + unsigned long inactive_msec; + unsigned long flags; + unsigned long num_ps_buf_frames; + unsigned long tx_retry_failed; + unsigned long tx_retry_count; + int last_rssi; + int last_ack_rssi; +}; + +struct hostapd_sta_add_params { + const u8 *addr; + u16 aid; + u16 capability; + const u8 *supp_rates; + size_t supp_rates_len; + u16 listen_interval; + const struct ieee80211_ht_capabilities *ht_capabilities; + const struct ieee80211_vht_capabilities *vht_capabilities; + int vht_opmode_enabled; + u8 vht_opmode; + u32 flags; /* bitmask of WPA_STA_* flags */ + u32 flags_mask; /* unset bits in flags */ +#ifdef CONFIG_MESH + enum mesh_plink_state plink_state; +#endif /* CONFIG_MESH */ + int set; /* Set STA parameters instead of add */ + u8 qosinfo; + const u8 *ext_capab; + size_t ext_capab_len; + const u8 *supp_channels; + size_t supp_channels_len; + const u8 *supp_oper_classes; + size_t supp_oper_classes_len; +}; + +struct mac_address { + u8 addr[ETH_ALEN]; +}; + +struct hostapd_acl_params { + u8 acl_policy; + unsigned int num_mac_acl; + struct mac_address mac_acl[0]; +}; + +enum wpa_driver_if_type { + /** + * WPA_IF_STATION - Station mode interface + */ + WPA_IF_STATION, + + /** + * WPA_IF_AP_VLAN - AP mode VLAN interface + * + * This interface shares its address and Beacon frame with the main + * BSS. + */ + WPA_IF_AP_VLAN, + + /** + * WPA_IF_AP_BSS - AP mode BSS interface + * + * This interface has its own address and Beacon frame. + */ + WPA_IF_AP_BSS, + + /** + * WPA_IF_P2P_GO - P2P Group Owner + */ + WPA_IF_P2P_GO, + + /** + * WPA_IF_P2P_CLIENT - P2P Client + */ + WPA_IF_P2P_CLIENT, + + /** + * WPA_IF_P2P_GROUP - P2P Group interface (will become either + * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known) + */ + WPA_IF_P2P_GROUP, + + /** + * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the + * abstracted P2P Device function in the driver + */ + WPA_IF_P2P_DEVICE, + + /* + * WPA_IF_MESH - Mesh interface + */ + WPA_IF_MESH, + + /* + * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only) + */ + WPA_IF_TDLS, + + /* + * WPA_IF_IBSS - IBSS interface (used for pref freq only) + */ + WPA_IF_IBSS, +}; + +struct wpa_init_params { + void *global_priv; + const u8 *bssid; + const char *ifname; + const char *driver_params; + int use_pae_group_addr; + char **bridge; + size_t num_bridge; + + u8 *own_addr; /* buffer for writing own MAC address */ +}; + + +struct wpa_bss_params { + /** Interface name (for multi-SSID/VLAN support) */ + const char *ifname; + /** Whether IEEE 802.1X or WPA/WPA2 is enabled */ + int enabled; + + int wpa; + int ieee802_1x; + int wpa_group; + int wpa_pairwise; + int wpa_key_mgmt; + int rsn_preauth; + enum mfp_options ieee80211w; +}; + +#define WPA_STA_AUTHORIZED BIT(0) +#define WPA_STA_WMM BIT(1) +#define WPA_STA_SHORT_PREAMBLE BIT(2) +#define WPA_STA_MFP BIT(3) +#define WPA_STA_TDLS_PEER BIT(4) +#define WPA_STA_AUTHENTICATED BIT(5) + +enum tdls_oper { + TDLS_DISCOVERY_REQ, + TDLS_SETUP, + TDLS_TEARDOWN, + TDLS_ENABLE_LINK, + TDLS_DISABLE_LINK, + TDLS_ENABLE, + TDLS_DISABLE +}; + +enum wnm_oper { + WNM_SLEEP_ENTER_CONFIRM, + WNM_SLEEP_ENTER_FAIL, + WNM_SLEEP_EXIT_CONFIRM, + WNM_SLEEP_EXIT_FAIL, + WNM_SLEEP_TFS_REQ_IE_ADD, /* STA requests driver to add TFS req IE */ + WNM_SLEEP_TFS_REQ_IE_NONE, /* STA requests empty TFS req IE */ + WNM_SLEEP_TFS_REQ_IE_SET, /* AP requests driver to set TFS req IE for + * a STA */ + WNM_SLEEP_TFS_RESP_IE_ADD, /* AP requests driver to add TFS resp IE + * for a STA */ + WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */ + WNM_SLEEP_TFS_RESP_IE_SET, /* AP requests driver to set TFS resp IE + * for a STA */ + WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */ +}; + +/* enum chan_width - Channel width definitions */ +enum chan_width { + CHAN_WIDTH_20_NOHT, + CHAN_WIDTH_20, + CHAN_WIDTH_40, + CHAN_WIDTH_80, + CHAN_WIDTH_80P80, + CHAN_WIDTH_160, + CHAN_WIDTH_UNKNOWN +}; + +/** + * struct wpa_signal_info - Information about channel signal quality + */ +struct wpa_signal_info { + u32 frequency; + int above_threshold; + int current_signal; + int avg_signal; + int avg_beacon_signal; + int current_noise; + int current_txrate; + enum chan_width chanwidth; + int center_frq1; + int center_frq2; +}; + +/** + * struct beacon_data - Beacon data + * @head: Head portion of Beacon frame (before TIM IE) + * @tail: Tail portion of Beacon frame (after TIM IE) + * @beacon_ies: Extra information element(s) to add into Beacon frames or %NULL + * @proberesp_ies: Extra information element(s) to add into Probe Response + * frames or %NULL + * @assocresp_ies: Extra information element(s) to add into (Re)Association + * Response frames or %NULL + * @probe_resp: Probe Response frame template + * @head_len: Length of @head + * @tail_len: Length of @tail + * @beacon_ies_len: Length of beacon_ies in octets + * @proberesp_ies_len: Length of proberesp_ies in octets + * @proberesp_ies_len: Length of proberesp_ies in octets + * @probe_resp_len: Length of probe response template (@probe_resp) + */ +struct beacon_data { + u8 *head, *tail; + u8 *beacon_ies; + u8 *proberesp_ies; + u8 *assocresp_ies; + u8 *probe_resp; + + size_t head_len, tail_len; + size_t beacon_ies_len; + size_t proberesp_ies_len; + size_t assocresp_ies_len; + size_t probe_resp_len; +}; + +/** + * struct csa_settings - Settings for channel switch command + * @cs_count: Count in Beacon frames (TBTT) to perform the switch + * @block_tx: 1 - block transmission for CSA period + * @freq_params: Next channel frequency parameter + * @beacon_csa: Beacon/probe resp/asooc resp info for CSA period + * @beacon_after: Next beacon/probe resp/asooc resp info + * @counter_offset_beacon: Offset to the count field in beacon's tail + * @counter_offset_presp: Offset to the count field in probe resp. + */ +struct csa_settings { + u8 cs_count; + u8 block_tx; + + struct hostapd_freq_params freq_params; + struct beacon_data beacon_csa; + struct beacon_data beacon_after; + + u16 counter_offset_beacon; + u16 counter_offset_presp; +}; + +/* TDLS peer capabilities for send_tdls_mgmt() */ +enum tdls_peer_capability { + TDLS_PEER_HT = BIT(0), + TDLS_PEER_VHT = BIT(1), + TDLS_PEER_WMM = BIT(2), +}; + +/* valid info in the wmm_params struct */ +enum wmm_params_valid_info { + WMM_PARAMS_UAPSD_QUEUES_INFO = BIT(0), +}; + +/** + * struct wmm_params - WMM parameterss configured for this association + * @info_bitmap: Bitmap of valid wmm_params info; indicates what fields + * of the struct contain valid information. + * @uapsd_queues: Bitmap of ACs configured for uapsd (valid only if + * %WMM_PARAMS_UAPSD_QUEUES_INFO is set) + */ +struct wmm_params { + u8 info_bitmap; + u8 uapsd_queues; +}; + +#ifdef CONFIG_MACSEC +struct macsec_init_params { + Boolean always_include_sci; + Boolean use_es; + Boolean use_scb; +}; +#endif /* CONFIG_MACSEC */ + +enum drv_br_port_attr { + DRV_BR_PORT_ATTR_PROXYARP, + DRV_BR_PORT_ATTR_HAIRPIN_MODE, +}; + +enum drv_br_net_param { + DRV_BR_NET_PARAM_GARP_ACCEPT, + DRV_BR_MULTICAST_SNOOPING, +}; + +struct drv_acs_params { + /* Selected mode (HOSTAPD_MODE_*) */ + enum hostapd_hw_mode hw_mode; + + /* Indicates whether HT is enabled */ + int ht_enabled; + + /* Indicates whether HT40 is enabled */ + int ht40_enabled; + + /* Indicates whether VHT is enabled */ + int vht_enabled; + + /* Configured ACS channel width */ + u16 ch_width; + + /* ACS channel list info */ + unsigned int ch_list_len; + const u8 *ch_list; + const int *freq_list; +}; + + +/** + * struct wpa_driver_ops - Driver interface API definition + * + * This structure defines the API that each driver interface needs to implement + * for core wpa_supplicant code. All driver specific functionality is captured + * in this wrapper. + */ +struct wpa_driver_ops { + /** Name of the driver interface */ + const char *name; + /** One line description of the driver interface */ + const char *desc; + + /** + * get_bssid - Get the current BSSID + * @priv: private driver interface data + * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes) + * + * Returns: 0 on success, -1 on failure + * + * Query kernel driver for the current BSSID and copy it to bssid. + * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not + * associated. + */ + int (*get_bssid)(void *priv, u8 *bssid); + + /** + * get_ssid - Get the current SSID + * @priv: private driver interface data + * @ssid: buffer for SSID (at least 32 bytes) + * + * Returns: Length of the SSID on success, -1 on failure + * + * Query kernel driver for the current SSID and copy it to ssid. + * Returning zero is recommended if the STA is not associated. + * + * Note: SSID is an array of octets, i.e., it is not nul terminated and + * can, at least in theory, contain control characters (including nul) + * and as such, should be processed as binary data, not a printable + * string. + */ + int (*get_ssid)(void *priv, u8 *ssid); + + /** + * set_key - Configure encryption key + * @ifname: Interface name (for multi-SSID/VLAN support) + * @priv: private driver interface data + * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK, + * %WPA_ALG_GCMP, %WPA_ALG_GCMP_256, %WPA_ALG_CCMP_256, + * %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256, + * %WPA_ALG_BIP_CMAC_256); + * %WPA_ALG_NONE clears the key. + * @addr: Address of the peer STA (BSSID of the current AP when setting + * pairwise key in station mode), ff:ff:ff:ff:ff:ff for + * broadcast keys, %NULL for default keys that are used both for + * broadcast and unicast; when clearing keys, %NULL is used to + * indicate that both the broadcast-only and default key of the + * specified key index is to be cleared + * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for + * IGTK + * @set_tx: configure this key as the default Tx key (only used when + * driver does not support separate unicast/individual key + * @seq: sequence number/packet number, seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys); %NULL if not set + * @seq_len: length of the seq, depends on the algorithm: + * TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets + * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, + * 8-byte Rx Mic Key + * @key_len: length of the key buffer in octets (WEP: 5 or 13, + * TKIP: 32, CCMP/GCMP: 16, IGTK: 16) + * + * Returns: 0 on success, -1 on failure + * + * Configure the given key for the kernel driver. If the driver + * supports separate individual keys (4 default keys + 1 individual), + * addr can be used to determine whether the key is default or + * individual. If only 4 keys are supported, the default key with key + * index 0 is used as the individual key. STA must be configured to use + * it as the default Tx key (set_tx is set) and accept Rx for all the + * key indexes. In most cases, WPA uses only key indexes 1 and 2 for + * broadcast keys, so key index 0 is available for this kind of + * configuration. + * + * Please note that TKIP keys include separate TX and RX MIC keys and + * some drivers may expect them in different order than wpa_supplicant + * is using. If the TX/RX keys are swapped, all TKIP encrypted packets + * will trigger Michael MIC errors. This can be fixed by changing the + * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key + * in driver_*.c set_key() implementation, see driver_ndis.c for an + * example on how this can be done. + */ + int (*set_key)(const char *ifname, void *priv, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); + + /** + * init - Initialize driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * + * Returns: Pointer to private data, %NULL on failure + * + * Initialize driver interface, including event processing for kernel + * driver events (e.g., associated, scan results, Michael MIC failure). + * This function can allocate a private configuration data area for + * @ctx, file descriptor, interface name, etc. information that may be + * needed in future driver operations. If this is not used, non-NULL + * value will need to be returned because %NULL is used to indicate + * failure. The returned value will be used as 'void *priv' data for + * all other driver_ops functions. + * + * The main event loop (eloop.c) of wpa_supplicant can be used to + * register callback for read sockets (eloop_register_read_sock()). + * + * See below for more information about events and + * wpa_supplicant_event() function. + */ + void * (*init)(void *ctx, const char *ifname); + + /** + * deinit - Deinitialize driver interface + * @priv: private driver interface data from init() + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in init() handler. + */ + void (*deinit)(void *priv); + + /** + * set_param - Set driver configuration parameters + * @priv: private driver interface data from init() + * @param: driver specific configuration parameters + * + * Returns: 0 on success, -1 on failure + * + * Optional handler for notifying driver interface about configuration + * parameters (driver_param). + */ + int (*set_param)(void *priv, const char *param); + + /** + * set_countermeasures - Enable/disable TKIP countermeasures + * @priv: private driver interface data + * @enabled: 1 = countermeasures enabled, 0 = disabled + * + * Returns: 0 on success, -1 on failure + * + * Configure TKIP countermeasures. When these are enabled, the driver + * should drop all received and queued frames that are using TKIP. + */ + int (*set_countermeasures)(void *priv, int enabled); + + /** + * deauthenticate - Request driver to deauthenticate + * @priv: private driver interface data + * @addr: peer address (BSSID of the AP) + * @reason_code: 16-bit reason code to be sent in the deauthentication + * frame + * + * Returns: 0 on success, -1 on failure + */ + int (*deauthenticate)(void *priv, const u8 *addr, int reason_code); + + /** + * associate - Request driver to associate + * @priv: private driver interface data + * @params: association parameters + * + * Returns: 0 on success, -1 on failure + */ + int (*associate)(void *priv, + struct wpa_driver_associate_params *params); + + /** + * add_pmkid - Add PMKSA cache entry to the driver + * @priv: private driver interface data + * @bssid: BSSID for the PMKSA cache entry + * @pmkid: PMKID for the PMKSA cache entry + * + * Returns: 0 on success, -1 on failure + * + * This function is called when a new PMK is received, as a result of + * either normal authentication or RSN pre-authentication. + * + * If the driver generates RSN IE, i.e., it does not use wpa_ie in + * associate(), add_pmkid() can be used to add new PMKSA cache entries + * in the driver. If the driver uses wpa_ie from wpa_supplicant, this + * driver_ops function does not need to be implemented. Likewise, if + * the driver does not support WPA, this function is not needed. + */ + int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + + /** + * remove_pmkid - Remove PMKSA cache entry to the driver + * @priv: private driver interface data + * @bssid: BSSID for the PMKSA cache entry + * @pmkid: PMKID for the PMKSA cache entry + * + * Returns: 0 on success, -1 on failure + * + * This function is called when the supplicant drops a PMKSA cache + * entry for any reason. + * + * If the driver generates RSN IE, i.e., it does not use wpa_ie in + * associate(), remove_pmkid() can be used to synchronize PMKSA caches + * between the driver and wpa_supplicant. If the driver uses wpa_ie + * from wpa_supplicant, this driver_ops function does not need to be + * implemented. Likewise, if the driver does not support WPA, this + * function is not needed. + */ + int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + + /** + * flush_pmkid - Flush PMKSA cache + * @priv: private driver interface data + * + * Returns: 0 on success, -1 on failure + * + * This function is called when the supplicant drops all PMKSA cache + * entries for any reason. + * + * If the driver generates RSN IE, i.e., it does not use wpa_ie in + * associate(), remove_pmkid() can be used to synchronize PMKSA caches + * between the driver and wpa_supplicant. If the driver uses wpa_ie + * from wpa_supplicant, this driver_ops function does not need to be + * implemented. Likewise, if the driver does not support WPA, this + * function is not needed. + */ + int (*flush_pmkid)(void *priv); + + /** + * get_capa - Get driver capabilities + * @priv: private driver interface data + * + * Returns: 0 on success, -1 on failure + * + * Get driver/firmware/hardware capabilities. + */ + int (*get_capa)(void *priv, struct wpa_driver_capa *capa); + + /** + * poll - Poll driver for association information + * @priv: private driver interface data + * + * This is an option callback that can be used when the driver does not + * provide event mechanism for association events. This is called when + * receiving WPA EAPOL-Key messages that require association + * information. The driver interface is supposed to generate associnfo + * event before returning from this callback function. In addition, the + * driver interface should generate an association event after having + * sent out associnfo. + */ + void (*poll)(void *priv); + + /** + * get_ifname - Get interface name + * @priv: private driver interface data + * + * Returns: Pointer to the interface name. This can differ from the + * interface name used in init() call. Init() is called first. + * + * This optional function can be used to allow the driver interface to + * replace the interface name with something else, e.g., based on an + * interface mapping from a more descriptive name. + */ + const char * (*get_ifname)(void *priv); + + /** + * get_mac_addr - Get own MAC address + * @priv: private driver interface data + * + * Returns: Pointer to own MAC address or %NULL on failure + * + * This optional function can be used to get the own MAC address of the + * device from the driver interface code. This is only needed if the + * l2_packet implementation for the OS does not provide easy access to + * a MAC address. */ + const u8 * (*get_mac_addr)(void *priv); + + /** + * set_operstate - Sets device operating state to DORMANT or UP + * @priv: private driver interface data + * @state: 0 = dormant, 1 = up + * Returns: 0 on success, -1 on failure + * + * This is an optional function that can be used on operating systems + * that support a concept of controlling network device state from user + * space applications. This function, if set, gets called with + * state = 1 when authentication has been completed and with state = 0 + * when connection is lost. + */ + int (*set_operstate)(void *priv, int state); + + /** + * mlme_setprotection - MLME-SETPROTECTION.request primitive + * @priv: Private driver interface data + * @addr: Address of the station for which to set protection (may be + * %NULL for group keys) + * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_* + * @key_type: MLME_SETPROTECTION_KEY_TYPE_* + * Returns: 0 on success, -1 on failure + * + * This is an optional function that can be used to set the driver to + * require protection for Tx and/or Rx frames. This uses the layer + * interface defined in IEEE 802.11i-2004 clause 10.3.22.1 + * (MLME-SETPROTECTION.request). Many drivers do not use explicit + * set protection operation; instead, they set protection implicitly + * based on configured keys. + */ + int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type, + int key_type); + + /** + * get_hw_feature_data - Get hardware support data (channels and rates) + * @priv: Private driver interface data + * @num_modes: Variable for returning the number of returned modes + * flags: Variable for returning hardware feature flags + * Returns: Pointer to allocated hardware data on success or %NULL on + * failure. Caller is responsible for freeing this. + */ + struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, + u16 *num_modes, + u16 *flags); + + /** + * send_mlme - Send management frame from MLME + * @priv: Private driver interface data + * @data: IEEE 802.11 management frame with IEEE 802.11 header + * @data_len: Size of the management frame + * @noack: Do not wait for this frame to be acked (disable retries) + * @freq: Frequency (in MHz) to send the frame on, or 0 to let the + * driver decide + * Returns: 0 on success, -1 on failure + */ + int (*send_mlme)(void *priv, const u8 *data, size_t data_len, + int noack, unsigned int freq); + + /** + * 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); + + /** + * get_scan_results2 - Fetch the latest scan results + * @priv: private driver interface data + * + * Returns: Allocated buffer of scan results (caller is responsible for + * freeing the data structure) on success, NULL on failure + */ + struct wpa_scan_results * (*get_scan_results2)(void *priv); + + /** + * set_country - Set country + * @priv: Private driver interface data + * @alpha2: country to which to switch to + * Returns: 0 on success, -1 on failure + * + * This function is for drivers which support some form + * of setting a regulatory domain. + */ + int (*set_country)(void *priv, const char *alpha2); + + /** + * get_country - Get country + * @priv: Private driver interface data + * @alpha2: Buffer for returning country code (at least 3 octets) + * Returns: 0 on success, -1 on failure + */ + int (*get_country)(void *priv, char *alpha2); + + /** + * global_init - Global driver initialization + * Returns: Pointer to private data (global), %NULL on failure + * + * This optional function is called to initialize the driver wrapper + * for global data, i.e., data that applies to all interfaces. If this + * function is implemented, global_deinit() will also need to be + * implemented to free the private data. The driver will also likely + * use init2() function instead of init() to get the pointer to global + * data available to per-interface initializer. + */ + void * (*global_init)(void); + + /** + * global_deinit - Global driver deinitialization + * @priv: private driver global data from global_init() + * + * Terminate any global driver related functionality and free the + * global data structure. + */ + void (*global_deinit)(void *priv); + + /** + * init2 - Initialize driver interface (with global data) + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * @global_priv: private driver global data from global_init() + * Returns: Pointer to private data, %NULL on failure + * + * This function can be used instead of init() if the driver wrapper + * uses global data. + */ + void * (*init2)(void *ctx, const char *ifname, void *global_priv); + + /** + * get_interfaces - Get information about available interfaces + * @global_priv: private driver global data from global_init() + * Returns: Allocated buffer of interface information (caller is + * responsible for freeing the data structure) on success, NULL on + * failure + */ + struct wpa_interface_info * (*get_interfaces)(void *global_priv); + + /** + * scan2 - Request the driver to initiate scan + * @priv: private driver interface data + * @params: Scan parameters + * + * Returns: 0 on success, -1 on failure + * + * Once the scan results are ready, the driver should report scan + * results event for wpa_supplicant which will eventually request the + * results with wpa_driver_get_scan_results2(). + */ + int (*scan2)(void *priv, struct wpa_driver_scan_params *params); + + /** + * authenticate - Request driver to authenticate + * @priv: private driver interface data + * @params: authentication parameters + * Returns: 0 on success, -1 on failure + * + * This is an optional function that can be used with drivers that + * support separate authentication and association steps, i.e., when + * wpa_supplicant can act as the SME. If not implemented, associate() + * function is expected to take care of IEEE 802.11 authentication, + * too. + */ + int (*authenticate)(void *priv, + struct wpa_driver_auth_params *params); + + /** + * set_ap - Set Beacon and Probe Response information for AP mode + * @priv: Private driver interface data + * @params: Parameters to use in AP mode + * + * This function is used to configure Beacon template and/or extra IEs + * to add for Beacon and Probe Response frames for the driver in + * AP mode. The driver is responsible for building the full Beacon + * frame by concatenating the head part with TIM IE generated by the + * driver/firmware and finishing with the tail part. Depending on the + * driver architectue, this can be done either by using the full + * template or the set of additional IEs (e.g., WPS and P2P IE). + * Similarly, Probe Response processing depends on the driver design. + * If the driver (or firmware) takes care of replying to Probe Request + * frames, the extra IEs provided here needs to be added to the Probe + * Response frames. + * + * Returns: 0 on success, -1 on failure + */ + int (*set_ap)(void *priv, struct wpa_driver_ap_params *params); + + /** + * set_acl - Set ACL in AP mode + * @priv: Private driver interface data + * @params: Parameters to configure ACL + * Returns: 0 on success, -1 on failure + * + * This is used only for the drivers which support MAC address ACL. + */ + int (*set_acl)(void *priv, struct hostapd_acl_params *params); + + /** + * hapd_init - Initialize driver interface (hostapd only) + * @hapd: Pointer to hostapd context + * @params: Configuration for the driver wrapper + * Returns: Pointer to private data, %NULL on failure + * + * This function is used instead of init() or init2() when the driver + * wrapper is used with hostapd. + */ + void * (*hapd_init)(struct hostapd_data *hapd, + struct wpa_init_params *params); + + /** + * hapd_deinit - Deinitialize driver interface (hostapd only) + * @priv: Private driver interface data from hapd_init() + */ + void (*hapd_deinit)(void *priv); + + /** + * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only) + * @priv: Private driver interface data + * @params: BSS parameters + * Returns: 0 on success, -1 on failure + * + * This is an optional function to configure the kernel driver to + * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This + * can be left undefined (set to %NULL) if IEEE 802.1X support is + * always enabled and the driver uses set_ap() to set WPA/RSN IE + * for Beacon frames. + * + * DEPRECATED - use set_ap() instead + */ + int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params); + + /** + * set_privacy - Enable/disable privacy (AP only) + * @priv: Private driver interface data + * @enabled: 1 = privacy enabled, 0 = disabled + * Returns: 0 on success, -1 on failure + * + * This is an optional function to configure privacy field in the + * kernel driver for Beacon frames. This can be left undefined (set to + * %NULL) if the driver uses the Beacon template from set_ap(). + * + * DEPRECATED - use set_ap() instead + */ + int (*set_privacy)(void *priv, int enabled); + + /** + * get_seqnum - Fetch the current TSC/packet number (AP only) + * @ifname: The interface name (main or virtual) + * @priv: Private driver interface data + * @addr: MAC address of the station or %NULL for group keys + * @idx: Key index + * @seq: Buffer for returning the latest used TSC/packet number + * Returns: 0 on success, -1 on failure + * + * This function is used to fetch the last used TSC/packet number for + * a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group + * keys, so there is no strict requirement on implementing support for + * unicast keys (i.e., addr != %NULL). + */ + int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq); + + /** + * flush - Flush all association stations (AP only) + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This function requests the driver to disassociate all associated + * stations. This function does not need to be implemented if the + * driver does not process association frames internally. + */ + int (*flush)(void *priv); + + /** + * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP) + * @priv: Private driver interface data + * @elem: Information elements + * @elem_len: Length of the elem buffer in octets + * Returns: 0 on success, -1 on failure + * + * This is an optional function to add information elements in the + * kernel driver for Beacon and Probe Response frames. This can be left + * undefined (set to %NULL) if the driver uses the Beacon template from + * set_ap(). + * + * DEPRECATED - use set_ap() instead + */ + int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len); + + /** + * read_sta_data - Fetch station data + * @priv: Private driver interface data + * @data: Buffer for returning station information + * @addr: MAC address of the station + * Returns: 0 on success, -1 on failure + */ + int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr); + + /** + * hapd_send_eapol - Send an EAPOL packet (AP only) + * @priv: private driver interface data + * @addr: Destination MAC address + * @data: EAPOL packet starting with IEEE 802.1X header + * @data_len: Length of the EAPOL packet in octets + * @encrypt: Whether the frame should be encrypted + * @own_addr: Source MAC address + * @flags: WPA_STA_* flags for the destination station + * + * Returns: 0 on success, -1 on failure + */ + int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, + const u8 *own_addr, u32 flags); + + /** + * sta_deauth - Deauthenticate a station (AP only) + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for the Deauthentication frame + * @addr: MAC address of the station to deauthenticate + * @reason: Reason code for the Deauthentiation frame + * Returns: 0 on success, -1 on failure + * + * This function requests a specific station to be deauthenticated and + * a Deauthentication frame to be sent to it. + */ + int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr, + int reason); + + /** + * sta_disassoc - Disassociate a station (AP only) + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for the Disassociation frame + * @addr: MAC address of the station to disassociate + * @reason: Reason code for the Disassociation frame + * Returns: 0 on success, -1 on failure + * + * This function requests a specific station to be disassociated and + * a Disassociation frame to be sent to it. + */ + int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr, + int reason); + + /** + * sta_remove - Remove a station entry (AP only) + * @priv: Private driver interface data + * @addr: MAC address of the station to be removed + * Returns: 0 on success, -1 on failure + */ + int (*sta_remove)(void *priv, const u8 *addr); + + /** + * hapd_get_ssid - Get the current SSID (AP only) + * @priv: Private driver interface data + * @buf: Buffer for returning the SSID + * @len: Maximum length of the buffer + * Returns: Length of the SSID on success, -1 on failure + * + * This function need not be implemented if the driver uses Beacon + * template from set_ap() and does not reply to Probe Request frames. + */ + int (*hapd_get_ssid)(void *priv, u8 *buf, int len); + + /** + * hapd_set_ssid - Set SSID (AP only) + * @priv: Private driver interface data + * @buf: SSID + * @len: Length of the SSID in octets + * Returns: 0 on success, -1 on failure + * + * DEPRECATED - use set_ap() instead + */ + int (*hapd_set_ssid)(void *priv, const u8 *buf, int len); + + /** + * hapd_set_countermeasures - Enable/disable TKIP countermeasures (AP) + * @priv: Private driver interface data + * @enabled: 1 = countermeasures enabled, 0 = disabled + * Returns: 0 on success, -1 on failure + * + * This need not be implemented if the driver does not take care of + * association processing. + */ + int (*hapd_set_countermeasures)(void *priv, int enabled); + + /** + * sta_add - Add a station entry + * @priv: Private driver interface data + * @params: Station parameters + * Returns: 0 on success, -1 on failure + * + * This function is used to add a station entry to the driver once the + * station has completed association. This is only used if the driver + * does not take care of association processing. + * + * With TDLS, this function is also used to add or set (params->set 1) + * TDLS peer entries. + */ + int (*sta_add)(void *priv, struct hostapd_sta_add_params *params); + + /** + * get_inact_sec - Get station inactivity duration (AP only) + * @priv: Private driver interface data + * @addr: Station address + * Returns: Number of seconds station has been inactive, -1 on failure + */ + int (*get_inact_sec)(void *priv, const u8 *addr); + + /** + * sta_clear_stats - Clear station statistics (AP only) + * @priv: Private driver interface data + * @addr: Station address + * Returns: 0 on success, -1 on failure + */ + int (*sta_clear_stats)(void *priv, const u8 *addr); + + /** + * set_freq - Set channel/frequency (AP only) + * @priv: Private driver interface data + * @freq: Channel parameters + * Returns: 0 on success, -1 on failure + */ + int (*set_freq)(void *priv, struct hostapd_freq_params *freq); + + /** + * set_rts - Set RTS threshold + * @priv: Private driver interface data + * @rts: RTS threshold in octets + * Returns: 0 on success, -1 on failure + */ + int (*set_rts)(void *priv, int rts); + + /** + * set_frag - Set fragmentation threshold + * @priv: Private driver interface data + * @frag: Fragmentation threshold in octets + * Returns: 0 on success, -1 on failure + */ + int (*set_frag)(void *priv, int frag); + + /** + * sta_set_flags - Set station flags (AP only) + * @priv: Private driver interface data + * @addr: Station address + * @total_flags: Bitmap of all WPA_STA_* flags currently set + * @flags_or: Bitmap of WPA_STA_* flags to add + * @flags_and: Bitmap of WPA_STA_* flags to us as a mask + * Returns: 0 on success, -1 on failure + */ + int (*sta_set_flags)(void *priv, const u8 *addr, + unsigned int total_flags, unsigned int flags_or, + unsigned int flags_and); + + /** + * set_tx_queue_params - Set TX queue parameters + * @priv: Private driver interface data + * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK) + * @aifs: AIFS + * @cw_min: cwMin + * @cw_max: cwMax + * @burst_time: Maximum length for bursting in 0.1 msec units + */ + int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min, + int cw_max, int burst_time); + + /** + * if_add - Add a virtual interface + * @priv: Private driver interface data + * @type: Interface type + * @ifname: Interface name for the new virtual interface + * @addr: Local address to use for the interface or %NULL to use the + * parent interface address + * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces + * @drv_priv: Pointer for overwriting the driver context or %NULL if + * not allowed (applies only to %WPA_IF_AP_BSS type) + * @force_ifname: Buffer for returning an interface name that the + * driver ended up using if it differs from the requested ifname + * @if_addr: Buffer for returning the allocated interface address + * (this may differ from the requested addr if the driver cannot + * change interface address) + * @bridge: Bridge interface to use or %NULL if no bridge configured + * @use_existing: Whether to allow existing interface to be used + * Returns: 0 on success, -1 on failure + */ + int (*if_add)(void *priv, enum wpa_driver_if_type type, + const char *ifname, const u8 *addr, void *bss_ctx, + void **drv_priv, char *force_ifname, u8 *if_addr, + const char *bridge, int use_existing); + + /** + * if_remove - Remove a virtual interface + * @priv: Private driver interface data + * @type: Interface type + * @ifname: Interface name of the virtual interface to be removed + * Returns: 0 on success, -1 on failure + */ + int (*if_remove)(void *priv, enum wpa_driver_if_type type, + const char *ifname); + + /** + * set_sta_vlan - Bind a station into a specific interface (AP only) + * @priv: Private driver interface data + * @ifname: Interface (main or virtual BSS or VLAN) + * @addr: MAC address of the associated station + * @vlan_id: VLAN ID + * Returns: 0 on success, -1 on failure + * + * This function is used to bind a station to a specific virtual + * interface. It is only used if when virtual interfaces are supported, + * e.g., to assign stations to different VLAN interfaces based on + * information from a RADIUS server. This allows separate broadcast + * domains to be used with a single BSS. + */ + int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname, + int vlan_id); + + /** + * commit - Optional commit changes handler (AP only) + * @priv: driver private data + * Returns: 0 on success, -1 on failure + * + * This optional handler function can be registered if the driver + * interface implementation needs to commit changes (e.g., by setting + * network interface up) at the end of initial configuration. If set, + * this handler will be called after initial setup has been completed. + */ + int (*commit)(void *priv); + + /** + * send_ether - Send an ethernet packet (AP only) + * @priv: private driver interface data + * @dst: Destination MAC address + * @src: Source MAC address + * @proto: Ethertype + * @data: EAPOL packet starting with IEEE 802.1X header + * @data_len: Length of the EAPOL packet in octets + * Returns: 0 on success, -1 on failure + */ + int (*send_ether)(void *priv, const u8 *dst, const u8 *src, u16 proto, + const u8 *data, size_t data_len); + + /** + * set_radius_acl_auth - Notification of RADIUS ACL change + * @priv: Private driver interface data + * @mac: MAC address of the station + * @accepted: Whether the station was accepted + * @session_timeout: Session timeout for the station + * Returns: 0 on success, -1 on failure + */ + int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, + u32 session_timeout); + + /** + * set_radius_acl_expire - Notification of RADIUS ACL expiration + * @priv: Private driver interface data + * @mac: MAC address of the station + * Returns: 0 on success, -1 on failure + */ + int (*set_radius_acl_expire)(void *priv, const u8 *mac); + + /** + * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP) + * @priv: Private driver interface data + * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s) + * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove + * extra IE(s) + * @assocresp: WPS IE(s) for (Re)Association Response frames or %NULL + * to remove extra IE(s) + * Returns: 0 on success, -1 on failure + * + * This is an optional function to add WPS IE in the kernel driver for + * Beacon and Probe Response frames. This can be left undefined (set + * to %NULL) if the driver uses the Beacon template from set_ap() + * and does not process Probe Request frames. If the driver takes care + * of (Re)Association frame processing, the assocresp buffer includes + * WPS IE(s) that need to be added to (Re)Association Response frames + * whenever a (Re)Association Request frame indicated use of WPS. + * + * This will also be used to add P2P IE(s) into Beacon/Probe Response + * frames when operating as a GO. The driver is responsible for adding + * timing related attributes (e.g., NoA) in addition to the IEs + * included here by appending them after these buffers. This call is + * also used to provide Probe Response IEs for P2P Listen state + * operations for drivers that generate the Probe Response frames + * internally. + * + * DEPRECATED - use set_ap() instead + */ + int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp); + + /** + * set_supp_port - Set IEEE 802.1X Supplicant Port status + * @priv: Private driver interface data + * @authorized: Whether the port is authorized + * Returns: 0 on success, -1 on failure + */ + int (*set_supp_port)(void *priv, int authorized); + + /** + * set_wds_sta - Bind a station into a 4-address WDS (AP only) + * @priv: Private driver interface data + * @addr: MAC address of the associated station + * @aid: Association ID + * @val: 1 = bind to 4-address WDS; 0 = unbind + * @bridge_ifname: Bridge interface to use for the WDS station or %NULL + * to indicate that bridge is not to be used + * @ifname_wds: Buffer to return the interface name for the new WDS + * station or %NULL to indicate name is not returned. + * Returns: 0 on success, -1 on failure + */ + int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val, + const char *bridge_ifname, char *ifname_wds); + + /** + * send_action - Transmit an Action frame + * @priv: Private driver interface data + * @freq: Frequency (in MHz) of the channel + * @wait: Time to wait off-channel for a response (in ms), or zero + * @dst: Destination MAC address (Address 1) + * @src: Source MAC address (Address 2) + * @bssid: BSSID (Address 3) + * @data: Frame body + * @data_len: data length in octets + @ @no_cck: Whether CCK rates must not be used to transmit this frame + * Returns: 0 on success, -1 on failure + * + * This command can be used to request the driver to transmit an action + * frame to the specified destination. + * + * If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will + * be transmitted on the given channel and the device will wait for a + * response on that channel for the given wait time. + * + * If the flag is not set, the wait time will be ignored. In this case, + * if a remain-on-channel duration is in progress, the frame must be + * transmitted on that channel; alternatively the frame may be sent on + * the current operational channel (if in associated state in station + * mode or while operating as an AP.) + */ + int (*send_action)(void *priv, unsigned int freq, unsigned int wait, + const u8 *dst, const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, int no_cck); + + /** + * send_action_cancel_wait - Cancel action frame TX wait + * @priv: Private driver interface data + * + * This command cancels the wait time associated with sending an action + * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is + * set in the driver flags. + */ + void (*send_action_cancel_wait)(void *priv); + + /** + * remain_on_channel - Remain awake on a channel + * @priv: Private driver interface data + * @freq: Frequency (in MHz) of the channel + * @duration: Duration in milliseconds + * Returns: 0 on success, -1 on failure + * + * This command is used to request the driver to remain awake on the + * specified channel for the specified duration and report received + * Action frames with EVENT_RX_MGMT events. Optionally, received + * Probe Request frames may also be requested to be reported by calling + * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ. + * + * The driver may not be at the requested channel when this function + * returns, i.e., the return code is only indicating whether the + * request was accepted. The caller will need to wait until the + * EVENT_REMAIN_ON_CHANNEL event indicates that the driver has + * completed the channel change. This may take some time due to other + * need for the radio and the caller should be prepared to timing out + * its wait since there are no guarantees on when this request can be + * executed. + */ + int (*remain_on_channel)(void *priv, unsigned int freq, + unsigned int duration); + + /** + * cancel_remain_on_channel - Cancel remain-on-channel operation + * @priv: Private driver interface data + * + * This command can be used to cancel a remain-on-channel operation + * before its originally requested duration has passed. This could be + * used, e.g., when remain_on_channel() is used to request extra time + * to receive a response to an Action frame and the response is + * received when there is still unneeded time remaining on the + * remain-on-channel operation. + */ + int (*cancel_remain_on_channel)(void *priv); + + /** + * probe_req_report - Request Probe Request frames to be indicated + * @priv: Private driver interface data + * @report: Whether to report received Probe Request frames + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This command can be used to request the driver to indicate when + * Probe Request frames are received with EVENT_RX_PROBE_REQ events. + * Since this operation may require extra resources, e.g., due to less + * optimal hardware/firmware RX filtering, many drivers may disable + * Probe Request reporting at least in station mode. This command is + * used to notify the driver when the Probe Request frames need to be + * reported, e.g., during remain-on-channel operations. + */ + int (*probe_req_report)(void *priv, int report); + + /** + * deinit_ap - Deinitialize AP mode + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This optional function can be used to disable AP mode related + * configuration. If the interface was not dynamically added, + * change the driver mode to station mode to allow normal station + * operations like scanning to be completed. + */ + int (*deinit_ap)(void *priv); + + /** + * deinit_p2p_cli - Deinitialize P2P client mode + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This optional function can be used to disable P2P client mode. If the + * interface was not dynamically added, change the interface type back + * to station mode. + */ + int (*deinit_p2p_cli)(void *priv); + + /** + * suspend - Notification on system suspend/hibernate event + * @priv: Private driver interface data + */ + void (*suspend)(void *priv); + + /** + * resume - Notification on system resume/thaw event + * @priv: Private driver interface data + */ + void (*resume)(void *priv); + + /** + * signal_monitor - Set signal monitoring parameters + * @priv: Private driver interface data + * @threshold: Threshold value for signal change events; 0 = disabled + * @hysteresis: Minimum change in signal strength before indicating a + * new event + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This function can be used to configure monitoring of signal strength + * with the current AP. Whenever signal strength drops below the + * %threshold value or increases above it, EVENT_SIGNAL_CHANGE event + * should be generated assuming the signal strength has changed at + * least %hysteresis from the previously indicated signal change event. + */ + int (*signal_monitor)(void *priv, int threshold, int hysteresis); + + /** + * send_frame - Send IEEE 802.11 frame (testing use only) + * @priv: Private driver interface data + * @data: IEEE 802.11 frame with IEEE 802.11 header + * @data_len: Size of the frame + * @encrypt: Whether to encrypt the frame (if keys are set) + * Returns: 0 on success, -1 on failure + * + * This function is only used for debugging purposes and is not + * required to be implemented for normal operations. + */ + int (*send_frame)(void *priv, const u8 *data, size_t data_len, + int encrypt); + + /** + * get_noa - Get current Notice of Absence attribute payload + * @priv: Private driver interface data + * @buf: Buffer for returning NoA + * @buf_len: Buffer length in octets + * Returns: Number of octets used in buf, 0 to indicate no NoA is being + * advertized, or -1 on failure + * + * This function is used to fetch the current Notice of Absence + * attribute value from GO. + */ + int (*get_noa)(void *priv, u8 *buf, size_t buf_len); + + /** + * set_noa - Set Notice of Absence parameters for GO (testing) + * @priv: Private driver interface data + * @count: Count + * @start: Start time in ms from next TBTT + * @duration: Duration in ms + * Returns: 0 on success or -1 on failure + * + * This function is used to set Notice of Absence parameters for GO. It + * is used only for testing. To disable NoA, all parameters are set to + * 0. + */ + int (*set_noa)(void *priv, u8 count, int start, int duration); + + /** + * set_p2p_powersave - Set P2P power save options + * @priv: Private driver interface data + * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change + * @opp_ps: 0 = disable, 1 = enable, -1 = no change + * @ctwindow: 0.. = change (msec), -1 = no change + * Returns: 0 on success or -1 on failure + */ + int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps, + int ctwindow); + + /** + * ampdu - Enable/disable aggregation + * @priv: Private driver interface data + * @ampdu: 1/0 = enable/disable A-MPDU aggregation + * Returns: 0 on success or -1 on failure + */ + int (*ampdu)(void *priv, int ampdu); + + /** + * get_radio_name - Get physical radio name for the device + * @priv: Private driver interface data + * Returns: Radio name or %NULL if not known + * + * The returned data must not be modified by the caller. It is assumed + * that any interface that has the same radio name as another is + * sharing the same physical radio. This information can be used to + * share scan results etc. information between the virtual interfaces + * to speed up various operations. + */ + const char * (*get_radio_name)(void *priv); + + /** + * send_tdls_mgmt - for sending TDLS management packets + * @priv: private driver interface data + * @dst: Destination (peer) MAC address + * @action_code: TDLS action code for the mssage + * @dialog_token: Dialog Token to use in the message (if needed) + * @status_code: Status Code or Reason Code to use (if needed) + * @peer_capab: TDLS peer capability (TDLS_PEER_* bitfield) + * @initiator: Is the current end the TDLS link initiator + * @buf: TDLS IEs to add to the message + * @len: Length of buf in octets + * Returns: 0 on success, negative (<0) on failure + * + * This optional function can be used to send packet to driver which is + * responsible for receiving and sending all TDLS packets. + */ + int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code, + u8 dialog_token, u16 status_code, u32 peer_capab, + int initiator, const u8 *buf, size_t len); + + /** + * tdls_oper - Ask the driver to perform high-level TDLS operations + * @priv: Private driver interface data + * @oper: TDLS high-level operation. See %enum tdls_oper + * @peer: Destination (peer) MAC address + * Returns: 0 on success, negative (<0) on failure + * + * This optional function can be used to send high-level TDLS commands + * to the driver. + */ + int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer); + + /** + * wnm_oper - Notify driver of the WNM frame reception + * @priv: Private driver interface data + * @oper: WNM operation. See %enum wnm_oper + * @peer: Destination (peer) MAC address + * @buf: Buffer for the driver to fill in (for getting IE) + * @buf_len: Return the len of buf + * Returns: 0 on success, negative (<0) on failure + */ + int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len); + + /** + * set_qos_map - Set QoS Map + * @priv: Private driver interface data + * @qos_map_set: QoS Map + * @qos_map_set_len: Length of QoS Map + */ + int (*set_qos_map)(void *priv, const u8 *qos_map_set, + u8 qos_map_set_len); + + /** + * br_add_ip_neigh - Add a neigh to the bridge ip neigh table + * @priv: Private driver interface data + * @version: IP version of the IP address, 4 or 6 + * @ipaddr: IP address for the neigh entry + * @prefixlen: IP address prefix length + * @addr: Corresponding MAC address + * Returns: 0 on success, negative (<0) on failure + */ + int (*br_add_ip_neigh)(void *priv, u8 version, const u8 *ipaddr, + int prefixlen, const u8 *addr); + + /** + * br_delete_ip_neigh - Remove a neigh from the bridge ip neigh table + * @priv: Private driver interface data + * @version: IP version of the IP address, 4 or 6 + * @ipaddr: IP address for the neigh entry + * Returns: 0 on success, negative (<0) on failure + */ + int (*br_delete_ip_neigh)(void *priv, u8 version, const u8 *ipaddr); + + /** + * br_port_set_attr - Set a bridge port attribute + * @attr: Bridge port attribute to set + * @val: Value to be set + * Returns: 0 on success, negative (<0) on failure + */ + int (*br_port_set_attr)(void *priv, enum drv_br_port_attr attr, + unsigned int val); + + /** + * br_port_set_attr - Set a bridge network parameter + * @param: Bridge parameter to set + * @val: Value to be set + * Returns: 0 on success, negative (<0) on failure + */ + int (*br_set_net_param)(void *priv, enum drv_br_net_param param, + unsigned int val); + + /** + * set_wowlan - Set wake-on-wireless triggers + * @priv: Private driver interface data + * @triggers: wowlan triggers + */ + int (*set_wowlan)(void *priv, const struct wowlan_triggers *triggers); + + /** + * signal_poll - Get current connection information + * @priv: Private driver interface data + * @signal_info: Connection info structure + */ + int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info); + + /** + * set_authmode - Set authentication algorithm(s) for static WEP + * @priv: Private driver interface data + * @authmode: 1=Open System, 2=Shared Key, 3=both + * Returns: 0 on success, -1 on failure + * + * This function can be used to set authentication algorithms for AP + * mode when static WEP is used. If the driver uses user space MLME/SME + * implementation, there is no need to implement this function. + * + * DEPRECATED - use set_ap() instead + */ + int (*set_authmode)(void *priv, int authmode); + +#ifdef ANDROID + /** + * driver_cmd - Execute driver-specific command + * @priv: Private driver interface data + * @cmd: Command to execute + * @buf: Return buffer + * @buf_len: Buffer length + * Returns: 0 on success, -1 on failure + */ + int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len); +#endif /* ANDROID */ + + /** + * vendor_cmd - Execute vendor specific command + * @priv: Private driver interface data + * @vendor_id: Vendor id + * @subcmd: Vendor command id + * @data: Vendor command parameters (%NULL if no parameters) + * @data_len: Data length + * @buf: Return buffer (%NULL to ignore reply) + * Returns: 0 on success, negative (<0) on failure + * + * This function handles vendor specific commands that are passed to + * the driver/device. The command is identified by vendor id and + * command id. Parameters can be passed as argument to the command + * in the data buffer. Reply (if any) will be filled in the supplied + * return buffer. + * + * The exact driver behavior is driver interface and vendor specific. As + * an example, this will be converted to a vendor specific cfg80211 + * command in case of the nl80211 driver interface. + */ + int (*vendor_cmd)(void *priv, unsigned int vendor_id, + unsigned int subcmd, const u8 *data, size_t data_len, + struct wpabuf *buf); + + /** + * set_rekey_info - Set rekey information + * @priv: Private driver interface data + * @kek: Current KEK + * @kek_len: KEK length in octets + * @kck: Current KCK + * @kck_len: KCK length in octets + * @replay_ctr: Current EAPOL-Key Replay Counter + * + * This optional function can be used to provide information for the + * driver/firmware to process EAPOL-Key frames in Group Key Handshake + * while the host (including wpa_supplicant) is sleeping. + */ + void (*set_rekey_info)(void *priv, const u8 *kek, size_t kek_len, + const u8 *kck, size_t kck_len, + const u8 *replay_ctr); + + /** + * sta_assoc - Station association indication + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for association frame + * @addr: MAC address of the station to associate + * @reassoc: flag to indicate re-association + * @status: association response status code + * @ie: assoc response ie buffer + * @len: ie buffer length + * Returns: 0 on success, -1 on failure + * + * This function indicates the driver to send (Re)Association + * Response frame to the station. + */ + int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr, + int reassoc, u16 status, const u8 *ie, size_t len); + + /** + * sta_auth - Station authentication indication + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for authentication frame + * @addr: MAC address of the station to associate + * @seq: authentication sequence number + * @status: authentication response status code + * @ie: authentication frame ie buffer + * @len: ie buffer length + * + * This function indicates the driver to send Authentication frame + * to the station. + */ + int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len); + + /** + * add_tspec - Add traffic stream + * @priv: Private driver interface data + * @addr: MAC address of the station to associate + * @tspec_ie: tspec ie buffer + * @tspec_ielen: tspec ie length + * Returns: 0 on success, -1 on failure + * + * This function adds the traffic steam for the station + * and fills the medium_time in tspec_ie. + */ + int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie, + size_t tspec_ielen); + + /** + * add_sta_node - Add a station node in the driver + * @priv: Private driver interface data + * @addr: MAC address of the station to add + * @auth_alg: authentication algorithm used by the station + * Returns: 0 on success, -1 on failure + * + * This function adds the station node in the driver, when + * the station gets added by FT-over-DS. + */ + int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg); + + /** + * sched_scan - Request the driver to initiate scheduled scan + * @priv: Private driver interface data + * @params: Scan parameters + * @interval: Interval between scan cycles in milliseconds + * Returns: 0 on success, -1 on failure + * + * This operation should be used for scheduled scan offload to + * the hardware. Every time scan results are available, the + * driver should report scan results event for wpa_supplicant + * which will eventually request the results with + * wpa_driver_get_scan_results2(). This operation is optional + * and if not provided or if it returns -1, we fall back to + * normal host-scheduled scans. + */ + int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params, + u32 interval); + + /** + * stop_sched_scan - Request the driver to stop a scheduled scan + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This should cause the scheduled scan to be stopped and + * results should stop being sent. Must be supported if + * sched_scan is supported. + */ + int (*stop_sched_scan)(void *priv); + + /** + * poll_client - Probe (null data or such) the given station + * @priv: Private driver interface data + * @own_addr: MAC address of sending interface + * @addr: MAC address of the station to probe + * @qos: Indicates whether station is QoS station + * + * This function is used to verify whether an associated station is + * still present. This function does not need to be implemented if the + * driver provides such inactivity polling mechanism. + */ + void (*poll_client)(void *priv, const u8 *own_addr, + const u8 *addr, int qos); + + /** + * radio_disable - Disable/enable radio + * @priv: Private driver interface data + * @disabled: 1=disable 0=enable radio + * Returns: 0 on success, -1 on failure + * + * This optional command is for testing purposes. It can be used to + * disable the radio on a testbed device to simulate out-of-radio-range + * conditions. + */ + int (*radio_disable)(void *priv, int disabled); + + /** + * switch_channel - Announce channel switch and migrate the GO to the + * given frequency + * @priv: Private driver interface data + * @settings: Settings for CSA period and new channel + * Returns: 0 on success, -1 on failure + * + * This function is used to move the GO to the legacy STA channel to + * avoid frequency conflict in single channel concurrency. + */ + int (*switch_channel)(void *priv, struct csa_settings *settings); + + /** + * add_tx_ts - Add traffic stream + * @priv: Private driver interface data + * @tsid: Traffic stream ID + * @addr: Receiver address + * @user_prio: User priority of the traffic stream + * @admitted_time: Admitted time for this TS in units of + * 32 microsecond periods (per second). + * Returns: 0 on success, -1 on failure + */ + int (*add_tx_ts)(void *priv, u8 tsid, const u8 *addr, u8 user_prio, + u16 admitted_time); + + /** + * del_tx_ts - Delete traffic stream + * @priv: Private driver interface data + * @tsid: Traffic stream ID + * @addr: Receiver address + * Returns: 0 on success, -1 on failure + */ + int (*del_tx_ts)(void *priv, u8 tsid, const u8 *addr); + + /** + * Enable channel-switching with TDLS peer + * @priv: Private driver interface data + * @addr: MAC address of the TDLS peer + * @oper_class: Operating class of the switch channel + * @params: Channel specification + * Returns: 0 on success, -1 on failure + * + * The function indicates to driver that it can start switching to a + * different channel with a specified TDLS peer. The switching is + * assumed on until canceled with tdls_disable_channel_switch(). + */ + int (*tdls_enable_channel_switch)( + void *priv, const u8 *addr, u8 oper_class, + const struct hostapd_freq_params *params); + + /** + * Disable channel switching with TDLS peer + * @priv: Private driver interface data + * @addr: MAC address of the TDLS peer + * Returns: 0 on success, -1 on failure + * + * This function indicates to the driver that it should stop switching + * with a given TDLS peer. + */ + int (*tdls_disable_channel_switch)(void *priv, const u8 *addr); + + /** + * start_dfs_cac - Listen for radar interference on the channel + * @priv: Private driver interface data + * @freq: Channel parameters + * Returns: 0 on success, -1 on failure + */ + int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq); + + /** + * stop_ap - Removes beacon from AP + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This optional function can be used to disable AP mode related + * configuration. Unlike deinit_ap, it does not change to station + * mode. + */ + int (*stop_ap)(void *priv); + + /** + * get_survey - Retrieve survey data + * @priv: Private driver interface data + * @freq: If set, survey data for the specified frequency is only + * being requested. If not set, all survey data is requested. + * Returns: 0 on success, -1 on failure + * + * Use this to retrieve: + * + * - the observed channel noise floor + * - the amount of time we have spent on the channel + * - the amount of time during which we have spent on the channel that + * the radio has determined the medium is busy and we cannot + * transmit + * - the amount of time we have spent receiving data + * - the amount of time we have spent transmitting data + * + * This data can be used for spectrum heuristics. One example is + * Automatic Channel Selection (ACS). The channel survey data is + * kept on a linked list on the channel data, one entry is added + * for each survey. The min_nf of the channel is updated for each + * survey. + */ + int (*get_survey)(void *priv, unsigned int freq); + + /** + * status - Get driver interface status information + * @priv: Private driver interface data + * @buf: Buffer for printing tou the status information + * @buflen: Maximum length of the buffer + * Returns: Length of written status information or -1 on failure + */ + int (*status)(void *priv, char *buf, size_t buflen); + + /** + * roaming - Set roaming policy for driver-based BSS selection + * @priv: Private driver interface data + * @allowed: Whether roaming within ESS is allowed + * @bssid: Forced BSSID if roaming is disabled or %NULL if not set + * Returns: Length of written status information or -1 on failure + * + * This optional callback can be used to update roaming policy from the + * associate() command (bssid being set there indicates that the driver + * should not roam before getting this roaming() call to allow roaming. + * If the driver does not indicate WPA_DRIVER_FLAGS_BSS_SELECTION + * capability, roaming policy is handled within wpa_supplicant and there + * is no need to implement or react to this callback. + */ + int (*roaming)(void *priv, int allowed, const u8 *bssid); + + /** + * set_mac_addr - Set MAC address + * @priv: Private driver interface data + * @addr: MAC address to use or %NULL for setting back to permanent + * Returns: 0 on success, -1 on failure + */ + int (*set_mac_addr)(void *priv, const u8 *addr); + +#ifdef CONFIG_MACSEC + int (*macsec_init)(void *priv, struct macsec_init_params *params); + + int (*macsec_deinit)(void *priv); + + /** + * enable_protect_frames - Set protect frames status + * @priv: Private driver interface data + * @enabled: TRUE = protect frames enabled + * FALSE = protect frames disabled + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*enable_protect_frames)(void *priv, Boolean enabled); + + /** + * set_replay_protect - Set replay protect status and window size + * @priv: Private driver interface data + * @enabled: TRUE = replay protect enabled + * FALSE = replay protect disabled + * @window: replay window size, valid only when replay protect enabled + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*set_replay_protect)(void *priv, Boolean enabled, u32 window); + + /** + * set_current_cipher_suite - Set current cipher suite + * @priv: Private driver interface data + * @cs: EUI64 identifier + * @cs_len: Length of the cs buffer in octets + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*set_current_cipher_suite)(void *priv, const u8 *cs, + size_t cs_len); + + /** + * enable_controlled_port - Set controlled port status + * @priv: Private driver interface data + * @enabled: TRUE = controlled port enabled + * FALSE = controlled port disabled + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*enable_controlled_port)(void *priv, Boolean enabled); + + /** + * get_receive_lowest_pn - Get receive lowest pn + * @priv: Private driver interface data + * @channel: secure channel + * @an: association number + * @lowest_pn: lowest accept pn + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*get_receive_lowest_pn)(void *priv, u32 channel, u8 an, + u32 *lowest_pn); + + /** + * get_transmit_next_pn - Get transmit next pn + * @priv: Private driver interface data + * @channel: secure channel + * @an: association number + * @next_pn: next pn + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*get_transmit_next_pn)(void *priv, u32 channel, u8 an, + u32 *next_pn); + + /** + * set_transmit_next_pn - Set transmit next pn + * @priv: Private driver interface data + * @channel: secure channel + * @an: association number + * @next_pn: next pn + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*set_transmit_next_pn)(void *priv, u32 channel, u8 an, + u32 next_pn); + + /** + * get_available_receive_sc - get available receive channel + * @priv: Private driver interface data + * @channel: secure channel + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*get_available_receive_sc)(void *priv, u32 *channel); + + /** + * create_receive_sc - create secure channel for receiving + * @priv: Private driver interface data + * @channel: secure channel + * @sci_addr: secure channel identifier - address + * @sci_port: secure channel identifier - port + * @conf_offset: confidentiality offset (0, 30, or 50) + * @validation: frame validation policy (0 = Disabled, 1 = Checked, + * 2 = Strict) + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*create_receive_sc)(void *priv, u32 channel, const u8 *sci_addr, + u16 sci_port, unsigned int conf_offset, + int validation); + + /** + * delete_receive_sc - delete secure connection for receiving + * @priv: private driver interface data from init() + * @channel: secure channel + * Returns: 0 on success, -1 on failure + */ + int (*delete_receive_sc)(void *priv, u32 channel); + + /** + * create_receive_sa - create secure association for receive + * @priv: private driver interface data from init() + * @channel: secure channel + * @an: association number + * @lowest_pn: the lowest packet number can be received + * @sak: the secure association key + * Returns: 0 on success, -1 on failure + */ + int (*create_receive_sa)(void *priv, u32 channel, u8 an, + u32 lowest_pn, const u8 *sak); + + /** + * enable_receive_sa - enable the SA for receive + * @priv: private driver interface data from init() + * @channel: secure channel + * @an: association number + * Returns: 0 on success, -1 on failure + */ + int (*enable_receive_sa)(void *priv, u32 channel, u8 an); + + /** + * disable_receive_sa - disable SA for receive + * @priv: private driver interface data from init() + * @channel: secure channel index + * @an: association number + * Returns: 0 on success, -1 on failure + */ + int (*disable_receive_sa)(void *priv, u32 channel, u8 an); + + /** + * get_available_transmit_sc - get available transmit channel + * @priv: Private driver interface data + * @channel: secure channel + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*get_available_transmit_sc)(void *priv, u32 *channel); + + /** + * create_transmit_sc - create secure connection for transmit + * @priv: private driver interface data from init() + * @channel: secure channel + * @sci_addr: secure channel identifier - address + * @sci_port: secure channel identifier - port + * Returns: 0 on success, -1 on failure + */ + int (*create_transmit_sc)(void *priv, u32 channel, const u8 *sci_addr, + u16 sci_port, unsigned int conf_offset); + + /** + * delete_transmit_sc - delete secure connection for transmit + * @priv: private driver interface data from init() + * @channel: secure channel + * Returns: 0 on success, -1 on failure + */ + int (*delete_transmit_sc)(void *priv, u32 channel); + + /** + * create_transmit_sa - create secure association for transmit + * @priv: private driver interface data from init() + * @channel: secure channel index + * @an: association number + * @next_pn: the packet number used as next transmit packet + * @confidentiality: True if the SA is to provide confidentiality + * as well as integrity + * @sak: the secure association key + * Returns: 0 on success, -1 on failure + */ + int (*create_transmit_sa)(void *priv, u32 channel, u8 an, u32 next_pn, + Boolean confidentiality, const u8 *sak); + + /** + * enable_transmit_sa - enable SA for transmit + * @priv: private driver interface data from init() + * @channel: secure channel + * @an: association number + * Returns: 0 on success, -1 on failure + */ + int (*enable_transmit_sa)(void *priv, u32 channel, u8 an); + + /** + * disable_transmit_sa - disable SA for transmit + * @priv: private driver interface data from init() + * @channel: secure channel + * @an: association number + * Returns: 0 on success, -1 on failure + */ + int (*disable_transmit_sa)(void *priv, u32 channel, u8 an); +#endif /* CONFIG_MACSEC */ + + /** + * init_mesh - Driver specific initialization for mesh + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + */ + int (*init_mesh)(void *priv); + + /** + * join_mesh - Join a mesh network + * @priv: Private driver interface data + * @params: Mesh configuration parameters + * Returns: 0 on success, -1 on failure + */ + int (*join_mesh)(void *priv, + struct wpa_driver_mesh_join_params *params); + + /** + * leave_mesh - Leave a mesh network + * @priv: Private driver interface data + * Returns 0 on success, -1 on failure + */ + int (*leave_mesh)(void *priv); + + /** + * do_acs - Automatically select channel + * @priv: Private driver interface data + * @params: Parameters for ACS + * Returns 0 on success, -1 on failure + * + * This command can be used to offload ACS to the driver if the driver + * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD). + */ + int (*do_acs)(void *priv, struct drv_acs_params *params); + + /** + * set_band - Notify driver of band selection + * @priv: Private driver interface data + * @band: The selected band(s) + * Returns 0 on success, -1 on failure + */ + int (*set_band)(void *priv, enum set_band band); + + /** + * get_pref_freq_list - Get preferred frequency list for an interface + * @priv: Private driver interface data + * @if_type: Interface type + * @num: Number of channels + * @freq_list: Preferred channel frequency list encoded in MHz values + * Returns 0 on success, -1 on failure + * + * This command can be used to query the preferred frequency list from + * the driver specific to a particular interface type. + */ + int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type, + unsigned int *num, unsigned int *freq_list); + + /** + * set_prob_oper_freq - Indicate probable P2P operating channel + * @priv: Private driver interface data + * @freq: Channel frequency in MHz + * Returns 0 on success, -1 on failure + * + * This command can be used to inform the driver of the operating + * frequency that an ongoing P2P group formation is likely to come up + * on. Local device is assuming P2P Client role. + */ + int (*set_prob_oper_freq)(void *priv, unsigned int freq); +}; + + +/** + * 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_WPS_BUTTON_PUSHED - Report hardware push button press for WPS + */ + EVENT_WPS_BUTTON_PUSHED, + + /** + * EVENT_TX_STATUS - Report TX status + */ + EVENT_TX_STATUS, + + /** + * EVENT_RX_FROM_UNKNOWN - Report RX from unknown STA + */ + EVENT_RX_FROM_UNKNOWN, + + /** + * EVENT_RX_MGMT - Report RX of a management frame + */ + EVENT_RX_MGMT, + + /** + * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started + * + * This event is used to indicate when the driver has started the + * requested remain-on-channel duration. Information about the + * operation is included in union wpa_event_data::remain_on_channel. + */ + EVENT_REMAIN_ON_CHANNEL, + + /** + * EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out + * + * This event is used to indicate when the driver has completed + * remain-on-channel duration, i.e., may noot be available on the + * requested channel anymore. Information about the + * operation is included in union wpa_event_data::remain_on_channel. + */ + EVENT_CANCEL_REMAIN_ON_CHANNEL, + + /** + * EVENT_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. + */ + 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. This event can be propagated when channel switching + * fails. + */ + EVENT_INTERFACE_UNAVAILABLE, + + /** + * EVENT_BEST_CHANNEL + * + * Driver generates this event whenever it detects a better channel + * (e.g., based on RSSI or channel use). This information can be used + * to improve channel selection for a new AP/P2P group. + */ + EVENT_BEST_CHANNEL, + + /** + * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received + * + * This event should be called when a Deauthentication frame is dropped + * due to it not being protected (MFP/IEEE 802.11w). + * union wpa_event_data::unprot_deauth is required to provide more + * details of the frame. + */ + EVENT_UNPROT_DEAUTH, + + /** + * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received + * + * This event should be called when a Disassociation frame is dropped + * due to it not being protected (MFP/IEEE 802.11w). + * union wpa_event_data::unprot_disassoc is required to provide more + * details of the frame. + */ + EVENT_UNPROT_DISASSOC, + + /** + * EVENT_STATION_LOW_ACK + * + * Driver generates this event whenever it detected that a particular + * station was lost. Detection can be through massive transmission + * failures for example. + */ + EVENT_STATION_LOW_ACK, + + /** + * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore + */ + EVENT_IBSS_PEER_LOST, + + /** + * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey + * + * This event carries the new replay counter to notify wpa_supplicant + * of the current EAPOL-Key Replay Counter in case the driver/firmware + * completed Group Key Handshake while the host (including + * wpa_supplicant was sleeping). + */ + EVENT_DRIVER_GTK_REKEY, + + /** + * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped + */ + EVENT_SCHED_SCAN_STOPPED, + + /** + * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll + * + * This event indicates that the station responded to the poll + * initiated with @poll_client. + */ + EVENT_DRIVER_CLIENT_POLL_OK, + + /** + * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status + */ + EVENT_EAPOL_TX_STATUS, + + /** + * EVENT_CH_SWITCH - AP or GO decided to switch channels + * + * Described in wpa_event_data.ch_switch + * */ + EVENT_CH_SWITCH, + + /** + * EVENT_WNM - Request WNM operation + * + * This event can be used to request a WNM operation to be performed. + */ + EVENT_WNM, + + /** + * EVENT_CONNECT_FAILED_REASON - Connection failure reason in AP mode + * + * This event indicates that the driver reported a connection failure + * with the specified client (for example, max client reached, etc.) in + * AP mode. + */ + EVENT_CONNECT_FAILED_REASON, + + /** + * EVENT_DFS_RADAR_DETECTED - Notify of radar detection + * + * A radar has been detected on the supplied frequency, hostapd should + * react accordingly (e.g., change channel). + */ + EVENT_DFS_RADAR_DETECTED, + + /** + * EVENT_DFS_CAC_FINISHED - Notify that channel availability check has been completed + * + * After a successful CAC, the channel can be marked clear and used. + */ + EVENT_DFS_CAC_FINISHED, + + /** + * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted + * + * The CAC was not successful, and the channel remains in the previous + * state. This may happen due to a radar beeing detected or other + * external influences. + */ + EVENT_DFS_CAC_ABORTED, + + /** + * EVENT_DFS_NOP_FINISHED - Notify that non-occupancy period is over + * + * The channel which was previously unavailable is now available again. + */ + EVENT_DFS_NOP_FINISHED, + + /** + * EVENT_SURVEY - Received survey data + * + * This event gets triggered when a driver query is issued for survey + * data and the requested data becomes available. The returned data is + * stored in struct survey_results. The results provide at most one + * survey entry for each frequency and at minimum will provide one + * survey entry for one frequency. The survey data can be os_malloc()'d + * and then os_free()'d, so the event callback must only copy data. + */ + EVENT_SURVEY, + + /** + * EVENT_SCAN_STARTED - Scan started + * + * This indicates that driver has started a scan operation either based + * on a request from wpa_supplicant/hostapd or from another application. + * EVENT_SCAN_RESULTS is used to indicate when the scan has been + * completed (either successfully or by getting cancelled). + */ + EVENT_SCAN_STARTED, + + /** + * EVENT_AVOID_FREQUENCIES - Received avoid frequency range + * + * This event indicates a set of frequency ranges that should be avoided + * to reduce issues due to interference or internal co-existence + * information in the driver. + */ + EVENT_AVOID_FREQUENCIES, + + /** + * EVENT_NEW_PEER_CANDIDATE - new (unknown) mesh peer notification + */ + EVENT_NEW_PEER_CANDIDATE, + + /** + * EVENT_ACS_CHANNEL_SELECTED - Received selected channels by ACS + * + * Indicates a pair of primary and secondary channels chosen by ACS + * in device. + */ + EVENT_ACS_CHANNEL_SELECTED, + + /** + * EVENT_DFS_CAC_STARTED - Notify that channel availability check has + * been started. + * + * This event indicates that channel availability check has been started + * on a DFS frequency by a driver that supports DFS Offload. + */ + EVENT_DFS_CAC_STARTED, +}; + + +/** + * struct freq_survey - Channel survey info + * + * @ifidx: Interface index in which this survey was observed + * @freq: Center of frequency of the surveyed channel + * @nf: Channel noise floor in dBm + * @channel_time: Amount of time in ms the radio spent on the channel + * @channel_time_busy: Amount of time in ms the radio detected some signal + * that indicated to the radio the channel was not clear + * @channel_time_rx: Amount of time the radio spent receiving data + * @channel_time_tx: Amount of time the radio spent transmitting data + * @filled: bitmask indicating which fields have been reported, see + * SURVEY_HAS_* defines. + * @list: Internal list pointers + */ +struct freq_survey { + u32 ifidx; + unsigned int freq; + s8 nf; + u64 channel_time; + u64 channel_time_busy; + u64 channel_time_rx; + u64 channel_time_tx; + unsigned int filled; + struct dl_list list; +}; + +#define SURVEY_HAS_NF BIT(0) +#define SURVEY_HAS_CHAN_TIME BIT(1) +#define SURVEY_HAS_CHAN_TIME_BUSY BIT(2) +#define SURVEY_HAS_CHAN_TIME_RX BIT(3) +#define SURVEY_HAS_CHAN_TIME_TX BIT(4) + + +/** + * union wpa_event_data - Additional data for wpa_supplicant_event() calls + */ +union wpa_event_data { + /** + * struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events + * + * This structure is optional for EVENT_ASSOC calls and required for + * EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the + * driver interface does not need to generate separate EVENT_ASSOCINFO + * calls. + */ + struct assoc_info { + /** + * reassoc - Flag to indicate association or reassociation + */ + int reassoc; + + /** + * req_ies - (Re)Association Request IEs + * + * If the driver generates WPA/RSN IE, this event data must be + * returned for WPA handshake to have needed information. If + * wpa_supplicant-generated WPA/RSN IE is used, this + * information event is optional. + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + const u8 *req_ies; + + /** + * req_ies_len - Length of req_ies in bytes + */ + size_t req_ies_len; + + /** + * resp_ies - (Re)Association Response IEs + * + * Optional association data from the driver. This data is not + * required WPA, but may be useful for some protocols and as + * such, should be reported if this is available to the driver + * interface. + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + const u8 *resp_ies; + + /** + * resp_ies_len - Length of resp_ies in bytes + */ + size_t resp_ies_len; + + /** + * beacon_ies - Beacon or Probe Response IEs + * + * Optional Beacon/ProbeResp data: IEs included in Beacon or + * Probe Response frames from the current AP (i.e., the one + * that the client just associated with). This information is + * used to update WPA/RSN IE for the AP. If this field is not + * set, the results from previous scan will be used. If no + * data for the new AP is found, scan results will be requested + * again (without scan request). At this point, the driver is + * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is + * used). + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + const u8 *beacon_ies; + + /** + * beacon_ies_len - Length of beacon_ies */ + size_t beacon_ies_len; + + /** + * freq - Frequency of the operational channel in MHz + */ + unsigned int freq; + + /** + * wmm_params - WMM parameters used in this association. + */ + struct wmm_params wmm_params; + + /** + * addr - Station address (for AP mode) + */ + const u8 *addr; + + /** + * The following is the key management offload information + * @authorized + * @key_replay_ctr + * @key_replay_ctr_len + * @ptk_kck + * @ptk_kek_len + * @ptk_kek + * @ptk_kek_len + */ + + /** + * authorized - Status of key management offload, + * 1 = successful + */ + int authorized; + + /** + * key_replay_ctr - Key replay counter value last used + * in a valid EAPOL-Key frame + */ + const u8 *key_replay_ctr; + + /** + * key_replay_ctr_len - The length of key_replay_ctr + */ + size_t key_replay_ctr_len; + + /** + * ptk_kck - The derived PTK KCK + */ + const u8 *ptk_kck; + + /** + * ptk_kek_len - The length of ptk_kck + */ + size_t ptk_kck_len; + + /** + * ptk_kek - The derived PTK KEK + */ + const u8 *ptk_kek; + + /** + * ptk_kek_len - The length of ptk_kek + */ + size_t ptk_kek_len; + } assoc_info; + + /** + * struct disassoc_info - Data for EVENT_DISASSOC events + */ + struct disassoc_info { + /** + * addr - Station address (for AP mode) + */ + const u8 *addr; + + /** + * reason_code - Reason Code (host byte order) used in + * Deauthentication frame + */ + u16 reason_code; + + /** + * ie - Optional IE(s) in Disassociation frame + */ + const u8 *ie; + + /** + * ie_len - Length of ie buffer in octets + */ + size_t ie_len; + + /** + * locally_generated - Whether the frame was locally generated + */ + int locally_generated; + } disassoc_info; + + /** + * struct deauth_info - Data for EVENT_DEAUTH events + */ + struct deauth_info { + /** + * addr - Station address (for AP mode) + */ + const u8 *addr; + + /** + * reason_code - Reason Code (host byte order) used in + * Deauthentication frame + */ + u16 reason_code; + + /** + * ie - Optional IE(s) in Deauthentication frame + */ + const u8 *ie; + + /** + * ie_len - Length of ie buffer in octets + */ + size_t ie_len; + + /** + * locally_generated - Whether the frame was locally generated + */ + int locally_generated; + } deauth_info; + + /** + * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE + */ + struct michael_mic_failure { + int unicast; + const u8 *src; + } michael_mic_failure; + + /** + * struct interface_status - Data for EVENT_INTERFACE_STATUS + */ + struct interface_status { + char ifname[100]; + enum { + EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED + } ievent; + } interface_status; + + /** + * struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE + */ + struct pmkid_candidate { + /** BSSID of the PMKID candidate */ + u8 bssid[ETH_ALEN]; + /** Smaller the index, higher the priority */ + int index; + /** Whether RSN IE includes pre-authenticate flag */ + int preauth; + } pmkid_candidate; + + /** + * struct stkstart - Data for EVENT_STKSTART + */ + struct stkstart { + u8 peer[ETH_ALEN]; + } stkstart; + + /** + * struct tdls - Data for EVENT_TDLS + */ + struct tdls { + u8 peer[ETH_ALEN]; + enum { + TDLS_REQUEST_SETUP, + TDLS_REQUEST_TEARDOWN, + TDLS_REQUEST_DISCOVER, + } oper; + u16 reason_code; /* for teardown */ + } tdls; + + /** + * struct wnm - Data for EVENT_WNM + */ + struct wnm { + u8 addr[ETH_ALEN]; + enum { + WNM_OPER_SLEEP, + } oper; + enum { + WNM_SLEEP_ENTER, + WNM_SLEEP_EXIT + } sleep_action; + int sleep_intval; + u16 reason_code; + u8 *buf; + u16 buf_len; + } wnm; + + /** + * struct ft_ies - FT information elements (EVENT_FT_RESPONSE) + * + * During FT (IEEE 802.11r) authentication sequence, the driver is + * expected to use this event to report received FT IEs (MDIE, FTIE, + * RSN IE, TIE, possible resource request) to the supplicant. The FT + * IEs for the next message will be delivered through the + * struct wpa_driver_ops::update_ft_ies() callback. + */ + struct ft_ies { + const u8 *ies; + size_t ies_len; + int ft_action; + u8 target_ap[ETH_ALEN]; + /** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */ + const u8 *ric_ies; + /** Length of ric_ies buffer in octets */ + size_t ric_ies_len; + } ft_ies; + + /** + * struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START + */ + struct ibss_rsn_start { + u8 peer[ETH_ALEN]; + } ibss_rsn_start; + + /** + * struct auth_info - Data for EVENT_AUTH events + */ + struct auth_info { + u8 peer[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u16 auth_type; + u16 auth_transaction; + u16 status_code; + const u8 *ies; + size_t ies_len; + } auth; + + /** + * struct assoc_reject - Data for EVENT_ASSOC_REJECT events + */ + struct assoc_reject { + /** + * bssid - BSSID of the AP that rejected association + */ + const u8 *bssid; + + /** + * resp_ies - (Re)Association Response IEs + * + * Optional association data from the driver. This data is not + * required WPA, but may be useful for some protocols and as + * such, should be reported if this is available to the driver + * interface. + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + const u8 *resp_ies; + + /** + * resp_ies_len - Length of resp_ies in bytes + */ + size_t resp_ies_len; + + /** + * status_code - Status Code from (Re)association Response + */ + u16 status_code; + } assoc_reject; + + struct timeout_event { + u8 addr[ETH_ALEN]; + } timeout_event; + + /** + * struct tx_status - Data for EVENT_TX_STATUS events + */ + struct tx_status { + u16 type; + u16 stype; + const u8 *dst; + const u8 *data; + size_t data_len; + int ack; + } tx_status; + + /** + * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events + */ + struct rx_from_unknown { + const u8 *bssid; + const u8 *addr; + int wds; + } rx_from_unknown; + + /** + * struct rx_mgmt - Data for EVENT_RX_MGMT events + */ + struct rx_mgmt { + const u8 *frame; + size_t frame_len; + u32 datarate; + + /** + * drv_priv - Pointer to store driver private BSS information + * + * If not set to NULL, this is used for comparison with + * hostapd_data->drv_priv to determine which BSS should process + * the frame. + */ + void *drv_priv; + + /** + * freq - Frequency (in MHz) on which the frame was received + */ + int freq; + + /** + * ssi_signal - Signal strength in dBm (or 0 if not available) + */ + int ssi_signal; + } rx_mgmt; + + /** + * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events + * + * This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events. + */ + struct remain_on_channel { + /** + * freq - Channel frequency in MHz + */ + unsigned int freq; + + /** + * duration - Duration to remain on the channel in milliseconds + */ + unsigned int duration; + } remain_on_channel; + + /** + * struct scan_info - Optional data for EVENT_SCAN_RESULTS events + * @aborted: Whether the scan was aborted + * @freqs: Scanned frequencies in MHz (%NULL = all channels scanned) + * @num_freqs: Number of entries in freqs array + * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard + * SSID) + * @num_ssids: Number of entries in ssids array + */ + struct scan_info { + int aborted; + const int *freqs; + size_t num_freqs; + struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; + size_t num_ssids; + } scan_info; + + /** + * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events + */ + struct rx_probe_req { + /** + * sa - Source address of the received Probe Request frame + */ + const u8 *sa; + + /** + * da - Destination address of the received Probe Request frame + * or %NULL if not available + */ + const u8 *da; + + /** + * bssid - BSSID of the received Probe Request frame or %NULL + * if not available + */ + const u8 *bssid; + + /** + * ie - IEs from the Probe Request body + */ + const u8 *ie; + + /** + * ie_len - Length of ie buffer in octets + */ + size_t ie_len; + + /** + * signal - signal strength in dBm (or 0 if not available) + */ + int ssi_signal; + } rx_probe_req; + + /** + * struct new_sta - Data for EVENT_NEW_STA events + */ + struct new_sta { + const u8 *addr; + } new_sta; + + /** + * struct eapol_rx - Data for EVENT_EAPOL_RX events + */ + struct eapol_rx { + const u8 *src; + const u8 *data; + size_t data_len; + } eapol_rx; + + /** + * signal_change - Data for EVENT_SIGNAL_CHANGE events + */ + struct wpa_signal_info signal_change; + + /** + * struct best_channel - Data for EVENT_BEST_CHANNEL events + * @freq_24: Best 2.4 GHz band channel frequency in MHz + * @freq_5: Best 5 GHz band channel frequency in MHz + * @freq_overall: Best channel frequency in MHz + * + * 0 can be used to indicate no preference in either band. + */ + struct best_channel { + int freq_24; + int freq_5; + int freq_overall; + } best_chan; + + struct unprot_deauth { + const u8 *sa; + const u8 *da; + u16 reason_code; + } unprot_deauth; + + struct unprot_disassoc { + const u8 *sa; + const u8 *da; + u16 reason_code; + } unprot_disassoc; + + /** + * struct low_ack - Data for EVENT_STATION_LOW_ACK events + * @addr: station address + */ + struct low_ack { + u8 addr[ETH_ALEN]; + } low_ack; + + /** + * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST + */ + struct ibss_peer_lost { + u8 peer[ETH_ALEN]; + } ibss_peer_lost; + + /** + * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY + */ + struct driver_gtk_rekey { + const u8 *bssid; + const u8 *replay_ctr; + } driver_gtk_rekey; + + /** + * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events + * @addr: station address + */ + struct client_poll { + u8 addr[ETH_ALEN]; + } client_poll; + + /** + * struct eapol_tx_status + * @dst: Original destination + * @data: Data starting with IEEE 802.1X header (!) + * @data_len: Length of data + * @ack: Indicates ack or lost frame + * + * This corresponds to hapd_send_eapol if the frame sent + * there isn't just reported as EVENT_TX_STATUS. + */ + struct eapol_tx_status { + const u8 *dst; + const u8 *data; + int data_len; + int ack; + } eapol_tx_status; + + /** + * struct ch_switch + * @freq: Frequency of new channel in MHz + * @ht_enabled: Whether this is an HT channel + * @ch_offset: Secondary channel offset + * @ch_width: Channel width + * @cf1: Center frequency 1 + * @cf2: Center frequency 2 + */ + struct ch_switch { + int freq; + int ht_enabled; + int ch_offset; + enum chan_width ch_width; + int cf1; + int cf2; + } ch_switch; + + /** + * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON + * @addr: Remote client address + * @code: Reason code for connection failure + */ + struct connect_failed_reason { + u8 addr[ETH_ALEN]; + enum { + MAX_CLIENT_REACHED, + BLOCKED_CLIENT + } code; + } connect_failed_reason; + + /** + * struct dfs_event - Data for radar detected events + * @freq: Frequency of the channel in MHz + */ + struct dfs_event { + int freq; + int ht_enabled; + int chan_offset; + enum chan_width chan_width; + int cf1; + int cf2; + } dfs_event; + + /** + * survey_results - Survey result data for EVENT_SURVEY + * @freq_filter: Requested frequency survey filter, 0 if request + * was for all survey data + * @survey_list: Linked list of survey data (struct freq_survey) + */ + struct survey_results { + unsigned int freq_filter; + struct dl_list survey_list; /* struct freq_survey */ + } survey_results; + + /** + * channel_list_changed - Data for EVENT_CHANNEL_LIST_CHANGED + * @initiator: Initiator of the regulatory change + * @type: Regulatory change type + * @alpha2: Country code (or "" if not available) + */ + struct channel_list_changed { + enum reg_change_initiator initiator; + enum reg_type type; + char alpha2[3]; + } channel_list_changed; + + /** + * freq_range - List of frequency ranges + * + * This is used as the data with EVENT_AVOID_FREQUENCIES. + */ + struct wpa_freq_range_list freq_range; + + /** + * struct mesh_peer + * + * @peer: Peer address + * @ies: Beacon IEs + * @ie_len: Length of @ies + * + * Notification of new candidate mesh peer. + */ + struct mesh_peer { + const u8 *peer; + const u8 *ies; + size_t ie_len; + } mesh_peer; + + /** + * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED + * @pri_channel: Selected primary channel + * @sec_channel: Selected secondary channel + * @vht_seg0_center_ch: VHT mode Segment0 center channel + * @vht_seg1_center_ch: VHT mode Segment1 center channel + * @ch_width: Selected Channel width by driver. Driver may choose to + * change hostapd configured ACS channel width due driver internal + * channel restrictions. + * hw_mode: Selected band (used with hw_mode=any) + */ + struct acs_selected_channels { + u8 pri_channel; + u8 sec_channel; + u8 vht_seg0_center_ch; + u8 vht_seg1_center_ch; + u16 ch_width; + enum hostapd_hw_mode hw_mode; + } acs_selected_channels; +}; + +/** + * wpa_supplicant_event - Report a driver event for wpa_supplicant + * @ctx: Context pointer (wpa_s); this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @event: event type (defined above) + * @data: possible extra data for the event + * + * Driver wrapper code should call this function whenever an event is received + * from the driver. + */ +void wpa_supplicant_event(void *ctx, enum wpa_event_type event, + union wpa_event_data *data); + + +/* + * The following inline functions are provided for convenience to simplify + * event indication for some of the common events. + */ + +static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie, + size_t ielen, int reassoc) +{ + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.assoc_info.reassoc = reassoc; + event.assoc_info.req_ies = ie; + event.assoc_info.req_ies_len = ielen; + event.assoc_info.addr = addr; + wpa_supplicant_event(ctx, EVENT_ASSOC, &event); +} + +static inline void drv_event_disassoc(void *ctx, const u8 *addr) +{ + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.disassoc_info.addr = addr; + wpa_supplicant_event(ctx, EVENT_DISASSOC, &event); +} + +static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data, + size_t data_len) +{ + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.eapol_rx.src = src; + event.eapol_rx.data = data; + event.eapol_rx.data_len = data_len; + wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event); +} + +/* driver_common.c */ +void wpa_scan_results_free(struct wpa_scan_results *res); + +/* Convert wpa_event_type to a string for logging */ +const char * event_to_string(enum wpa_event_type event); + +/* Convert chan_width to a string for logging and control interfaces */ +const char * channel_width_to_string(enum chan_width width); + +int ht_supported(const struct hostapd_hw_modes *mode); +int vht_supported(const struct hostapd_hw_modes *mode); + +struct wowlan_triggers * +wpa_get_wowlan_triggers(const char *wowlan_triggers, + const struct wpa_driver_capa *capa); + +/* NULL terminated array of linked in driver wrappers */ +extern const struct wpa_driver_ops *const wpa_drivers[]; + +#endif /* DRIVER_H */ diff --git a/freebsd/contrib/wpa/src/drivers/driver_bsd.c b/freebsd/contrib/wpa/src/drivers/driver_bsd.c new file mode 100644 index 00000000..5f6e129d --- /dev/null +++ b/freebsd/contrib/wpa/src/drivers/driver_bsd.c @@ -0,0 +1,1675 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * WPA Supplicant - driver interaction with BSD net80211 layer + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * Copyright (c) 2004, 2Wire, Inc + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <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 */ + enum ieee80211_opmode opmode; /* operation mode */ + char *event_buf; + size_t event_buf_len; +}; + +/* 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) { + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", + strerror(errno)); + return -1; + } + + if (enable) { + if (ifr.ifr_flags & IFF_UP) + return 0; + ifr.ifr_flags |= IFF_UP; + } else { + if (!(ifr.ifr_flags & IFF_UP)) + return 0; + ifr.ifr_flags &= ~IFF_UP; + } + + if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", + strerror(errno)); + return -1; + } + + return 0; +} + +static int +bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, + const unsigned char *addr, int key_idx, int set_tx, const u8 *seq, + size_t seq_len, const u8 *key, size_t key_len) +{ + struct ieee80211req_key wk; +#ifdef IEEE80211_KEY_NOREPLAY + struct bsd_driver_data *drv = priv; +#endif /* IEEE80211_KEY_NOREPLAY */ + + wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d " + "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx, + set_tx, seq_len, key_len); + + if (alg == WPA_ALG_NONE) { +#ifndef HOSTAPD + if (addr == NULL || is_broadcast_ether_addr(addr)) + return bsd_del_key(priv, NULL, key_idx); + else +#endif /* HOSTAPD */ + return bsd_del_key(priv, addr, key_idx); + } + + os_memset(&wk, 0, sizeof(wk)); + switch (alg) { + case WPA_ALG_WEP: + wk.ik_type = IEEE80211_CIPHER_WEP; + break; + case WPA_ALG_TKIP: + wk.ik_type = IEEE80211_CIPHER_TKIP; + break; + case WPA_ALG_CCMP: + wk.ik_type = IEEE80211_CIPHER_AES_CCM; + break; + default: + wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg); + return -1; + } + + wk.ik_flags = IEEE80211_KEY_RECV; + if (set_tx) + wk.ik_flags |= IEEE80211_KEY_XMIT; + + if (addr == NULL) { + os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + } else { + os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + /* + * Deduce whether group/global or unicast key by checking + * the address (yech). Note also that we can only mark global + * keys default; doing this for a unicast key is an error. + */ + if (is_broadcast_ether_addr(addr)) { + wk.ik_flags |= IEEE80211_KEY_GROUP; + wk.ik_keyix = key_idx; + } else { + wk.ik_keyix = key_idx == 0 ? IEEE80211_KEYIX_NONE : + key_idx; + } + } + if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx) + wk.ik_flags |= IEEE80211_KEY_DEFAULT; +#ifndef HOSTAPD +#ifdef IEEE80211_KEY_NOREPLAY + /* + * Ignore replay failures in IBSS and AHDEMO mode. + */ + if (drv->opmode == IEEE80211_M_IBSS || + drv->opmode == IEEE80211_M_AHDEMO) + wk.ik_flags |= IEEE80211_KEY_NOREPLAY; +#endif /* IEEE80211_KEY_NOREPLAY */ +#endif /* HOSTAPD */ + wk.ik_keylen = key_len; + if (seq) { +#ifdef WORDS_BIGENDIAN + /* + * wk.ik_keyrsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 *keyrsc = (u8 *) &wk.ik_keyrsc; + for (i = 0; i < seq_len; i++) + keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i]; +#else /* WORDS_BIGENDIAN */ + os_memcpy(&wk.ik_keyrsc, seq, seq_len); +#endif /* WORDS_BIGENDIAN */ + } + os_memcpy(wk.ik_keydata, key, key_len); + + return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); +} + +static int +bsd_configure_wpa(void *priv, struct wpa_bss_params *params) +{ +#ifndef IEEE80211_IOC_APPIE + static const char *ciphernames[] = + { "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" }; + int v; + + switch (params->wpa_group) { + case WPA_CIPHER_CCMP: + v = IEEE80211_CIPHER_AES_CCM; + break; + case WPA_CIPHER_TKIP: + v = IEEE80211_CIPHER_TKIP; + break; + case WPA_CIPHER_WEP104: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_WEP40: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_NONE: + v = IEEE80211_CIPHER_NONE; + break; + default: + wpa_printf(MSG_INFO, "Unknown group key cipher %u", + 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)) { + wpa_printf(MSG_INFO, + "Unable to set group key cipher to %u (%s)", + 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)) { + wpa_printf(MSG_INFO, + "Unable to set group key length to %u", 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)) { + wpa_printf(MSG_INFO, + "Unable to set pairwise key ciphers to 0x%x", 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)) { + wpa_printf(MSG_INFO, + "Unable to set key management algorithms to 0x%x", + 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)) { + wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x", + 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)) { + wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa); + return -1; + } + return 0; +} + +static int +bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); + + if (!params->enabled) { + /* XXX restore state */ + return set80211param(priv, IEEE80211_IOC_AUTHMODE, + IEEE80211_AUTH_AUTO); + } + if (!params->wpa && !params->ieee802_1x) { + wpa_printf(MSG_ERROR, "%s: No 802.1X or WPA enabled", + __func__); + return -1; + } + if (params->wpa && bsd_configure_wpa(priv, params) != 0) { + wpa_printf(MSG_ERROR, "%s: Failed to configure WPA state", + __func__); + return -1; + } + if (set80211param(priv, IEEE80211_IOC_AUTHMODE, + (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + wpa_printf(MSG_ERROR, "%s: Failed to enable WPA/802.1X", + __func__); + return -1; + } + return bsd_ctrl_iface(priv, 1); +} + +static void +bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct ieee80211req_wpaie ie; + int ielen = 0; + u8 *iebuf = NULL; + + /* + * Fetch and validate any negotiated WPA/RSN parameters. + */ + memset(&ie, 0, sizeof(ie)); + memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); + if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) { + wpa_printf(MSG_INFO, + "Failed to get WPA/RSN information element"); + goto no_ie; + } + iebuf = ie.wpa_ie; + ielen = ie.wpa_ie[1]; + if (ielen == 0) + iebuf = NULL; + else + ielen += 2; + +no_ie: + drv_event_assoc(ctx, addr, iebuf, ielen, 0); +} + +static int +bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr, u32 flags) +{ + struct bsd_driver_data *drv = priv; + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", data, data_len); + + return l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, data, + data_len); +} + +static int +bsd_set_freq(void *priv, struct hostapd_freq_params *freq) +{ + struct bsd_driver_data *drv = priv; +#ifdef SIOCS80211CHANNEL + struct ieee80211chanreq creq; +#endif /* SIOCS80211CHANNEL */ + u32 mode; + int channel = freq->channel; + + if (channel < 14) { + mode = +#ifdef CONFIG_IEEE80211N + freq->ht_enabled ? IFM_IEEE80211_11NG : +#endif /* CONFIG_IEEE80211N */ + IFM_IEEE80211_11G; + } else if (channel == 14) { + mode = IFM_IEEE80211_11B; + } else { + mode = +#ifdef CONFIG_IEEE80211N + freq->ht_enabled ? IFM_IEEE80211_11NA : +#endif /* CONFIG_IEEE80211N */ + IFM_IEEE80211_11A; + } + if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to set modulation mode", + __func__); + return -1; + } + +#ifdef SIOCS80211CHANNEL + os_memset(&creq, 0, sizeof(creq)); + os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name)); + creq.i_channel = (u_int16_t)channel; + return ioctl(drv->sock, SIOCS80211CHANNEL, &creq); +#else /* SIOCS80211CHANNEL */ + return set80211param(priv, IEEE80211_IOC_CHANNEL, channel); +#endif /* SIOCS80211CHANNEL */ +} + +static int +bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) +{ +#ifdef IEEE80211_IOC_APPIE + wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %lu)", __func__, + (unsigned long)ie_len); + return bsd_set80211(priv, IEEE80211_IOC_APPIE, IEEE80211_APPIE_WPA, + ie, ie_len); +#endif /* IEEE80211_IOC_APPIE */ + return 0; +} + +static size_t +rtbuf_len(void) +{ + size_t len; + + int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0}; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { + wpa_printf(MSG_WARNING, "%s failed: %s", __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) { + wpa_printf(MSG_INFO, "Failed to get encryption"); + 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; + struct if_announcemsghdr *ifan; + struct rt_msghdr *rtm; + struct ieee80211_michael_event *mic; + struct ieee80211_join_event *join; + struct ieee80211_leave_event *leave; + int n; + union wpa_event_data data; + + n = read(sock, drv->event_buf, drv->event_buf_len); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + wpa_printf(MSG_ERROR, "%s read() failed: %s", + __func__, strerror(errno)); + return; + } + + rtm = (struct rt_msghdr *) drv->event_buf; + if (rtm->rtm_version != RTM_VERSION) { + wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", + rtm->rtm_version); + return; + } + ifan = (struct if_announcemsghdr *) rtm; + switch (rtm->rtm_type) { + case RTM_IEEE80211: + switch (ifan->ifan_what) { + case RTM_IEEE80211_ASSOC: + case RTM_IEEE80211_REASSOC: + case RTM_IEEE80211_DISASSOC: + case RTM_IEEE80211_SCAN: + break; + case RTM_IEEE80211_LEAVE: + leave = (struct ieee80211_leave_event *) &ifan[1]; + drv_event_disassoc(drv->hapd, leave->iev_addr); + break; + case RTM_IEEE80211_JOIN: +#ifdef RTM_IEEE80211_REJOIN + case RTM_IEEE80211_REJOIN: +#endif + join = (struct ieee80211_join_event *) &ifan[1]; + bsd_new_sta(drv, drv->hapd, join->iev_addr); + break; + case RTM_IEEE80211_REPLAY: + /* ignore */ + break; + case RTM_IEEE80211_MICHAEL: + mic = (struct ieee80211_michael_event *) &ifan[1]; + wpa_printf(MSG_DEBUG, + "Michael MIC failure wireless event: " + "keyix=%u src_addr=" MACSTR, mic->iev_keyix, + MAC2STR(mic->iev_src)); + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = 1; + data.michael_mic_failure.src = mic->iev_src; + wpa_supplicant_event(drv->hapd, + EVENT_MICHAEL_MIC_FAILURE, &data); + break; + } + break; + } +} + +static 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) { + wpa_printf(MSG_ERROR, "Could not allocate memory for bsd driver data"); + return NULL; + } + + drv->event_buf_len = rtbuf_len(); + + drv->event_buf = os_malloc(drv->event_buf_len); + if (drv->event_buf == NULL) { + wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); + goto bad; + } + + drv->hapd = hapd; + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); + 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) { + wpa_printf(MSG_ERROR, "socket(PF_ROUTE,SOCK_RAW): %s", + strerror(errno)); + 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); + os_free(drv->event_buf); + 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->event_buf); + os_free(drv); +} + + +static int +bsd_commit(void *priv) +{ + return bsd_ctrl_iface(priv, 1); +} + + +static int +bsd_set_sta_authorized(void *priv, const u8 *addr, + unsigned int total_flags, unsigned int flags_or, + unsigned 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); +} +#else /* HOSTAPD */ + +static int +get80211param(struct bsd_driver_data *drv, int op) +{ + struct ieee80211req ireq; + + if (bsd_get80211(drv, &ireq, op, NULL, 0) < 0) + return -1; + return ireq.i_val; +} + +static int +wpa_driver_bsd_get_bssid(void *priv, u8 *bssid) +{ + struct bsd_driver_data *drv = priv; +#ifdef SIOCG80211BSSID + struct ieee80211_bssid bs; + + os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name)); + if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0) + return -1; + os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid)); + return 0; +#else + return get80211var(drv, IEEE80211_IOC_BSSID, + bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0; +#endif +} + +static int +wpa_driver_bsd_get_ssid(void *priv, u8 *ssid) +{ + struct bsd_driver_data *drv = priv; + return bsd_get_ssid(drv, ssid, 0); +} + +static int +wpa_driver_bsd_set_wpa_ie(struct bsd_driver_data *drv, const u8 *wpa_ie, + size_t wpa_ie_len) +{ +#ifdef IEEE80211_IOC_APPIE + return bsd_set_opt_ie(drv, wpa_ie, wpa_ie_len); +#else /* IEEE80211_IOC_APPIE */ + return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len); +#endif /* IEEE80211_IOC_APPIE */ +} + +static int +wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy) +{ + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d", + __FUNCTION__, wpa, privacy); + + if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0) + ret = -1; + if (set80211param(priv, IEEE80211_IOC_PRIVACY, privacy) < 0) + ret = -1; + if (set80211param(priv, IEEE80211_IOC_WPA, wpa) < 0) + ret = -1; + + return ret; +} + +static int +wpa_driver_bsd_set_wpa(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + + return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled); +} + +static int +wpa_driver_bsd_set_countermeasures(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled); +} + + +static int +wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled); +} + +static int +wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code) +{ + return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code, + addr); +} + +static int +wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg) +{ + int authmode; + + if ((auth_alg & WPA_AUTH_ALG_OPEN) && + (auth_alg & WPA_AUTH_ALG_SHARED)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_alg & WPA_AUTH_ALG_SHARED) + authmode = IEEE80211_AUTH_SHARED; + else + authmode = IEEE80211_AUTH_OPEN; + + return set80211param(priv, IEEE80211_IOC_AUTHMODE, authmode); +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct bsd_driver_data *drv = ctx; + + drv_event_eapol_rx(drv->ctx, src_addr, buf, len); +} + +static int +wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + u32 mode; + int privacy; + int ret = 0; + + wpa_printf(MSG_DEBUG, + "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u" + , __func__ + , (unsigned int) params->ssid_len, params->ssid + , (unsigned int) params->wpa_ie_len + , params->pairwise_suite + , params->group_suite + , params->key_mgmt_suite + ); + + switch (params->mode) { + case IEEE80211_MODE_INFRA: + mode = 0 /* STA */; + break; + case IEEE80211_MODE_IBSS: + /* + * Ref bin/203086 - FreeBSD's net80211 currently uses + * IFM_IEEE80211_ADHOC. + */ +#if 0 + mode = IFM_IEEE80211_IBSS; +#endif + mode = IFM_IEEE80211_ADHOC; + 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 == WPA_CIPHER_NONE && + params->group_suite == WPA_CIPHER_NONE && + params->key_mgmt_suite == WPA_KEY_MGMT_NONE && + params->wpa_ie_len == 0); + wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy); + + 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; + + /* + * NB: interface must be marked UP for association + * or scanning (ap_scan=2) + */ + if (bsd_ctrl_iface(drv, 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; + struct if_announcemsghdr *ifan; + struct if_msghdr *ifm; + struct rt_msghdr *rtm; + union wpa_event_data event; + struct ieee80211_michael_event *mic; + struct ieee80211_leave_event *leave; + struct ieee80211_join_event *join; + int n; + + n = read(sock, drv->event_buf, drv->event_buf_len); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + wpa_printf(MSG_ERROR, "%s read() failed: %s", + __func__, strerror(errno)); + return; + } + + rtm = (struct rt_msghdr *) drv->event_buf; + if (rtm->rtm_version != RTM_VERSION) { + wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", + rtm->rtm_version); + return; + } + os_memset(&event, 0, sizeof(event)); + switch (rtm->rtm_type) { + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *) rtm; + if (ifan->ifan_index != drv->ifindex) + break; + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + switch (ifan->ifan_what) { + case IFAN_DEPARTURE: + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + default: + return; + } + wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s", + event.interface_status.ifname, + ifan->ifan_what == IFAN_DEPARTURE ? + "removed" : "added"); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + break; + case RTM_IEEE80211: + ifan = (struct if_announcemsghdr *) rtm; + if (ifan->ifan_index != drv->ifindex) + break; + switch (ifan->ifan_what) { + case RTM_IEEE80211_ASSOC: + case RTM_IEEE80211_REASSOC: + if (drv->is_ap) + break; + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + break; + case RTM_IEEE80211_DISASSOC: + if (drv->is_ap) + break; + wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); + break; + case RTM_IEEE80211_SCAN: + if (drv->is_ap) + break; + wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + break; + case RTM_IEEE80211_LEAVE: + leave = (struct ieee80211_leave_event *) &ifan[1]; + drv_event_disassoc(ctx, leave->iev_addr); + break; + case RTM_IEEE80211_JOIN: +#ifdef RTM_IEEE80211_REJOIN + case RTM_IEEE80211_REJOIN: +#endif + join = (struct ieee80211_join_event *) &ifan[1]; + bsd_new_sta(drv, ctx, join->iev_addr); + break; + case RTM_IEEE80211_REPLAY: + /* ignore */ + break; + case RTM_IEEE80211_MICHAEL: + mic = (struct ieee80211_michael_event *) &ifan[1]; + wpa_printf(MSG_DEBUG, + "Michael MIC failure wireless event: " + "keyix=%u src_addr=" MACSTR, mic->iev_keyix, + MAC2STR(mic->iev_src)); + + os_memset(&event, 0, sizeof(event)); + event.michael_mic_failure.unicast = + !IEEE80211_IS_MULTICAST(mic->iev_dst); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, + &event); + break; + } + break; + case RTM_IFINFO: + ifm = (struct if_msghdr *) rtm; + if (ifm->ifm_index != drv->ifindex) + break; + if ((rtm->rtm_flags & RTF_UP) == 0) { + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN", + event.interface_status.ifname); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + } + break; + } +} + +static void +wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res, + struct ieee80211req_scan_result *sr) +{ + struct wpa_scan_res *result, **tmp; + size_t extra_len; + u8 *pos; + + extra_len = 2 + sr->isr_ssid_len; + extra_len += 2 + sr->isr_nrates; + extra_len += 3; /* ERP IE */ + extra_len += sr->isr_ie_len; + + result = os_zalloc(sizeof(*result) + extra_len); + if (result == NULL) + return; + os_memcpy(result->bssid, sr->isr_bssid, ETH_ALEN); + result->freq = sr->isr_freq; + result->beacon_int = sr->isr_intval; + result->caps = sr->isr_capinfo; + result->qual = sr->isr_rssi; + result->noise = sr->isr_noise; + /* + * the rssi value reported by the kernel is in 0.5dB steps relative to + * the reported noise floor. see ieee80211_node.h for details. + */ + result->level = sr->isr_rssi / 2 + sr->isr_noise; + + pos = (u8 *)(result + 1); + + *pos++ = WLAN_EID_SSID; + *pos++ = sr->isr_ssid_len; + os_memcpy(pos, sr + 1, sr->isr_ssid_len); + pos += sr->isr_ssid_len; + + /* + * Deal all rates as supported rate. + * Because net80211 doesn't report extended supported rate or not. + */ + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = sr->isr_nrates; + os_memcpy(pos, sr->isr_rates, sr->isr_nrates); + pos += sr->isr_nrates; + + *pos++ = WLAN_EID_ERP_INFO; + *pos++ = 1; + *pos++ = sr->isr_erp; + +#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len + sr->isr_meshid_len, + sr->isr_ie_len); +#else + os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len); +#endif + pos += sr->isr_ie_len; + + result->ie_len = pos - (u8 *)(result + 1); + + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); + if (tmp == NULL) { + os_free(result); + return; + } + tmp[res->num++] = result; + res->res = tmp; +} + +struct wpa_scan_results * +wpa_driver_bsd_get_scan_results2(void *priv) +{ + struct ieee80211req_scan_result *sr; + struct wpa_scan_results *res; + int len, rest; + uint8_t buf[24*1024], *pos; + + len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024); + if (len < 0) + return NULL; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return NULL; + + pos = buf; + rest = len; + while (rest >= sizeof(struct ieee80211req_scan_result)) { + sr = (struct ieee80211req_scan_result *)pos; + wpa_driver_bsd_add_scan_entry(res, sr); + pos += sr->isr_len; + rest -= sr->isr_len; + } + + wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)", + len, (unsigned long)res->num); + + return res; +} + +static int wpa_driver_bsd_capa(struct bsd_driver_data *drv) +{ +#ifdef IEEE80211_IOC_DEVCAPS +/* kernel definitions copied from net80211/ieee80211_var.h */ +#define IEEE80211_CIPHER_WEP 0 +#define IEEE80211_CIPHER_TKIP 1 +#define IEEE80211_CIPHER_AES_CCM 3 +#define IEEE80211_CRYPTO_WEP (1<<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; + +#ifdef __FreeBSD__ + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP; +#else + /* + * XXX + * FreeBSD exports hardware cryptocaps. These have no meaning for wpa + * since net80211 performs software crypto. + */ + 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; +#endif + + 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 enum ieee80211_opmode +get80211opmode(struct bsd_driver_data *drv) +{ + struct ifmediareq ifmr; + + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name)); + + if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { + if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) { + if (ifmr.ifm_current & IFM_FLAG0) + return IEEE80211_M_AHDEMO; + else + return IEEE80211_M_IBSS; + } + if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) + return IEEE80211_M_HOSTAP; + if (ifmr.ifm_current & IFM_IEEE80211_IBSS) + return IEEE80211_M_IBSS; + if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) + return IEEE80211_M_MONITOR; +#ifdef IEEE80211_M_MBSS + if (ifmr.ifm_current & IFM_IEEE80211_MBSS) + return IEEE80211_M_MBSS; +#endif /* IEEE80211_M_MBSS */ + } + return IEEE80211_M_STA; +} + +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; + + drv->event_buf_len = rtbuf_len(); + + drv->event_buf = os_malloc(drv->event_buf_len); + if (drv->event_buf == NULL) { + wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); + goto fail1; + } + + /* + * NB: We require the interface name be mappable to an index. + * This implies we do not support having wpa_supplicant + * wait for an interface to appear. This seems ok; that + * doesn't belong here; it's really the job of devd. + */ + drv->ifindex = if_nametoindex(ifname); + if (drv->ifindex == 0) { + wpa_printf(MSG_DEBUG, "%s: interface %s does not exist", + __func__, ifname); + goto fail1; + } + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) + goto fail1; + + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + /* Down interface during setup. */ + if (bsd_ctrl_iface(drv, 0) < 0) + goto fail; + + drv->route = socket(PF_ROUTE, SOCK_RAW, 0); + if (drv->route < 0) + goto fail; + eloop_register_read_sock(drv->route, + wpa_driver_bsd_event_receive, ctx, drv); + + drv->ctx = ctx; + + if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) { + wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s", + __func__, strerror(errno)); + goto fail; + } + if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) { + wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s", + __func__, strerror(errno)); + goto fail; + } + if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) { + wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s", + __func__, strerror(errno)); + goto fail; + } + + if (wpa_driver_bsd_capa(drv)) + goto fail; + + drv->opmode = get80211opmode(drv); + + return drv; +fail: + close(drv->sock); +fail1: + os_free(drv->event_buf); + 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->event_buf); + os_free(drv); +} + +static int +wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct bsd_driver_data *drv = priv; + + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} +#endif /* HOSTAPD */ + + +const struct wpa_driver_ops wpa_driver_bsd_ops = { + .name = "bsd", + .desc = "BSD 802.11 support", +#ifdef HOSTAPD + .hapd_init = bsd_init, + .hapd_deinit = bsd_deinit, + .set_privacy = bsd_set_privacy, + .get_seqnum = bsd_get_seqnum, + .flush = bsd_flush, + .read_sta_data = bsd_read_sta_driver_data, + .sta_disassoc = bsd_sta_disassoc, + .sta_deauth = bsd_sta_deauth, + .sta_set_flags = bsd_set_sta_authorized, + .commit = bsd_commit, +#else /* HOSTAPD */ + .init = wpa_driver_bsd_init, + .deinit = wpa_driver_bsd_deinit, + .get_bssid = wpa_driver_bsd_get_bssid, + .get_ssid = wpa_driver_bsd_get_ssid, + .set_countermeasures = wpa_driver_bsd_set_countermeasures, + .scan2 = wpa_driver_bsd_scan, + .get_scan_results2 = wpa_driver_bsd_get_scan_results2, + .deauthenticate = wpa_driver_bsd_deauthenticate, + .associate = wpa_driver_bsd_associate, + .get_capa = wpa_driver_bsd_get_capa, +#endif /* HOSTAPD */ + .set_freq = bsd_set_freq, + .set_key = bsd_set_key, + .set_ieee8021x = bsd_set_ieee8021x, + .hapd_set_ssid = bsd_set_ssid, + .hapd_get_ssid = bsd_get_ssid, + .hapd_send_eapol = bsd_send_eapol, + .set_generic_elem = bsd_set_opt_ie, +}; diff --git a/freebsd/contrib/wpa/src/drivers/driver_common.c b/freebsd/contrib/wpa/src/drivers/driver_common.c new file mode 100644 index 00000000..28f483e9 --- /dev/null +++ b/freebsd/contrib/wpa/src/drivers/driver_common.c @@ -0,0 +1,222 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Common driver-related functions + * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "utils/common.h" +#include "driver.h" + +void wpa_scan_results_free(struct wpa_scan_results *res) +{ + size_t i; + + if (res == NULL) + return; + + for (i = 0; i < res->num; i++) + os_free(res->res[i]); + os_free(res->res); + os_free(res); +} + + +const char * event_to_string(enum wpa_event_type event) +{ +#define E2S(n) case EVENT_ ## n: return #n + switch (event) { + E2S(ASSOC); + E2S(DISASSOC); + E2S(MICHAEL_MIC_FAILURE); + E2S(SCAN_RESULTS); + E2S(ASSOCINFO); + E2S(INTERFACE_STATUS); + E2S(PMKID_CANDIDATE); + E2S(STKSTART); + E2S(TDLS); + E2S(FT_RESPONSE); + E2S(IBSS_RSN_START); + E2S(AUTH); + E2S(DEAUTH); + E2S(ASSOC_REJECT); + E2S(AUTH_TIMED_OUT); + E2S(ASSOC_TIMED_OUT); + E2S(WPS_BUTTON_PUSHED); + E2S(TX_STATUS); + E2S(RX_FROM_UNKNOWN); + E2S(RX_MGMT); + E2S(REMAIN_ON_CHANNEL); + E2S(CANCEL_REMAIN_ON_CHANNEL); + E2S(RX_PROBE_REQ); + E2S(NEW_STA); + E2S(EAPOL_RX); + E2S(SIGNAL_CHANGE); + E2S(INTERFACE_ENABLED); + E2S(INTERFACE_DISABLED); + E2S(CHANNEL_LIST_CHANGED); + E2S(INTERFACE_UNAVAILABLE); + E2S(BEST_CHANNEL); + E2S(UNPROT_DEAUTH); + E2S(UNPROT_DISASSOC); + E2S(STATION_LOW_ACK); + E2S(IBSS_PEER_LOST); + E2S(DRIVER_GTK_REKEY); + E2S(SCHED_SCAN_STOPPED); + E2S(DRIVER_CLIENT_POLL_OK); + E2S(EAPOL_TX_STATUS); + E2S(CH_SWITCH); + E2S(WNM); + E2S(CONNECT_FAILED_REASON); + E2S(DFS_RADAR_DETECTED); + E2S(DFS_CAC_FINISHED); + E2S(DFS_CAC_ABORTED); + E2S(DFS_NOP_FINISHED); + E2S(SURVEY); + E2S(SCAN_STARTED); + E2S(AVOID_FREQUENCIES); + E2S(NEW_PEER_CANDIDATE); + E2S(ACS_CHANNEL_SELECTED); + E2S(DFS_CAC_STARTED); + } + + return "UNKNOWN"; +#undef E2S +} + + +const char * channel_width_to_string(enum chan_width width) +{ + switch (width) { + case CHAN_WIDTH_20_NOHT: + return "20 MHz (no HT)"; + case CHAN_WIDTH_20: + return "20 MHz"; + case CHAN_WIDTH_40: + return "40 MHz"; + case CHAN_WIDTH_80: + return "80 MHz"; + case CHAN_WIDTH_80P80: + return "80+80 MHz"; + case CHAN_WIDTH_160: + return "160 MHz"; + default: + return "unknown"; + } +} + + +int ht_supported(const struct hostapd_hw_modes *mode) +{ + if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) { + /* + * The driver did not indicate whether it supports HT. Assume + * it does to avoid connection issues. + */ + return 1; + } + + /* + * IEEE Std 802.11n-2009 20.1.1: + * An HT non-AP STA shall support all EQM rates for one spatial stream. + */ + return mode->mcs_set[0] == 0xff; +} + + +int vht_supported(const struct hostapd_hw_modes *mode) +{ + if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) { + /* + * The driver did not indicate whether it supports VHT. Assume + * it does to avoid connection issues. + */ + return 1; + } + + /* + * A VHT non-AP STA shall support MCS 0-7 for one spatial stream. + * TODO: Verify if this complies with the standard + */ + return (mode->vht_mcs_set[0] & 0x3) != 3; +} + + +static int wpa_check_wowlan_trigger(const char *start, const char *trigger, + int capa_trigger, u8 *param_trigger) +{ + if (os_strcmp(start, trigger) != 0) + return 0; + if (!capa_trigger) + return 0; + + *param_trigger = 1; + return 1; +} + + +struct wowlan_triggers * +wpa_get_wowlan_triggers(const char *wowlan_triggers, + const struct wpa_driver_capa *capa) +{ + struct wowlan_triggers *triggers; + char *start, *end, *buf; + int last; + + if (!wowlan_triggers) + return NULL; + + buf = os_strdup(wowlan_triggers); + if (buf == NULL) + return NULL; + + triggers = os_zalloc(sizeof(*triggers)); + if (triggers == NULL) + goto out; + +#define CHECK_TRIGGER(trigger) \ + wpa_check_wowlan_trigger(start, #trigger, \ + capa->wowlan_triggers.trigger, \ + &triggers->trigger) + + start = buf; + while (*start != '\0') { + while (isblank(*start)) + start++; + if (*start == '\0') + break; + end = start; + while (!isblank(*end) && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + + if (!CHECK_TRIGGER(any) && + !CHECK_TRIGGER(disconnect) && + !CHECK_TRIGGER(magic_pkt) && + !CHECK_TRIGGER(gtk_rekey_failure) && + !CHECK_TRIGGER(eap_identity_req) && + !CHECK_TRIGGER(four_way_handshake) && + !CHECK_TRIGGER(rfkill_release)) { + wpa_printf(MSG_DEBUG, + "Unknown/unsupported wowlan trigger '%s'", + start); + os_free(triggers); + triggers = NULL; + goto out; + } + + if (last) + break; + start = end + 1; + } +#undef CHECK_TRIGGER + +out: + os_free(buf); + return triggers; +} diff --git a/freebsd/contrib/wpa/src/drivers/driver_ndis.c b/freebsd/contrib/wpa/src/drivers/driver_ndis.c new file mode 100644 index 00000000..6b3b26bc --- /dev/null +++ b/freebsd/contrib/wpa/src/drivers/driver_ndis.c @@ -0,0 +1,3221 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * WPA Supplicant - Windows/NDIS driver interface + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifdef __CYGWIN__ +/* Avoid some header file conflicts by not including standard headers for + * cygwin builds when Packet32.h is included. */ +#include "build_config.h" +int close(int fd); +#else /* __CYGWIN__ */ +#include "includes.h" +#endif /* __CYGWIN__ */ +#ifdef CONFIG_USE_NDISUIO +#include <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_3_MULTICAST_LIST 0x01010103 +#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[SSID_MAX_LEN]; + int i; + for (i = 0; i < SSID_MAX_LEN; i++) + ssid[i] = rand() & 0xff; + return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, SSID_MAX_LEN); +} + + +static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ndis_data *drv = priv; + return wpa_driver_ndis_disconnect(drv); +} + + +static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static int wpa_driver_ndis_scan_native80211( + struct wpa_driver_ndis_data *drv, + struct wpa_driver_scan_params *params) +{ + DOT11_SCAN_REQUEST_V2 req; + int res; + + os_memset(&req, 0, sizeof(req)); + req.dot11BSSType = dot11_BSS_type_any; + os_memset(req.dot11BSSID, 0xff, ETH_ALEN); + req.dot11ScanType = dot11_scan_type_auto; + res = ndis_set_oid(drv, OID_DOT11_SCAN_REQUEST, (char *) &req, + sizeof(req)); + eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); + eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv, + drv->ctx); + return res; +} + + +static int wpa_driver_ndis_scan(void *priv, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_ndis_data *drv = priv; + int res; + + if (drv->native80211) + return wpa_driver_ndis_scan_native80211(drv, params); + + if (!drv->radio_enabled) { + wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first" + " scan"); + if (wpa_driver_ndis_disconnect(drv) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio"); + } + drv->radio_enabled = 1; + } + + res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, " ", 4); + eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); + eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv, + drv->ctx); + return res; +} + + +static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) +{ + const u8 *end, *pos; + + pos = (const u8 *) (res + 1); + end = pos + res->ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid( + struct wpa_scan_res *r, NDIS_802_11_SSID *ssid) +{ + struct wpa_scan_res *nr; + u8 *pos; + + if (wpa_scan_get_ie(r, WLAN_EID_SSID)) + return r; /* SSID IE already present */ + + if (ssid->SsidLength == 0 || ssid->SsidLength > SSID_MAX_LEN) + return r; /* No valid SSID inside scan data */ + + nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength); + if (nr == NULL) + return r; + + pos = ((u8 *) (nr + 1)) + nr->ie_len; + *pos++ = WLAN_EID_SSID; + *pos++ = ssid->SsidLength; + os_memcpy(pos, ssid->Ssid, ssid->SsidLength); + nr->ie_len += 2 + ssid->SsidLength; + + return nr; +} + + +static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + NDIS_802_11_BSSID_LIST_EX *b; + size_t blen, count, i; + int len; + char *pos; + struct wpa_scan_results *results; + struct wpa_scan_res *r; + + blen = 65535; + b = os_zalloc(blen); + if (b == NULL) + return NULL; + len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen); + if (len < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results"); + os_free(b); + return NULL; + } + count = b->NumberOfItems; + + results = os_zalloc(sizeof(*results)); + if (results == NULL) { + os_free(b); + return NULL; + } + results->res = os_calloc(count, sizeof(struct wpa_scan_res *)); + if (results->res == NULL) { + os_free(results); + os_free(b); + return NULL; + } + + pos = (char *) &b->Bssid[0]; + for (i = 0; i < count; i++) { + NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos; + NDIS_802_11_FIXED_IEs *fixed; + + if (bss->IELength < sizeof(NDIS_802_11_FIXED_IEs)) { + wpa_printf(MSG_DEBUG, "NDIS: too small IELength=%d", + (int) bss->IELength); + break; + } + if (((char *) bss->IEs) + bss->IELength > (char *) b + blen) { + /* + * Some NDIS drivers have been reported to include an + * entry with an invalid IELength in scan results and + * this has crashed wpa_supplicant, so validate the + * returned value before using it. + */ + wpa_printf(MSG_DEBUG, "NDIS: skipped invalid scan " + "result IE (BSSID=" MACSTR ") IELength=%d", + MAC2STR(bss->MacAddress), + (int) bss->IELength); + break; + } + + r = os_zalloc(sizeof(*r) + bss->IELength - + sizeof(NDIS_802_11_FIXED_IEs)); + if (r == NULL) + break; + + os_memcpy(r->bssid, bss->MacAddress, ETH_ALEN); + r->level = (int) bss->Rssi; + r->freq = bss->Configuration.DSConfig / 1000; + fixed = (NDIS_802_11_FIXED_IEs *) bss->IEs; + r->beacon_int = WPA_GET_LE16((u8 *) &fixed->BeaconInterval); + r->caps = WPA_GET_LE16((u8 *) &fixed->Capabilities); + r->tsf = WPA_GET_LE64(fixed->Timestamp); + os_memcpy(r + 1, bss->IEs + sizeof(NDIS_802_11_FIXED_IEs), + bss->IELength - sizeof(NDIS_802_11_FIXED_IEs)); + r->ie_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs); + r = wpa_driver_ndis_add_scan_ssid(r, &bss->Ssid); + + results->res[results->num++] = r; + + pos += bss->Length; + if (pos > (char *) b + blen) + break; + } + + os_free(b); + + return results; +} + + +static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv, + int key_idx, const u8 *addr, + const u8 *bssid, int pairwise) +{ + NDIS_802_11_REMOVE_KEY rkey; + NDIS_802_11_KEY_INDEX index; + int res, res2; + + os_memset(&rkey, 0, sizeof(rkey)); + + rkey.Length = sizeof(rkey); + rkey.KeyIndex = key_idx; + if (pairwise) + rkey.KeyIndex |= 1 << 30; + os_memcpy(rkey.BSSID, bssid, ETH_ALEN); + + res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey, + sizeof(rkey)); + if (!pairwise) { + index = key_idx; + res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP, + (char *) &index, sizeof(index)); + } else + res2 = 0; + + if (res < 0 && res2 < 0) + return -1; + return 0; +} + + +static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv, + int pairwise, int key_idx, int set_tx, + const u8 *key, size_t key_len) +{ + NDIS_802_11_WEP *wep; + size_t len; + int res; + + len = 12 + key_len; + wep = os_zalloc(len); + if (wep == NULL) + return -1; + wep->Length = len; + wep->KeyIndex = key_idx; + if (set_tx) + wep->KeyIndex |= 1 << 31; +#if 0 /* Setting bit30 does not seem to work with some NDIS drivers */ + if (pairwise) + wep->KeyIndex |= 1 << 30; +#endif + wep->KeyLength = key_len; + os_memcpy(wep->KeyMaterial, key, key_len); + + wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_WEP", + (u8 *) wep, len); + res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len); + + os_free(wep); + + return res; +} + + +static int wpa_driver_ndis_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_ndis_data *drv = priv; + size_t len, i; + NDIS_802_11_KEY *nkey; + int res, pairwise; + u8 bssid[ETH_ALEN]; + + if (addr == NULL || is_broadcast_ether_addr(addr)) { + /* Group Key */ + pairwise = 0; + if (wpa_driver_ndis_get_bssid(drv, bssid) < 0) + os_memset(bssid, 0xff, ETH_ALEN); + } else { + /* Pairwise Key */ + pairwise = 1; + os_memcpy(bssid, addr, ETH_ALEN); + } + + if (alg == WPA_ALG_NONE || key_len == 0) { + return wpa_driver_ndis_remove_key(drv, key_idx, addr, bssid, + pairwise); + } + + if (alg == WPA_ALG_WEP) { + return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx, + key, key_len); + } + + len = 12 + 6 + 6 + 8 + key_len; + + nkey = os_zalloc(len); + if (nkey == NULL) + return -1; + + nkey->Length = len; + nkey->KeyIndex = key_idx; + if (set_tx) + nkey->KeyIndex |= 1 << 31; + if (pairwise) + nkey->KeyIndex |= 1 << 30; + if (seq && seq_len) + nkey->KeyIndex |= 1 << 29; + nkey->KeyLength = key_len; + os_memcpy(nkey->BSSID, bssid, ETH_ALEN); + if (seq && seq_len) { + for (i = 0; i < seq_len; i++) + nkey->KeyRSC |= (ULONGLONG) seq[i] << (i * 8); + } + if (alg == WPA_ALG_TKIP && key_len == 32) { + os_memcpy(nkey->KeyMaterial, key, 16); + os_memcpy(nkey->KeyMaterial + 16, key + 24, 8); + os_memcpy(nkey->KeyMaterial + 24, key + 16, 8); + } else { + os_memcpy(nkey->KeyMaterial, key, key_len); + } + + wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_KEY", + (u8 *) nkey, len); + res = ndis_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len); + os_free(nkey); + + return res; +} + + +static int +wpa_driver_ndis_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ndis_data *drv = priv; + u32 auth_mode, encr, priv_mode, mode; + u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + drv->mode = params->mode; + + /* Note: Setting OID_802_11_INFRASTRUCTURE_MODE clears current keys, + * so static WEP keys needs to be set again after this. */ + if (params->mode == IEEE80211_MODE_IBSS) { + mode = Ndis802_11IBSS; + /* Need to make sure that BSSID polling is enabled for + * IBSS mode. */ + eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL); + eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, + drv, NULL); + } else + mode = Ndis802_11Infrastructure; + if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, + (char *) &mode, sizeof(mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_INFRASTRUCTURE_MODE (%d)", + (int) mode); + /* Try to continue anyway */ + } + + if (params->key_mgmt_suite == WPA_KEY_MGMT_NONE || + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_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 == WPA_KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPA2PSK; + else + auth_mode = Ndis802_11AuthModeWPA2; +#ifdef CONFIG_WPS + } else if (params->key_mgmt_suite == WPA_KEY_MGMT_WPS) { + auth_mode = Ndis802_11AuthModeOpen; + priv_mode = Ndis802_11PrivFilterAcceptAll; + if (params->wps == WPS_MODE_PRIVACY) { + u8 dummy_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 }; + /* + * Some NDIS drivers refuse to associate in open mode + * configuration due to Privacy field mismatch, so use + * a workaround to make the configuration look like + * matching one for WPS provisioning. + */ + wpa_printf(MSG_DEBUG, "NDIS: Set dummy WEP key as a " + "workaround to allow driver to associate " + "for WPS"); + wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP, + bcast, 0, 1, + NULL, 0, dummy_key, + sizeof(dummy_key)); + } +#endif /* CONFIG_WPS */ + } else { + priv_mode = Ndis802_11PrivFilter8021xWEP; + if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE) + auth_mode = Ndis802_11AuthModeWPANone; + else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPAPSK; + else + auth_mode = Ndis802_11AuthModeWPA; + } + + switch (params->pairwise_suite) { + case WPA_CIPHER_CCMP: + encr = Ndis802_11Encryption3Enabled; + break; + case WPA_CIPHER_TKIP: + encr = Ndis802_11Encryption2Enabled; + break; + case WPA_CIPHER_WEP40: + case WPA_CIPHER_WEP104: + encr = Ndis802_11Encryption1Enabled; + break; + case WPA_CIPHER_NONE: +#ifdef CONFIG_WPS + if (params->wps == WPS_MODE_PRIVACY) { + encr = Ndis802_11Encryption1Enabled; + break; + } +#endif /* CONFIG_WPS */ + if (params->group_suite == WPA_CIPHER_CCMP) + encr = Ndis802_11Encryption3Enabled; + else if (params->group_suite == WPA_CIPHER_TKIP) + encr = Ndis802_11Encryption2Enabled; + else + encr = Ndis802_11EncryptionDisabled; + break; + default: +#ifdef CONFIG_WPS + if (params->wps == WPS_MODE_PRIVACY) { + encr = Ndis802_11Encryption1Enabled; + break; + } +#endif /* CONFIG_WPS */ + encr = Ndis802_11EncryptionDisabled; + break; + }; + + if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER, + (char *) &priv_mode, sizeof(priv_mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_PRIVACY_FILTER (%d)", + (int) priv_mode); + /* Try to continue anyway */ + } + + ndis_set_auth_mode(drv, auth_mode); + ndis_set_encr_status(drv, encr); + + if (params->bssid) { + ndis_set_oid(drv, OID_802_11_BSSID, (char *) params->bssid, + ETH_ALEN); + drv->oid_bssid_set = 1; + } else if (drv->oid_bssid_set) { + ndis_set_oid(drv, OID_802_11_BSSID, "\xff\xff\xff\xff\xff\xff", + ETH_ALEN); + drv->oid_bssid_set = 0; + } + + return wpa_driver_ndis_set_ssid(drv, params->ssid, params->ssid_len); +} + + +static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv) +{ + int len, count, i, ret; + struct ndis_pmkid_entry *entry; + NDIS_802_11_PMKID *p; + + count = 0; + entry = drv->pmkid; + while (entry) { + count++; + if (count >= drv->no_of_pmkid) + break; + entry = entry->next; + } + len = 8 + count * sizeof(BSSID_INFO); + p = os_zalloc(len); + if (p == NULL) + return -1; + + p->Length = len; + p->BSSIDInfoCount = count; + entry = drv->pmkid; + for (i = 0; i < count; i++) { + os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN); + os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16); + entry = entry->next; + } + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (u8 *) p, len); + ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) p, len); + os_free(p); + return ret; +} + + +static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ndis_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->no_of_pmkid == 0) + return 0; + + prev = NULL; + entry = drv->pmkid; + while (entry) { + if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0) + break; + prev = entry; + entry = entry->next; + } + + if (entry) { + /* Replace existing entry for this BSSID and move it into the + * beginning of the list. */ + os_memcpy(entry->pmkid, pmkid, 16); + if (prev) { + prev->next = entry->next; + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } else { + entry = os_malloc(sizeof(*entry)); + if (entry) { + os_memcpy(entry->bssid, bssid, ETH_ALEN); + os_memcpy(entry->pmkid, pmkid, 16); + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } + + return wpa_driver_ndis_set_pmkid(drv); +} + + +static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ndis_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->no_of_pmkid == 0) + return 0; + + entry = drv->pmkid; + prev = NULL; + while (entry) { + if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 && + os_memcmp(entry->pmkid, pmkid, 16) == 0) { + if (prev) + prev->next = entry->next; + else + drv->pmkid = entry->next; + os_free(entry); + break; + } + prev = entry; + entry = entry->next; + } + return wpa_driver_ndis_set_pmkid(drv); +} + + +static int wpa_driver_ndis_flush_pmkid(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + NDIS_802_11_PMKID p; + struct ndis_pmkid_entry *pmkid, *prev; + int prev_authmode, ret; + + if (drv->no_of_pmkid == 0) + return 0; + + pmkid = drv->pmkid; + drv->pmkid = NULL; + while (pmkid) { + prev = pmkid; + pmkid = pmkid->next; + os_free(prev); + } + + /* + * Some drivers may refuse OID_802_11_PMKID if authMode is not set to + * WPA2, so change authMode temporarily, if needed. + */ + prev_authmode = ndis_get_auth_mode(drv); + if (prev_authmode != Ndis802_11AuthModeWPA2) + ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA2); + + os_memset(&p, 0, sizeof(p)); + p.Length = 8; + p.BSSIDInfoCount = 0; + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)", + (u8 *) &p, 8); + ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8); + + if (prev_authmode != Ndis802_11AuthModeWPA2) + ndis_set_auth_mode(drv, prev_authmode); + + return ret; +} + + +static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv) +{ + char buf[512], *pos; + NDIS_802_11_ASSOCIATION_INFORMATION *ai; + int len; + union wpa_event_data data; + NDIS_802_11_BSSID_LIST_EX *b; + size_t blen, i; + + len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf, + sizeof(buf)); + if (len < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to get association " + "information"); + return -1; + } + if (len > sizeof(buf)) { + /* Some drivers seem to be producing incorrect length for this + * data. Limit the length to the current buffer size to avoid + * crashing in hexdump. The data seems to be otherwise valid, + * so better try to use it. */ + wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association " + "information length %d", len); + len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, + buf, sizeof(buf)); + if (len < -1) { + wpa_printf(MSG_DEBUG, "NDIS: re-reading association " + "information failed"); + return -1; + } + if (len > sizeof(buf)) { + wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association" + " information length %d (re-read)", len); + len = sizeof(buf); + } + } + wpa_hexdump(MSG_MSGDUMP, "NDIS: association information", + (u8 *) buf, len); + if (len < sizeof(*ai)) { + wpa_printf(MSG_DEBUG, "NDIS: too short association " + "information"); + return -1; + } + ai = (NDIS_802_11_ASSOCIATION_INFORMATION *) buf; + wpa_printf(MSG_DEBUG, "NDIS: ReqFixed=0x%x RespFixed=0x%x off_req=%d " + "off_resp=%d len_req=%d len_resp=%d", + ai->AvailableRequestFixedIEs, ai->AvailableResponseFixedIEs, + (int) ai->OffsetRequestIEs, (int) ai->OffsetResponseIEs, + (int) ai->RequestIELength, (int) ai->ResponseIELength); + + if (ai->OffsetRequestIEs + ai->RequestIELength > (unsigned) len || + ai->OffsetResponseIEs + ai->ResponseIELength > (unsigned) len) { + wpa_printf(MSG_DEBUG, "NDIS: association information - " + "IE overflow"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "NDIS: Request IEs", + (u8 *) buf + ai->OffsetRequestIEs, ai->RequestIELength); + wpa_hexdump(MSG_MSGDUMP, "NDIS: Response IEs", + (u8 *) buf + ai->OffsetResponseIEs, ai->ResponseIELength); + + os_memset(&data, 0, sizeof(data)); + data.assoc_info.req_ies = (u8 *) buf + ai->OffsetRequestIEs; + data.assoc_info.req_ies_len = ai->RequestIELength; + data.assoc_info.resp_ies = (u8 *) buf + ai->OffsetResponseIEs; + data.assoc_info.resp_ies_len = ai->ResponseIELength; + + blen = 65535; + b = os_zalloc(blen); + if (b == NULL) + goto skip_scan_results; + len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen); + if (len < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results"); + os_free(b); + b = NULL; + goto skip_scan_results; + } + wpa_printf(MSG_DEBUG, "NDIS: %d BSSID items to process for AssocInfo", + (unsigned int) b->NumberOfItems); + + pos = (char *) &b->Bssid[0]; + for (i = 0; i < b->NumberOfItems; i++) { + NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos; + if (os_memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 && + bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) { + data.assoc_info.beacon_ies = + ((u8 *) bss->IEs) + + sizeof(NDIS_802_11_FIXED_IEs); + data.assoc_info.beacon_ies_len = + bss->IELength - sizeof(NDIS_802_11_FIXED_IEs); + wpa_hexdump(MSG_MSGDUMP, "NDIS: Beacon IEs", + data.assoc_info.beacon_ies, + data.assoc_info.beacon_ies_len); + break; + } + pos += bss->Length; + if (pos > (char *) b + blen) + break; + } + +skip_scan_results: + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + + os_free(b); + + return 0; +} + + +static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_ndis_data *drv = eloop_ctx; + u8 bssid[ETH_ALEN]; + int poll; + + if (drv->wired) + return; + + if (wpa_driver_ndis_get_bssid(drv, bssid)) { + /* Disconnected */ + if (!is_zero_ether_addr(drv->bssid)) { + os_memset(drv->bssid, 0, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + } + } else { + /* Connected */ + if (os_memcmp(drv->bssid, bssid, ETH_ALEN) != 0) { + os_memcpy(drv->bssid, bssid, ETH_ALEN); + wpa_driver_ndis_get_associnfo(drv); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + } + } + + /* When using integrated NDIS event receiver, we can skip BSSID + * polling when using infrastructure network. However, when using + * IBSS mode, many driver do not seem to generate connection event, + * so we need to enable BSSID polling to figure out when IBSS network + * has been formed. + */ + poll = drv->mode == IEEE80211_MODE_IBSS; +#ifndef CONFIG_NDIS_EVENTS_INTEGRATED +#ifndef _WIN32_WCE + poll = 1; +#endif /* _WIN32_WCE */ +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + + if (poll) { + eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, + drv, NULL); + } +} + + +static void wpa_driver_ndis_poll(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL); + wpa_driver_ndis_poll_timeout(drv, NULL); +} + + +/* Called when driver generates Media Connect Event by calling + * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_CONNECT */ +void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv) +{ + wpa_printf(MSG_DEBUG, "NDIS: Media Connect Event"); + if (wpa_driver_ndis_get_bssid(drv, drv->bssid) == 0) { + wpa_driver_ndis_get_associnfo(drv); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + } +} + + +/* Called when driver generates Media Disconnect Event by calling + * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_DISCONNECT */ +void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv) +{ + wpa_printf(MSG_DEBUG, "NDIS: Media Disconnect Event"); + os_memset(drv->bssid, 0, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); +} + + +static void wpa_driver_ndis_event_auth(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_AUTHENTICATION_REQUEST *req; + int pairwise = 0, group = 0; + union wpa_event_data event; + + if (data_len < sizeof(*req)) { + wpa_printf(MSG_DEBUG, "NDIS: Too short Authentication Request " + "Event (len=%d)", data_len); + return; + } + req = (NDIS_802_11_AUTHENTICATION_REQUEST *) data; + + wpa_printf(MSG_DEBUG, "NDIS: Authentication Request Event: " + "Bssid " MACSTR " Flags 0x%x", + MAC2STR(req->Bssid), (int) req->Flags); + + if ((req->Flags & NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) == + NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) + pairwise = 1; + else if ((req->Flags & NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) == + NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) + group = 1; + + if (pairwise || group) { + os_memset(&event, 0, sizeof(event)); + event.michael_mic_failure.unicast = pairwise; + wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, + &event); + } +} + + +static void wpa_driver_ndis_event_pmkid(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid; + size_t i; + union wpa_event_data event; + + if (data_len < 8) { + wpa_printf(MSG_DEBUG, "NDIS: Too short PMKID Candidate List " + "Event (len=%d)", data_len); + return; + } + pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data; + wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List Event - Version %d " + "NumCandidates %d", + (int) pmkid->Version, (int) pmkid->NumCandidates); + + if (pmkid->Version != 1) { + wpa_printf(MSG_DEBUG, "NDIS: Unsupported PMKID Candidate List " + "Version %d", (int) pmkid->Version); + return; + } + + if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) { + wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List underflow"); + return; + } + + os_memset(&event, 0, sizeof(event)); + for (i = 0; i < pmkid->NumCandidates; i++) { + PMKID_CANDIDATE *p = &pmkid->CandidateList[i]; + wpa_printf(MSG_DEBUG, "NDIS: %d: " MACSTR " Flags 0x%x", + i, MAC2STR(p->BSSID), (int) p->Flags); + os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN); + event.pmkid_candidate.index = i; + event.pmkid_candidate.preauth = + p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, + &event); + } +} + + +/* Called when driver calls NdisMIndicateStatus() with + * NDIS_STATUS_MEDIA_SPECIFIC_INDICATION */ +void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_STATUS_INDICATION *status; + + if (data == NULL || data_len < sizeof(*status)) + return; + + wpa_hexdump(MSG_DEBUG, "NDIS: Media Specific Indication", + data, data_len); + + status = (NDIS_802_11_STATUS_INDICATION *) data; + data += sizeof(status); + data_len -= sizeof(status); + + switch (status->StatusType) { + case Ndis802_11StatusType_Authentication: + wpa_driver_ndis_event_auth(drv, data, data_len); + break; + case Ndis802_11StatusType_PMKID_CandidateList: + wpa_driver_ndis_event_pmkid(drv, data, data_len); + break; + default: + wpa_printf(MSG_DEBUG, "NDIS: Unknown StatusType %d", + (int) status->StatusType); + break; + } +} + + +/* Called when an adapter is added */ +void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv) +{ + union wpa_event_data event; + int i; + + wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Arrival"); + + for (i = 0; i < 30; i++) { + /* Re-open Packet32/NDISUIO connection */ + wpa_driver_ndis_adapter_close(drv); + if (wpa_driver_ndis_adapter_init(drv) < 0 || + wpa_driver_ndis_adapter_open(drv) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialization " + "(%d) failed", i); + os_sleep(1, 0); + } else { + wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialized"); + break; + } + } + + os_memset(&event, 0, sizeof(event)); + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_ADDED; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); +} + + +/* Called when an adapter is removed */ +void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv) +{ + union wpa_event_data event; + + wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Removal"); + os_memset(&event, 0, sizeof(event)); + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static void +wpa_driver_ndis_get_wpa_capability(struct wpa_driver_ndis_data *drv) +{ + wpa_printf(MSG_DEBUG, "NDIS: verifying driver WPA capability"); + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPA) { + wpa_printf(MSG_DEBUG, "NDIS: WPA key management supported"); + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA; + } + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPAPSK) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPAPSK) { + wpa_printf(MSG_DEBUG, "NDIS: WPA-PSK key management " + "supported"); + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + } + + if (ndis_set_encr_status(drv, Ndis802_11Encryption3Enabled) == 0 && + ndis_get_encr_status(drv) == Ndis802_11Encryption3KeyAbsent) { + wpa_printf(MSG_DEBUG, "NDIS: CCMP encryption supported"); + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + } + + if (ndis_set_encr_status(drv, Ndis802_11Encryption2Enabled) == 0 && + ndis_get_encr_status(drv) == Ndis802_11Encryption2KeyAbsent) { + wpa_printf(MSG_DEBUG, "NDIS: TKIP encryption supported"); + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + } + + if (ndis_set_encr_status(drv, Ndis802_11Encryption1Enabled) == 0 && + ndis_get_encr_status(drv) == Ndis802_11Encryption1KeyAbsent) { + wpa_printf(MSG_DEBUG, "NDIS: WEP encryption supported"); + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + } + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeShared) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeShared) { + drv->capa.auth |= WPA_DRIVER_AUTH_SHARED; + } + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeOpen) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeOpen) { + drv->capa.auth |= WPA_DRIVER_AUTH_OPEN; + } + + ndis_set_encr_status(drv, Ndis802_11EncryptionDisabled); + + /* Could also verify OID_802_11_ADD_KEY error reporting and + * support for OID_802_11_ASSOCIATION_INFORMATION. */ + + if (drv->capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA && + drv->capa.enc & (WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP)) { + wpa_printf(MSG_DEBUG, "NDIS: driver supports WPA"); + drv->has_capability = 1; + } else { + wpa_printf(MSG_DEBUG, "NDIS: no WPA support found"); + } + + wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x " + "enc 0x%x auth 0x%x", + drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth); +} + + +static void wpa_driver_ndis_get_capability(struct wpa_driver_ndis_data *drv) +{ + char buf[512]; + int len; + size_t i; + NDIS_802_11_CAPABILITY *c; + + drv->capa.flags = WPA_DRIVER_FLAGS_DRIVER_IE; + + len = ndis_get_oid(drv, OID_802_11_CAPABILITY, buf, sizeof(buf)); + if (len < 0) { + wpa_driver_ndis_get_wpa_capability(drv); + return; + } + + wpa_hexdump(MSG_MSGDUMP, "OID_802_11_CAPABILITY", (u8 *) buf, len); + c = (NDIS_802_11_CAPABILITY *) buf; + if (len < sizeof(*c) || c->Version != 2) { + wpa_printf(MSG_DEBUG, "NDIS: unsupported " + "OID_802_11_CAPABILITY data"); + return; + } + wpa_printf(MSG_DEBUG, "NDIS: Driver supports OID_802_11_CAPABILITY - " + "NoOfPMKIDs %d NoOfAuthEncrPairs %d", + (int) c->NoOfPMKIDs, + (int) c->NoOfAuthEncryptPairsSupported); + drv->has_capability = 1; + drv->no_of_pmkid = c->NoOfPMKIDs; + for (i = 0; i < c->NoOfAuthEncryptPairsSupported; i++) { + NDIS_802_11_AUTHENTICATION_ENCRYPTION *ae; + ae = &c->AuthenticationEncryptionSupported[i]; + if ((char *) (ae + 1) > buf + len) { + wpa_printf(MSG_DEBUG, "NDIS: auth/encr pair list " + "overflow"); + break; + } + wpa_printf(MSG_MSGDUMP, "NDIS: %d - auth %d encr %d", + i, (int) ae->AuthModeSupported, + (int) ae->EncryptStatusSupported); + switch (ae->AuthModeSupported) { + case Ndis802_11AuthModeOpen: + drv->capa.auth |= WPA_DRIVER_AUTH_OPEN; + break; + case Ndis802_11AuthModeShared: + drv->capa.auth |= WPA_DRIVER_AUTH_SHARED; + break; + case Ndis802_11AuthModeWPA: + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA; + break; + case Ndis802_11AuthModeWPAPSK: + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + break; + case Ndis802_11AuthModeWPA2: + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2; + break; + case Ndis802_11AuthModeWPA2PSK: + drv->capa.key_mgmt |= + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + break; + case Ndis802_11AuthModeWPANone: + drv->capa.key_mgmt |= + WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE; + break; + default: + break; + } + switch (ae->EncryptStatusSupported) { + case Ndis802_11Encryption1Enabled: + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40; + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP104; + break; + case Ndis802_11Encryption2Enabled: + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + break; + case Ndis802_11Encryption3Enabled: + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + break; + default: + break; + } + } + + wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x " + "enc 0x%x auth 0x%x", + drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth); +} + + +static int wpa_driver_ndis_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_ndis_data *drv = priv; + if (!drv->has_capability) + return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + + +static const char * wpa_driver_ndis_get_ifname(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + return drv->ifname; +} + + +static const u8 * wpa_driver_ndis_get_mac_addr(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + return drv->own_addr; +} + + +#ifdef _WIN32_WCE + +#define NDISUIO_MSG_SIZE (sizeof(NDISUIO_DEVICE_NOTIFICATION) + 512) + +static void ndisuio_notification_receive(void *eloop_data, void *user_ctx) +{ + struct wpa_driver_ndis_data *drv = eloop_data; + NDISUIO_DEVICE_NOTIFICATION *hdr; + u8 buf[NDISUIO_MSG_SIZE]; + DWORD len, flags; + + if (!ReadMsgQueue(drv->event_queue, buf, NDISUIO_MSG_SIZE, &len, 0, + &flags)) { + wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: " + "ReadMsgQueue failed: %d", (int) GetLastError()); + return; + } + + if (len < sizeof(NDISUIO_DEVICE_NOTIFICATION)) { + wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: " + "Too short message (len=%d)", (int) len); + return; + } + + hdr = (NDISUIO_DEVICE_NOTIFICATION *) buf; + wpa_printf(MSG_DEBUG, "NDIS: Notification received: len=%d type=0x%x", + (int) len, hdr->dwNotificationType); + + switch (hdr->dwNotificationType) { +#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL + case NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL: + wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_ARRIVAL"); + wpa_driver_ndis_event_adapter_arrival(drv); + break; +#endif +#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL + case NDISUIO_NOTIFICATION_ADAPTER_REMOVAL: + wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_REMOVAL"); + wpa_driver_ndis_event_adapter_removal(drv); + break; +#endif + case NDISUIO_NOTIFICATION_MEDIA_CONNECT: + wpa_printf(MSG_DEBUG, "NDIS: MEDIA_CONNECT"); + SetEvent(drv->connected_event); + wpa_driver_ndis_event_connect(drv); + break; + case NDISUIO_NOTIFICATION_MEDIA_DISCONNECT: + ResetEvent(drv->connected_event); + wpa_printf(MSG_DEBUG, "NDIS: MEDIA_DISCONNECT"); + wpa_driver_ndis_event_disconnect(drv); + break; + case NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION: + wpa_printf(MSG_DEBUG, "NDIS: MEDIA_SPECIFIC_NOTIFICATION"); +#if _WIN32_WCE == 420 || _WIN32_WCE == 0x420 + wpa_driver_ndis_event_media_specific( + drv, hdr->pvStatusBuffer, hdr->uiStatusBufferSize); +#else + wpa_driver_ndis_event_media_specific( + drv, ((const u8 *) hdr) + hdr->uiOffsetToStatusBuffer, + (size_t) hdr->uiStatusBufferSize); +#endif + break; + default: + wpa_printf(MSG_DEBUG, "NDIS: Unknown notification type 0x%x", + hdr->dwNotificationType); + break; + } +} + + +static void ndisuio_notification_deinit(struct wpa_driver_ndis_data *drv) +{ + NDISUIO_REQUEST_NOTIFICATION req; + + memset(&req, 0, sizeof(req)); + req.hMsgQueue = drv->event_queue; + req.dwNotificationTypes = 0; + + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION, + &req, sizeof(req), NULL, 0, NULL, NULL)) { + wpa_printf(MSG_INFO, "ndisuio_notification_deinit: " + "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d", + (int) GetLastError()); + } + + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_CANCEL_NOTIFICATION, + NULL, 0, NULL, 0, NULL, NULL)) { + wpa_printf(MSG_INFO, "ndisuio_notification_deinit: " + "IOCTL_NDISUIO_CANCEL_NOTIFICATION failed: %d", + (int) GetLastError()); + } + + if (drv->event_queue) { + eloop_unregister_event(drv->event_queue, + sizeof(drv->event_queue)); + CloseHandle(drv->event_queue); + drv->event_queue = NULL; + } + + if (drv->connected_event) { + CloseHandle(drv->connected_event); + drv->connected_event = NULL; + } +} + + +static int ndisuio_notification_init(struct wpa_driver_ndis_data *drv) +{ + MSGQUEUEOPTIONS opt; + NDISUIO_REQUEST_NOTIFICATION req; + + drv->connected_event = + CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected")); + if (drv->connected_event == NULL) { + wpa_printf(MSG_INFO, "ndisuio_notification_init: " + "CreateEvent failed: %d", + (int) GetLastError()); + return -1; + } + + memset(&opt, 0, sizeof(opt)); + opt.dwSize = sizeof(opt); + opt.dwMaxMessages = 5; + opt.cbMaxMessage = NDISUIO_MSG_SIZE; + opt.bReadAccess = TRUE; + + drv->event_queue = CreateMsgQueue(NULL, &opt); + if (drv->event_queue == NULL) { + wpa_printf(MSG_INFO, "ndisuio_notification_init: " + "CreateMsgQueue failed: %d", + (int) GetLastError()); + ndisuio_notification_deinit(drv); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.hMsgQueue = drv->event_queue; + req.dwNotificationTypes = +#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL + NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL | +#endif +#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL + NDISUIO_NOTIFICATION_ADAPTER_REMOVAL | +#endif + NDISUIO_NOTIFICATION_MEDIA_CONNECT | + NDISUIO_NOTIFICATION_MEDIA_DISCONNECT | + NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION; + + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION, + &req, sizeof(req), NULL, 0, NULL, NULL)) { + wpa_printf(MSG_INFO, "ndisuio_notification_init: " + "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d", + (int) GetLastError()); + ndisuio_notification_deinit(drv); + return -1; + } + + eloop_register_event(drv->event_queue, sizeof(drv->event_queue), + ndisuio_notification_receive, drv, NULL); + + return 0; +} +#endif /* _WIN32_WCE */ + + +static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv) +{ +#ifdef CONFIG_USE_NDISUIO + NDISUIO_QUERY_BINDING *b; + size_t blen = sizeof(*b) + 1024; + int i, error, found = 0; + DWORD written; + char name[256], desc[256], *dpos; + WCHAR *pos; + size_t j, len, dlen; + + b = os_malloc(blen); + if (b == NULL) + return -1; + + for (i = 0; ; i++) { + os_memset(b, 0, blen); + b->BindingIndex = i; + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_BINDING, + b, sizeof(NDISUIO_QUERY_BINDING), b, blen, + &written, NULL)) { + error = (int) GetLastError(); + if (error == ERROR_NO_MORE_ITEMS) + break; + wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING " + "failed: %d", error); + break; + } + + pos = (WCHAR *) ((char *) b + b->DeviceNameOffset); + len = b->DeviceNameLength; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + for (j = 0; j < len; j++) + name[j] = (char) pos[j]; + name[len] = '\0'; + + pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset); + len = b->DeviceDescrLength; + if (len >= sizeof(desc)) + len = sizeof(desc) - 1; + for (j = 0; j < len; j++) + desc[j] = (char) pos[j]; + desc[len] = '\0'; + + wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc); + + if (os_strstr(name, drv->ifname)) { + wpa_printf(MSG_DEBUG, "NDIS: Interface name match"); + found = 1; + break; + } + + if (os_strncmp(desc, drv->ifname, os_strlen(drv->ifname)) == 0) + { + wpa_printf(MSG_DEBUG, "NDIS: Interface description " + "match"); + found = 1; + break; + } + } + + if (!found) { + wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'", + drv->ifname); + os_free(b); + return -1; + } + + os_strlcpy(drv->ifname, + os_strncmp(name, "\\DEVICE\\", 8) == 0 ? name + 8 : name, + sizeof(drv->ifname)); +#ifdef _WIN32_WCE + drv->adapter_name = wpa_strdup_tchar(drv->ifname); + if (drv->adapter_name == NULL) { + wpa_printf(MSG_ERROR, "NDIS: Failed to allocate memory for " + "adapter name"); + os_free(b); + return -1; + } +#endif /* _WIN32_WCE */ + + dpos = os_strstr(desc, " - "); + if (dpos) + dlen = dpos - desc; + else + dlen = os_strlen(desc); + drv->adapter_desc = dup_binstr(desc, dlen); + os_free(b); + if (drv->adapter_desc == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'", + drv->adapter_desc); + + return 0; +#else /* CONFIG_USE_NDISUIO */ + PTSTR _names; + char *names, *pos, *pos2; + ULONG len; + BOOLEAN res; +#define MAX_ADAPTERS 32 + char *name[MAX_ADAPTERS]; + char *desc[MAX_ADAPTERS]; + int num_name, num_desc, i, found_name, found_desc; + size_t dlen; + + wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s", + PacketGetVersion()); + + len = 8192; + _names = os_zalloc(len); + if (_names == NULL) + return -1; + + res = PacketGetAdapterNames(_names, &len); + if (!res && len > 8192) { + os_free(_names); + _names = os_zalloc(len); + if (_names == NULL) + return -1; + res = PacketGetAdapterNames(_names, &len); + } + + if (!res) { + wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list " + "(PacketGetAdapterNames)"); + os_free(_names); + return -1; + } + + names = (char *) _names; + if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in " + "UNICODE"); + /* Convert to ASCII */ + pos2 = pos = names; + while (pos2 < names + len) { + if (pos2[0] == '\0' && pos2[1] == '\0' && + pos2[2] == '\0' && pos2[3] == '\0') { + pos2 += 4; + break; + } + *pos++ = pos2[0]; + pos2 += 2; + } + os_memcpy(pos + 2, names, pos - names); + pos += 2; + } else + pos = names; + + num_name = 0; + while (pos < names + len) { + name[num_name] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + os_free(names); + return -1; + } + pos++; + num_name++; + if (num_name >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapters"); + os_free(names); + return -1; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found", + num_name); + pos++; + break; + } + } + + num_desc = 0; + while (pos < names + len) { + desc[num_desc] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + os_free(names); + return -1; + } + pos++; + num_desc++; + if (num_desc >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapter " + "descriptions"); + os_free(names); + return -1; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions " + "found", num_name); + pos++; + break; + } + } + + /* + * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter + * descriptions. Fill in dummy descriptors to work around this. + */ + while (num_desc < num_name) + desc[num_desc++] = "dummy description"; + + if (num_name != num_desc) { + wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and " + "description counts (%d != %d)", + num_name, num_desc); + os_free(names); + return -1; + } + + found_name = found_desc = -1; + for (i = 0; i < num_name; i++) { + wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", + i, name[i], desc[i]); + if (found_name == -1 && os_strstr(name[i], drv->ifname)) + found_name = i; + if (found_desc == -1 && + os_strncmp(desc[i], drv->ifname, os_strlen(drv->ifname)) == + 0) + found_desc = i; + } + + if (found_name < 0 && found_desc >= 0) { + wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on " + "description '%s'", + name[found_desc], desc[found_desc]); + found_name = found_desc; + os_strlcpy(drv->ifname, + os_strncmp(name[found_desc], "\\Device\\NPF_", 12) + == 0 ? name[found_desc] + 12 : name[found_desc], + sizeof(drv->ifname)); + } + + if (found_name < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'", + drv->ifname); + os_free(names); + return -1; + } + + i = found_name; + pos = os_strrchr(desc[i], '('); + if (pos) { + dlen = pos - desc[i]; + pos--; + if (pos > desc[i] && *pos == ' ') + dlen--; + } else { + dlen = os_strlen(desc[i]); + } + drv->adapter_desc = dup_binstr(desc[i], dlen); + os_free(names); + if (drv->adapter_desc == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'", + drv->adapter_desc); + + return 0; +#endif /* CONFIG_USE_NDISUIO */ +} + + +#if defined(CONFIG_NATIVE_WINDOWS) || defined(__CYGWIN__) +#ifndef _WIN32_WCE +/* + * These structures are undocumented for WinXP; only WinCE version is + * documented. These would be included wzcsapi.h if it were available. Some + * changes here have been needed to make the structures match with WinXP SP2. + * It is unclear whether these work with any other version. + */ + +typedef struct { + LPWSTR wszGuid; +} INTF_KEY_ENTRY, *PINTF_KEY_ENTRY; + +typedef struct { + DWORD dwNumIntfs; + PINTF_KEY_ENTRY pIntfs; +} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE; + +typedef struct { + DWORD dwDataLen; + LPBYTE pData; +} RAW_DATA, *PRAW_DATA; + +typedef struct { + LPWSTR wszGuid; + LPWSTR wszDescr; + ULONG ulMediaState; + ULONG ulMediaType; + ULONG ulPhysicalMediaType; + INT nInfraMode; + INT nAuthMode; + INT nWepStatus; +#ifndef _WIN32_WCE + u8 pad[2]; /* why is this needed? */ +#endif /* _WIN32_WCE */ + DWORD dwCtlFlags; + DWORD dwCapabilities; /* something added for WinXP SP2(?) */ + RAW_DATA rdSSID; + RAW_DATA rdBSSID; + RAW_DATA rdBSSIDList; + RAW_DATA rdStSSIDList; + RAW_DATA rdCtrlData; +#ifdef UNDER_CE + BOOL bInitialized; +#endif + DWORD nWPAMCastCipher; + /* add some extra buffer for later additions since this interface is + * far from stable */ + u8 later_additions[100]; +} INTF_ENTRY, *PINTF_ENTRY; + +#define INTF_ALL 0xffffffff +#define INTF_ALL_FLAGS 0x0000ffff +#define INTF_CTLFLAGS 0x00000010 +#define INTFCTL_ENABLED 0x8000 +#endif /* _WIN32_WCE */ + + +#ifdef _WIN32_WCE +static int wpa_driver_ndis_rebind_adapter(struct wpa_driver_ndis_data *drv) +{ + HANDLE ndis; + TCHAR multi[100]; + int len; + + len = _tcslen(drv->adapter_name); + if (len > 80) + return -1; + + ndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL); + if (ndis == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to open file to NDIS " + "device: %d", (int) GetLastError()); + return -1; + } + + len++; + memcpy(multi, drv->adapter_name, len * sizeof(TCHAR)); + memcpy(&multi[len], TEXT("NDISUIO\0"), 9 * sizeof(TCHAR)); + len += 9; + + if (!DeviceIoControl(ndis, IOCTL_NDIS_REBIND_ADAPTER, + multi, len * sizeof(TCHAR), NULL, 0, NULL, NULL)) + { + wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDIS_REBIND_ADAPTER " + "failed: 0x%x", (int) GetLastError()); + wpa_hexdump_ascii(MSG_DEBUG, "NDIS: rebind multi_sz", + (u8 *) multi, len * sizeof(TCHAR)); + CloseHandle(ndis); + return -1; + } + + CloseHandle(ndis); + + wpa_printf(MSG_DEBUG, "NDIS: Requested NDIS rebind of NDISUIO " + "protocol"); + + return 0; +} +#endif /* _WIN32_WCE */ + + +static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv, + int enable) +{ +#ifdef _WIN32_WCE + HKEY hk, hk2; + LONG ret; + DWORD i, hnd, len; + TCHAR keyname[256], devname[256]; + +#define WZC_DRIVER TEXT("Drivers\\BuiltIn\\ZeroConfig") + + if (enable) { + HANDLE h; + h = ActivateDeviceEx(WZC_DRIVER, NULL, 0, NULL); + if (h == INVALID_HANDLE_VALUE || h == 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to re-enable WZC " + "- ActivateDeviceEx failed: %d", + (int) GetLastError()); + return -1; + } + + wpa_printf(MSG_DEBUG, "NDIS: WZC re-enabled"); + return wpa_driver_ndis_rebind_adapter(drv); + } + + /* + * Unfortunately, just disabling the WZC for an interface is not enough + * to free NDISUIO for us, so need to disable and unload WZC completely + * for now when using WinCE with NDISUIO. In addition, must request + * NDISUIO protocol to be rebound to the adapter in order to free the + * NDISUIO binding that WZC hold before us. + */ + + /* Enumerate HKLM\Drivers\Active\* to find a handle to WZC. */ + ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVLOAD_ACTIVE_KEY, 0, 0, &hk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(DEVLOAD_ACTIVE_KEY) " + "failed: %d %d", (int) ret, (int) GetLastError()); + return -1; + } + + for (i = 0; ; i++) { + len = sizeof(keyname); + ret = RegEnumKeyEx(hk, i, keyname, &len, NULL, NULL, NULL, + NULL); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: Could not find active " + "WZC - assuming it is not running."); + RegCloseKey(hk); + return -1; + } + + ret = RegOpenKeyEx(hk, keyname, 0, 0, &hk2); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(active dev) " + "failed: %d %d", + (int) ret, (int) GetLastError()); + continue; + } + + len = sizeof(devname); + ret = RegQueryValueEx(hk2, DEVLOAD_DEVKEY_VALNAME, NULL, NULL, + (LPBYTE) devname, &len); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(" + "DEVKEY_VALNAME) failed: %d %d", + (int) ret, (int) GetLastError()); + RegCloseKey(hk2); + continue; + } + + if (_tcscmp(devname, WZC_DRIVER) == 0) + break; + + RegCloseKey(hk2); + } + + RegCloseKey(hk); + + /* Found WZC - get handle to it. */ + len = sizeof(hnd); + ret = RegQueryValueEx(hk2, DEVLOAD_HANDLE_VALNAME, NULL, NULL, + (PUCHAR) &hnd, &len); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(HANDLE_VALNAME) " + "failed: %d %d", (int) ret, (int) GetLastError()); + RegCloseKey(hk2); + return -1; + } + + RegCloseKey(hk2); + + /* Deactivate WZC */ + if (!DeactivateDevice((HANDLE) hnd)) { + wpa_printf(MSG_DEBUG, "NDIS: DeactivateDevice failed: %d", + (int) GetLastError()); + return -1; + } + + wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily"); + drv->wzc_disabled = 1; + return wpa_driver_ndis_rebind_adapter(drv); + +#else /* _WIN32_WCE */ + + HMODULE hm; + DWORD (WINAPI *wzc_enum_interf)(LPWSTR pSrvAddr, + PINTFS_KEY_TABLE pIntfs); + DWORD (WINAPI *wzc_query_interf)(LPWSTR pSrvAddr, DWORD dwInFlags, + PINTF_ENTRY pIntf, + LPDWORD pdwOutFlags); + DWORD (WINAPI *wzc_set_interf)(LPWSTR pSrvAddr, DWORD dwInFlags, + PINTF_ENTRY pIntf, LPDWORD pdwOutFlags); + int ret = -1, j; + DWORD res; + INTFS_KEY_TABLE guids; + INTF_ENTRY intf; + char guid[128]; + WCHAR *pos; + DWORD flags, i; + + hm = LoadLibrary(TEXT("wzcsapi.dll")); + if (hm == NULL) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to load wzcsapi.dll (%u) " + "- WZC probably not running", + (unsigned int) GetLastError()); + return -1; + } + +#ifdef _WIN32_WCE + wzc_enum_interf = (void *) GetProcAddressA(hm, "WZCEnumInterfaces"); + wzc_query_interf = (void *) GetProcAddressA(hm, "WZCQueryInterface"); + wzc_set_interf = (void *) GetProcAddressA(hm, "WZCSetInterface"); +#else /* _WIN32_WCE */ + wzc_enum_interf = (void *) GetProcAddress(hm, "WZCEnumInterfaces"); + wzc_query_interf = (void *) GetProcAddress(hm, "WZCQueryInterface"); + wzc_set_interf = (void *) GetProcAddress(hm, "WZCSetInterface"); +#endif /* _WIN32_WCE */ + + if (wzc_enum_interf == NULL || wzc_query_interf == NULL || + wzc_set_interf == NULL) { + wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces, " + "WZCQueryInterface, or WZCSetInterface not found " + "in wzcsapi.dll"); + goto fail; + } + + os_memset(&guids, 0, sizeof(guids)); + res = wzc_enum_interf(NULL, &guids); + if (res != 0) { + wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces failed: %d; " + "WZC service is apparently not running", + (int) res); + goto fail; + } + + wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces: %d interfaces", + (int) guids.dwNumIntfs); + + for (i = 0; i < guids.dwNumIntfs; i++) { + pos = guids.pIntfs[i].wszGuid; + for (j = 0; j < sizeof(guid); j++) { + guid[j] = (char) *pos; + if (*pos == 0) + break; + pos++; + } + guid[sizeof(guid) - 1] = '\0'; + wpa_printf(MSG_DEBUG, "NDIS: intfs %d GUID '%s'", + (int) i, guid); + if (os_strstr(drv->ifname, guid) == NULL) + continue; + + wpa_printf(MSG_DEBUG, "NDIS: Current interface found from " + "WZC"); + break; + } + + if (i >= guids.dwNumIntfs) { + wpa_printf(MSG_DEBUG, "NDIS: Current interface not found from " + "WZC"); + goto fail; + } + + os_memset(&intf, 0, sizeof(intf)); + intf.wszGuid = guids.pIntfs[i].wszGuid; + /* Set flags to verify that the structure has not changed. */ + intf.dwCtlFlags = -1; + flags = 0; + res = wzc_query_interf(NULL, INTFCTL_ENABLED, &intf, &flags); + if (res != 0) { + wpa_printf(MSG_DEBUG, "NDIS: Could not query flags for the " + "WZC interface: %d (0x%x)", + (int) res, (int) res); + wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u", + (unsigned int) GetLastError()); + goto fail; + } + + wpa_printf(MSG_DEBUG, "NDIS: WZC interface flags 0x%x dwCtlFlags 0x%x", + (int) flags, (int) intf.dwCtlFlags); + + if (intf.dwCtlFlags == -1) { + wpa_printf(MSG_DEBUG, "NDIS: Looks like wzcsapi has changed " + "again - could not disable WZC"); + wpa_hexdump(MSG_MSGDUMP, "NDIS: intf", + (u8 *) &intf, sizeof(intf)); + goto fail; + } + + if (enable) { + if (!(intf.dwCtlFlags & INTFCTL_ENABLED)) { + wpa_printf(MSG_DEBUG, "NDIS: Enabling WZC for this " + "interface"); + intf.dwCtlFlags |= INTFCTL_ENABLED; + res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf, + &flags); + if (res != 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to enable " + "WZC: %d (0x%x)", + (int) res, (int) res); + wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u", + (unsigned int) GetLastError()); + goto fail; + } + wpa_printf(MSG_DEBUG, "NDIS: Re-enabled WZC for this " + "interface"); + drv->wzc_disabled = 0; + } + } else { + if (intf.dwCtlFlags & INTFCTL_ENABLED) { + wpa_printf(MSG_DEBUG, "NDIS: Disabling WZC for this " + "interface"); + intf.dwCtlFlags &= ~INTFCTL_ENABLED; + res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf, + &flags); + if (res != 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to " + "disable WZC: %d (0x%x)", + (int) res, (int) res); + wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u", + (unsigned int) GetLastError()); + goto fail; + } + wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily " + "for this interface"); + drv->wzc_disabled = 1; + } else { + wpa_printf(MSG_DEBUG, "NDIS: WZC was not enabled for " + "this interface"); + } + } + + ret = 0; + +fail: + FreeLibrary(hm); + + return ret; +#endif /* _WIN32_WCE */ +} + +#else /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */ + +static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv, + int enable) +{ + return 0; +} + +#endif /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */ + + +#ifdef CONFIG_USE_NDISUIO +/* + * l2_packet_ndis.c is sharing the same handle to NDISUIO, so we must be able + * to export this handle. This is somewhat ugly, but there is no better + * mechanism available to pass data from driver interface to l2_packet wrapper. + */ +static HANDLE driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE; + +HANDLE driver_ndis_get_ndisuio_handle(void) +{ + return driver_ndis_ndisuio_handle; +} +#endif /* CONFIG_USE_NDISUIO */ + + +static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv) +{ +#ifdef CONFIG_USE_NDISUIO +#ifndef _WIN32_WCE +#define NDISUIO_DEVICE_NAME TEXT("\\\\.\\\\Ndisuio") + DWORD written; +#endif /* _WIN32_WCE */ + drv->ndisuio = CreateFile(NDISUIO_DEVICE_NAME, + GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + INVALID_HANDLE_VALUE); + if (drv->ndisuio == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to " + "NDISUIO: %d", (int) GetLastError()); + return -1; + } + driver_ndis_ndisuio_handle = drv->ndisuio; + +#ifndef _WIN32_WCE + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, + NULL, 0, &written, NULL)) { + wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: " + "%d", (int) GetLastError()); + CloseHandle(drv->ndisuio); + drv->ndisuio = INVALID_HANDLE_VALUE; + return -1; + } +#endif /* _WIN32_WCE */ + + return 0; +#else /* CONFIG_USE_NDISUIO */ + return 0; +#endif /* CONFIG_USE_NDISUIO */ +} + + +static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv) +{ +#ifdef CONFIG_USE_NDISUIO + DWORD written; +#define MAX_NDIS_DEVICE_NAME_LEN 256 + WCHAR ifname[MAX_NDIS_DEVICE_NAME_LEN]; + size_t len, i, pos; + const char *prefix = "\\DEVICE\\"; + +#ifdef _WIN32_WCE + pos = 0; +#else /* _WIN32_WCE */ + pos = 8; +#endif /* _WIN32_WCE */ + len = pos + os_strlen(drv->ifname); + if (len >= MAX_NDIS_DEVICE_NAME_LEN) + return -1; + for (i = 0; i < pos; i++) + ifname[i] = (WCHAR) prefix[i]; + for (i = pos; i < len; i++) + ifname[i] = (WCHAR) drv->ifname[i - pos]; + ifname[i] = L'\0'; + + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_OPEN_DEVICE, + ifname, len * sizeof(WCHAR), NULL, 0, &written, + NULL)) { + wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_OPEN_DEVICE " + "failed: %d", (int) GetLastError()); + wpa_hexdump_ascii(MSG_DEBUG, "NDIS: ifname", + (const u8 *) ifname, len * sizeof(WCHAR)); + CloseHandle(drv->ndisuio); + drv->ndisuio = INVALID_HANDLE_VALUE; + return -1; + } + + wpa_printf(MSG_DEBUG, "NDIS: Opened NDISUIO device successfully"); + + return 0; +#else /* CONFIG_USE_NDISUIO */ + char ifname[128]; + os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", drv->ifname); + drv->adapter = PacketOpenAdapter(ifname); + if (drv->adapter == NULL) { + wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for " + "'%s'", ifname); + return -1; + } + return 0; +#endif /* CONFIG_USE_NDISUIO */ +} + + +static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv) +{ +#ifdef CONFIG_USE_NDISUIO + driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE; + if (drv->ndisuio != INVALID_HANDLE_VALUE) + CloseHandle(drv->ndisuio); +#else /* CONFIG_USE_NDISUIO */ + if (drv->adapter) + PacketCloseAdapter(drv->adapter); +#endif /* CONFIG_USE_NDISUIO */ +} + + +static int ndis_add_multicast(struct wpa_driver_ndis_data *drv) +{ + if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST, + (const char *) pae_group_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address " + "to the multicast list"); + return -1; + } + + return 0; +} + + +static void * wpa_driver_ndis_init(void *ctx, const char *ifname) +{ + struct wpa_driver_ndis_data *drv; + u32 mode; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + /* + * Compatibility code to strip possible prefix from the GUID. Previous + * versions include \Device\NPF_ prefix for all names, but the internal + * interface name is now only the GUI. Both Packet32 and NDISUIO + * prefixes are supported. + */ + if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0) + ifname += 12; + else if (os_strncmp(ifname, "\\DEVICE\\", 8) == 0) + ifname += 8; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + if (wpa_driver_ndis_adapter_init(drv) < 0) { + os_free(drv); + return NULL; + } + + if (wpa_driver_ndis_get_names(drv) < 0) { + wpa_driver_ndis_adapter_close(drv); + os_free(drv); + return NULL; + } + + wpa_driver_ndis_set_wzc(drv, 0); + + if (wpa_driver_ndis_adapter_open(drv) < 0) { + wpa_driver_ndis_adapter_close(drv); + os_free(drv); + return NULL; + } + + if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS, + (char *) drv->own_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS " + "failed"); + wpa_driver_ndis_adapter_close(drv); + os_free(drv); + return NULL; + } + wpa_driver_ndis_get_capability(drv); + + /* Make sure that the driver does not have any obsolete PMKID entries. + */ + wpa_driver_ndis_flush_pmkid(drv); + + /* + * Disconnect to make sure that driver re-associates if it was + * connected. + */ + wpa_driver_ndis_disconnect(drv); + + eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL); + +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED + drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail, + drv->ifname, drv->adapter_desc); + if (drv->events == NULL) { + wpa_driver_ndis_deinit(drv); + return NULL; + } + eloop_register_event(drv->event_avail, sizeof(drv->event_avail), + wpa_driver_ndis_event_pipe_cb, drv, NULL); +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + +#ifdef _WIN32_WCE + if (ndisuio_notification_init(drv) < 0) { + wpa_driver_ndis_deinit(drv); + return NULL; + } +#endif /* _WIN32_WCE */ + + /* Set mode here in case card was configured for ad-hoc mode + * previously. */ + mode = Ndis802_11Infrastructure; + if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, + (char *) &mode, sizeof(mode)) < 0) { + char buf[8]; + int res; + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_INFRASTRUCTURE_MODE (%d)", + (int) mode); + /* Try to continue anyway */ + + res = ndis_get_oid(drv, OID_DOT11_CURRENT_OPERATION_MODE, buf, + sizeof(buf)); + if (res > 0) { + wpa_printf(MSG_INFO, "NDIS: The driver seems to use " + "Native 802.11 OIDs. These are not yet " + "fully supported."); + drv->native80211 = 1; + } else if (!drv->has_capability || drv->capa.enc == 0) { + /* + * Note: This will also happen with NDIS 6 drivers with + * Vista. + */ + wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide " + "any wireless capabilities - assume it is " + "a wired interface"); + drv->wired = 1; + drv->capa.flags |= WPA_DRIVER_FLAGS_WIRED; + drv->has_capability = 1; + ndis_add_multicast(drv); + } + } + + return drv; +} + + +static void wpa_driver_ndis_deinit(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED + if (drv->events) { + eloop_unregister_event(drv->event_avail, + sizeof(drv->event_avail)); + ndis_events_deinit(drv->events); + } +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + +#ifdef _WIN32_WCE + ndisuio_notification_deinit(drv); +#endif /* _WIN32_WCE */ + + eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL); + wpa_driver_ndis_flush_pmkid(drv); + wpa_driver_ndis_disconnect(drv); + if (wpa_driver_ndis_radio_off(drv) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn " + "radio off"); + } + + wpa_driver_ndis_adapter_close(drv); + + if (drv->wzc_disabled) + wpa_driver_ndis_set_wzc(drv, 1); + +#ifdef _WIN32_WCE + os_free(drv->adapter_name); +#endif /* _WIN32_WCE */ + os_free(drv->adapter_desc); + os_free(drv); +} + + +static struct wpa_interface_info * +wpa_driver_ndis_get_interfaces(void *global_priv) +{ + struct wpa_interface_info *iface = NULL, *niface; + +#ifdef CONFIG_USE_NDISUIO + NDISUIO_QUERY_BINDING *b; + size_t blen = sizeof(*b) + 1024; + int i, error; + DWORD written; + char name[256], desc[256]; + WCHAR *pos; + size_t j, len; + HANDLE ndisuio; + + ndisuio = CreateFile(NDISUIO_DEVICE_NAME, + GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + INVALID_HANDLE_VALUE); + if (ndisuio == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to " + "NDISUIO: %d", (int) GetLastError()); + return NULL; + } + +#ifndef _WIN32_WCE + if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, + NULL, 0, &written, NULL)) { + wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: " + "%d", (int) GetLastError()); + CloseHandle(ndisuio); + return NULL; + } +#endif /* _WIN32_WCE */ + + b = os_malloc(blen); + if (b == NULL) { + CloseHandle(ndisuio); + return NULL; + } + + for (i = 0; ; i++) { + os_memset(b, 0, blen); + b->BindingIndex = i; + if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING, + b, sizeof(NDISUIO_QUERY_BINDING), b, blen, + &written, NULL)) { + error = (int) GetLastError(); + if (error == ERROR_NO_MORE_ITEMS) + break; + wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING " + "failed: %d", error); + break; + } + + pos = (WCHAR *) ((char *) b + b->DeviceNameOffset); + len = b->DeviceNameLength; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + for (j = 0; j < len; j++) + name[j] = (char) pos[j]; + name[len] = '\0'; + + pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset); + len = b->DeviceDescrLength; + if (len >= sizeof(desc)) + len = sizeof(desc) - 1; + for (j = 0; j < len; j++) + desc[j] = (char) pos[j]; + desc[len] = '\0'; + + wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc); + + niface = os_zalloc(sizeof(*niface)); + if (niface == NULL) + break; + niface->drv_name = "ndis"; + if (os_strncmp(name, "\\DEVICE\\", 8) == 0) + niface->ifname = os_strdup(name + 8); + else + niface->ifname = os_strdup(name); + if (niface->ifname == NULL) { + os_free(niface); + break; + } + niface->desc = os_strdup(desc); + niface->next = iface; + iface = niface; + } + + os_free(b); + CloseHandle(ndisuio); +#else /* CONFIG_USE_NDISUIO */ + PTSTR _names; + char *names, *pos, *pos2; + ULONG len; + BOOLEAN res; + char *name[MAX_ADAPTERS]; + char *desc[MAX_ADAPTERS]; + int num_name, num_desc, i; + + wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s", + PacketGetVersion()); + + len = 8192; + _names = os_zalloc(len); + if (_names == NULL) + return NULL; + + res = PacketGetAdapterNames(_names, &len); + if (!res && len > 8192) { + os_free(_names); + _names = os_zalloc(len); + if (_names == NULL) + return NULL; + res = PacketGetAdapterNames(_names, &len); + } + + if (!res) { + wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list " + "(PacketGetAdapterNames)"); + os_free(_names); + return NULL; + } + + names = (char *) _names; + if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in " + "UNICODE"); + /* Convert to ASCII */ + pos2 = pos = names; + while (pos2 < names + len) { + if (pos2[0] == '\0' && pos2[1] == '\0' && + pos2[2] == '\0' && pos2[3] == '\0') { + pos2 += 4; + break; + } + *pos++ = pos2[0]; + pos2 += 2; + } + os_memcpy(pos + 2, names, pos - names); + pos += 2; + } else + pos = names; + + num_name = 0; + while (pos < names + len) { + name[num_name] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + os_free(names); + return NULL; + } + pos++; + num_name++; + if (num_name >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapters"); + os_free(names); + return NULL; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found", + num_name); + pos++; + break; + } + } + + num_desc = 0; + while (pos < names + len) { + desc[num_desc] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + os_free(names); + return NULL; + } + pos++; + num_desc++; + if (num_desc >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapter " + "descriptions"); + os_free(names); + return NULL; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions " + "found", num_name); + pos++; + break; + } + } + + /* + * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter + * descriptions. Fill in dummy descriptors to work around this. + */ + while (num_desc < num_name) + desc[num_desc++] = "dummy description"; + + if (num_name != num_desc) { + wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and " + "description counts (%d != %d)", + num_name, num_desc); + os_free(names); + return NULL; + } + + for (i = 0; i < num_name; i++) { + niface = os_zalloc(sizeof(*niface)); + if (niface == NULL) + break; + niface->drv_name = "ndis"; + if (os_strncmp(name[i], "\\Device\\NPF_", 12) == 0) + niface->ifname = os_strdup(name[i] + 12); + else + niface->ifname = os_strdup(name[i]); + if (niface->ifname == NULL) { + os_free(niface); + break; + } + niface->desc = os_strdup(desc[i]); + niface->next = iface; + iface = niface; + } + +#endif /* CONFIG_USE_NDISUIO */ + + return iface; +} + + +static const char *ndis_drv_name = "ndis"; +static const char *ndis_drv_desc = "Windows NDIS driver"; + +struct wpa_driver_ops wpa_driver_ndis_ops; + +void driver_ndis_init_ops(void) +{ + os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops)); + wpa_driver_ndis_ops.name = ndis_drv_name; + wpa_driver_ndis_ops.desc = ndis_drv_desc; + wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid; + wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid; + wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key; + wpa_driver_ndis_ops.init = wpa_driver_ndis_init; + wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit; + wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate; + wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate; + wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid; + wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid; + wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid; + wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa; + wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll; + wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname; + wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr; + wpa_driver_ndis_ops.get_scan_results2 = + wpa_driver_ndis_get_scan_results; + wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces; + wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan; +} diff --git a/freebsd/contrib/wpa/src/drivers/driver_ndis.h b/freebsd/contrib/wpa/src/drivers/driver_ndis.h new file mode 100644 index 00000000..89d136d3 --- /dev/null +++ b/freebsd/contrib/wpa/src/drivers/driver_ndis.h @@ -0,0 +1,59 @@ +/* + * WPA Supplicant - Windows/NDIS driver interface + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DRIVER_NDIS_H +#define DRIVER_NDIS_H + +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED +struct ndis_events_data; +struct ndis_events_data * ndis_events_init(HANDLE *read_pipe, HANDLE *event, + const char *ifname, + const char *desc); +void ndis_events_deinit(struct ndis_events_data *events); +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + +struct ndis_pmkid_entry { + struct ndis_pmkid_entry *next; + u8 bssid[ETH_ALEN]; + u8 pmkid[16]; +}; + +struct wpa_driver_ndis_data { + void *ctx; + char ifname[100]; /* GUID: {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D} */ +#ifdef _WIN32_WCE + TCHAR *adapter_name; + HANDLE event_queue; /* NDISUIO notifier MsgQueue */ + HANDLE connected_event; /* WpaSupplicantConnected event */ +#endif /* _WIN32_WCE */ + u8 own_addr[ETH_ALEN]; +#ifdef CONFIG_USE_NDISUIO + HANDLE ndisuio; +#else /* CONFIG_USE_NDISUIO */ + LPADAPTER adapter; +#endif /* CONFIG_USE_NDISUIO */ + u8 bssid[ETH_ALEN]; + + int has_capability; + int no_of_pmkid; + int radio_enabled; + struct wpa_driver_capa capa; + struct ndis_pmkid_entry *pmkid; + char *adapter_desc; + int wired; + int native80211; + int mode; + int wzc_disabled; + int oid_bssid_set; +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED + HANDLE events_pipe, event_avail; + struct ndis_events_data *events; +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ +}; + +#endif /* DRIVER_NDIS_H */ diff --git a/freebsd/contrib/wpa/src/drivers/driver_nl80211.h b/freebsd/contrib/wpa/src/drivers/driver_nl80211.h new file mode 100644 index 00000000..5c21e0fa --- /dev/null +++ b/freebsd/contrib/wpa/src/drivers/driver_nl80211.h @@ -0,0 +1,277 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - definitions + * Copyright (c) 2002-2014, 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 software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DRIVER_NL80211_H +#define DRIVER_NL80211_H + +#include "nl80211_copy.h" +#include "utils/list.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 +#endif /* CONFIG_LIBNL20 */ + +struct nl80211_global { + struct dl_list interfaces; + int if_add_ifindex; + u64 if_add_wdevid; + int if_add_wdevid_set; + struct netlink_data *netlink; + struct nl_cb *nl_cb; + struct nl_handle *nl; + int nl80211_id; + int ioctl_sock; /* socket for ioctl() use */ + + struct nl_handle *nl_event; +}; + +struct nl80211_wiphy_data { + struct dl_list list; + struct dl_list bsss; + struct dl_list drvs; + + struct nl_handle *nl_beacons; + struct nl_cb *nl_cb; + + int wiphy_idx; +}; + +struct i802_bss { + struct wpa_driver_nl80211_data *drv; + struct i802_bss *next; + int ifindex; + int br_ifindex; + u64 wdev_id; + char ifname[IFNAMSIZ + 1]; + char brname[IFNAMSIZ]; + unsigned int beacon_set:1; + unsigned int added_if_into_bridge:1; + unsigned int added_bridge:1; + unsigned int in_deinit:1; + unsigned int wdev_id_set:1; + unsigned int added_if:1; + unsigned int static_ap:1; + + u8 addr[ETH_ALEN]; + + int freq; + int bandwidth; + int if_dynamic; + + void *ctx; + struct nl_handle *nl_preq, *nl_mgmt; + struct nl_cb *nl_cb; + + struct nl80211_wiphy_data *wiphy_data; + struct dl_list wiphy_list; +}; + +struct wpa_driver_nl80211_data { + struct nl80211_global *global; + struct dl_list list; + struct dl_list wiphy_list; + char phyname[32]; + u8 perm_addr[ETH_ALEN]; + void *ctx; + int ifindex; + int if_removed; + int if_disabled; + int ignore_if_down_event; + struct rfkill_data *rfkill; + struct wpa_driver_capa capa; + u8 *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; + int has_capability; + + int operstate; + + int scan_complete_events; + enum scan_states { + NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED, + SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED, + SCHED_SCAN_RESULTS + } scan_state; + + u8 auth_bssid[ETH_ALEN]; + u8 auth_attempt_bssid[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u8 prev_bssid[ETH_ALEN]; + int associated; + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; + enum nl80211_iftype nlmode; + enum nl80211_iftype ap_scan_as_station; + unsigned int assoc_freq; + + int monitor_sock; + int monitor_ifidx; + int monitor_refcount; + + unsigned int disabled_11b_rates:1; + unsigned int pending_remain_on_chan:1; + unsigned int in_interface_list:1; + unsigned int device_ap_sme:1; + unsigned int poll_command_supported:1; + unsigned int data_tx_status:1; + unsigned int scan_for_auth:1; + unsigned int retry_auth:1; + unsigned int use_monitor:1; + unsigned int ignore_next_local_disconnect:1; + unsigned int ignore_next_local_deauth:1; + unsigned int hostapd:1; + unsigned int start_mode_ap:1; + unsigned int start_iface_up:1; + unsigned int test_use_roc_tx:1; + unsigned int ignore_deauth_event:1; + unsigned int vendor_cmd_test_avail:1; + unsigned int roaming_vendor_cmd_avail:1; + unsigned int dfs_vendor_cmd_avail:1; + unsigned int have_low_prio_scan:1; + unsigned int force_connect_cmd:1; + unsigned int addr_changed:1; + unsigned int get_features_vendor_cmd_avail:1; + unsigned int set_rekey_offload:1; + unsigned int p2p_go_ctwindow_supported:1; + unsigned int setband_vendor_cmd_avail:1; + unsigned int get_pref_freq_list:1; + unsigned int set_prob_oper_freq:1; + + u64 remain_on_chan_cookie; + u64 send_action_cookie; + + unsigned int last_mgmt_freq; + + struct wpa_driver_scan_filter *filter_ssids; + size_t num_filter_ssids; + + struct i802_bss *first_bss; + + int eapol_tx_sock; + + int eapol_sock; /* socket for EAPOL frames */ + + struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */ + + int default_if_indices[16]; + int *if_indices; + int num_if_indices; + + /* From failed authentication command */ + int auth_freq; + u8 auth_bssid_[ETH_ALEN]; + u8 auth_ssid[SSID_MAX_LEN]; + size_t auth_ssid_len; + int auth_alg; + u8 *auth_ie; + size_t auth_ie_len; + u8 auth_wep_key[4][16]; + size_t auth_wep_key_len[4]; + int auth_wep_tx_keyidx; + int auth_local_state_change; + int auth_p2p; +}; + +struct nl_msg; + +void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, int flags, uint8_t cmd); +struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd); +struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags, + uint8_t cmd); +struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd); +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); +int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, + const char *ifname, enum nl80211_iftype iftype, + const u8 *addr, int wds, + int (*handler)(struct nl_msg *, void *), + void *arg, int use_existing); +void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx); +unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv); +enum chan_width convert2width(int width); +void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv); +struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv, + int ifindex); +int is_ap_interface(enum nl80211_iftype nlmode); +int is_sta_interface(enum nl80211_iftype nlmode); +int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv); +int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig); +int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig_change); +int nl80211_get_wiphy_index(struct i802_bss *bss); +int wpa_driver_nl80211_set_mode(struct i802_bss *bss, + enum nl80211_iftype nlmode); +int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, + const u8 *addr, int cmd, u16 reason_code, + int local_state_change); + +int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv); +void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv); +int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv, + const void *data, size_t len, + int encrypt, int noack); + +int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv); +struct hostapd_hw_modes * +nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags); + +int process_global_event(struct nl_msg *msg, void *arg); +int process_bss_event(struct nl_msg *msg, void *arg); + +#ifdef ANDROID +int android_nl_socket_set_nonblocking(struct nl_handle *handle); +int android_pno_start(struct i802_bss *bss, + struct wpa_driver_scan_params *params); +int android_pno_stop(struct i802_bss *bss); +extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, + size_t buf_len); + +#ifdef ANDROID_P2P +int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration); +int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len); +int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow); +int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp); +#endif /* ANDROID_P2P */ +#endif /* ANDROID */ + + +/* driver_nl80211_scan.c */ + +struct nl80211_bss_info_arg { + struct wpa_driver_nl80211_data *drv; + struct wpa_scan_results *res; + unsigned int assoc_freq; + unsigned int ibss_freq; + u8 assoc_bssid[ETH_ALEN]; +}; + +int bss_info_handler(struct nl_msg *msg, void *arg); +void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); +int wpa_driver_nl80211_scan(struct i802_bss *bss, + struct wpa_driver_scan_params *params); +int wpa_driver_nl80211_sched_scan(void *priv, + struct wpa_driver_scan_params *params, + u32 interval); +int wpa_driver_nl80211_stop_sched_scan(void *priv); +struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv); +void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv); +const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie); + +#endif /* DRIVER_NL80211_H */ diff --git a/freebsd/contrib/wpa/src/drivers/driver_wired.c b/freebsd/contrib/wpa/src/drivers/driver_wired.c new file mode 100644 index 00000000..b30d47cf --- /dev/null +++ b/freebsd/contrib/wpa/src/drivers/driver_wired.c @@ -0,0 +1,681 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Wired Ethernet driver interface + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Gunter Burchardt <tira@isx.de> + * + * This software may be distributed under the terms of the BSD license. + * See README 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> +#include <net/if_media.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) { + wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno)); + 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) { + wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); + 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) { + wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); + 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) { + wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s", + strerror(errno)); + return -1; + } + + if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) { + wpa_printf(MSG_INFO, "Could not register read socket"); + 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) { + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", + strerror(errno)); + 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) { + wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); + 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) { + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s", + strerror(errno)); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x", + 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) { + wpa_printf(MSG_ERROR, "socket call failed for dhcp: %s", + strerror(errno)); + return -1; + } + + if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx, + NULL)) { + wpa_printf(MSG_INFO, "Could not register read socket"); + 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) { + wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_REUSEADDR]: %s", + strerror(errno)); + return -1; + } + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n, + sizeof(n)) == -1) { + wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_BROADCAST]: %s", + strerror(errno)); + 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) { + wpa_printf(MSG_ERROR, + "setsockopt[SOL_SOCKET,SO_BINDTODEVICE]: %s", + strerror(errno)); + return -1; + } + + if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2, + sizeof(struct sockaddr)) == -1) { + wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); + 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) { + wpa_printf(MSG_INFO, + "malloc() failed for wired_send_eapol(len=%lu)", + (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) { + wpa_printf(MSG_ERROR, + "wired_send_eapol - packet len: %lu - failed: send: %s", + (unsigned long) len, strerror(errno)); + } + + 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) { + wpa_printf(MSG_INFO, + "Could not allocate memory for wired driver data"); + 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) { + eloop_unregister_read_sock(drv->sock); + close(drv->sock); + } + + if (drv->dhcp_sock >= 0) { + eloop_unregister_read_sock(drv->dhcp_sock); + 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) { + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", + strerror(errno)); + 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) { + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); + 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) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", + strerror(errno)); + close(s); + return -1; + } + close(s); + return 0; +} + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status) +{ + struct ifmediareq ifmr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); + return -1; + } + + os_memset(&ifmr, 0, sizeof(ifmr)); + os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s", + strerror(errno)); + close(s); + return -1; + } + close(s); + *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID); + + return 0; +} +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ + + +static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) +{ + struct ifreq ifr; + int s; + +#ifdef __sun__ + return -1; +#endif /* __sun__ */ + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); + 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) { + wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s", + strerror(errno)); + 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, status; + + 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) + wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno)); +#else /* __linux__ */ + drv->pf_sock = -1; +#endif /* __linux__ */ + + if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 && + !(flags & IFF_UP) && + wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) { + drv->iff_up = 1; + } + + if (wired_multicast_membership(drv->pf_sock, + if_nametoindex(drv->ifname), + pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " + "packet socket", __func__); + drv->membership = 1; + } else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " + "SIOCADDMULTI", __func__); + drv->multi = 1; + } else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) { + wpa_printf(MSG_INFO, "%s: Could not get interface " + "flags", __func__); + os_free(drv); + return NULL; + } else if (flags & IFF_ALLMULTI) { + wpa_printf(MSG_DEBUG, "%s: Interface is already configured " + "for multicast", __func__); + } else if (wpa_driver_wired_set_ifflags(ifname, + flags | IFF_ALLMULTI) < 0) { + wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", + __func__); + os_free(drv); + return NULL; + } else { + wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", + __func__); + drv->iff_allmulti = 1; + } +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + { + int status; + wpa_printf(MSG_DEBUG, "%s: waiting for link to become active", + __func__); + while (wpa_driver_wired_get_ifstatus(ifname, &status) == 0 && + status == 0) + sleep(1); + } +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ + + return drv; +} + + +static void wpa_driver_wired_deinit(void *priv) +{ + struct wpa_driver_wired_data *drv = priv; + int flags; + + if (drv->membership && + wired_multicast_membership(drv->pf_sock, + if_nametoindex(drv->ifname), + pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " + "group (PACKET)", __func__); + } + + if (drv->multi && + wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " + "group (SIOCDELMULTI)", __func__); + } + + if (drv->iff_allmulti && + (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 || + wpa_driver_wired_set_ifflags(drv->ifname, + flags & ~IFF_ALLMULTI) < 0)) { + wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", + __func__); + } + + if (drv->iff_up && + wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 && + (flags & IFF_UP) && + wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", + __func__); + } + + if (drv->pf_sock != -1) + close(drv->pf_sock); + + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_wired_ops = { + .name = "wired", + .desc = "Wired Ethernet driver", + .hapd_init = wired_driver_hapd_init, + .hapd_deinit = wired_driver_hapd_deinit, + .hapd_send_eapol = wired_send_eapol, + .get_ssid = wpa_driver_wired_get_ssid, + .get_bssid = wpa_driver_wired_get_bssid, + .get_capa = wpa_driver_wired_get_capa, + .init = wpa_driver_wired_init, + .deinit = wpa_driver_wired_deinit, +}; diff --git a/freebsd/contrib/wpa/src/drivers/drivers.c b/freebsd/contrib/wpa/src/drivers/drivers.c new file mode 100644 index 00000000..5a0b58c2 --- /dev/null +++ b/freebsd/contrib/wpa/src/drivers/drivers.c @@ -0,0 +1,88 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Driver interface list + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include "utils/common.h" +#include "driver.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_BSD +extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_OPENBSD +extern struct wpa_driver_ops wpa_driver_openbsd_ops; /* driver_openbsd.c */ +#endif /* CONFIG_DRIVER_OPENBSD */ +#ifdef CONFIG_DRIVER_NDIS +extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ +#endif /* CONFIG_DRIVER_NDIS */ +#ifdef CONFIG_DRIVER_WIRED +extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ +#endif /* CONFIG_DRIVER_WIRED */ +#ifdef CONFIG_DRIVER_MACSEC_QCA + /* driver_macsec_qca.c */ +extern struct wpa_driver_ops wpa_driver_macsec_qca_ops; +#endif /* CONFIG_DRIVER_MACSEC_QCA */ +#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 */ + + +const struct wpa_driver_ops *const wpa_drivers[] = +{ +#ifdef CONFIG_DRIVER_NL80211 + &wpa_driver_nl80211_ops, +#endif /* CONFIG_DRIVER_NL80211 */ +#ifdef CONFIG_DRIVER_WEXT + &wpa_driver_wext_ops, +#endif /* CONFIG_DRIVER_WEXT */ +#ifdef CONFIG_DRIVER_HOSTAP + &wpa_driver_hostap_ops, +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_BSD + &wpa_driver_bsd_ops, +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_OPENBSD + &wpa_driver_openbsd_ops, +#endif /* CONFIG_DRIVER_OPENBSD */ +#ifdef CONFIG_DRIVER_NDIS + &wpa_driver_ndis_ops, +#endif /* CONFIG_DRIVER_NDIS */ +#ifdef CONFIG_DRIVER_WIRED + &wpa_driver_wired_ops, +#endif /* CONFIG_DRIVER_WIRED */ +#ifdef CONFIG_DRIVER_MACSEC_QCA + &wpa_driver_macsec_qca_ops, +#endif /* CONFIG_DRIVER_MACSEC_QCA */ +#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/freebsd/contrib/wpa/src/drivers/linux_defines.h b/freebsd/contrib/wpa/src/drivers/linux_defines.h new file mode 100644 index 00000000..a107479a --- /dev/null +++ b/freebsd/contrib/wpa/src/drivers/linux_defines.h @@ -0,0 +1,46 @@ +/* + * Linux defines for values that are not yet included in common C libraries + * Copyright (c) 2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef LINUX_DEFINES_H +#define LINUX_DEFINES_H + +#ifndef SO_WIFI_STATUS +# if defined(__sparc__) +# define SO_WIFI_STATUS 0x0025 +# elif defined(__parisc__) +# define SO_WIFI_STATUS 0x4022 +# else +# define SO_WIFI_STATUS 41 +# endif + +# define SCM_WIFI_STATUS SO_WIFI_STATUS +#endif + +#ifndef SO_EE_ORIGIN_TXSTATUS +#define SO_EE_ORIGIN_TXSTATUS 4 +#endif + +#ifndef PACKET_TX_TIMESTAMP +#define PACKET_TX_TIMESTAMP 16 +#endif + +#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 + +#endif /* LINUX_DEFINES_H */ diff --git a/freebsd/contrib/wpa/src/eap_common/chap.c b/freebsd/contrib/wpa/src/eap_common/chap.c new file mode 100644 index 00000000..012000f0 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_common/chap.c @@ -0,0 +1,30 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * CHAP-MD5 (RFC 1994) + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/crypto.h" +#include "chap.h" + +int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, + size_t challenge_len, u8 *response) +{ + const u8 *addr[3]; + size_t len[3]; + + addr[0] = &id; + len[0] = 1; + addr[1] = secret; + len[1] = secret_len; + addr[2] = challenge; + len[2] = challenge_len; + return md5_vector(3, addr, len, response); +} diff --git a/freebsd/contrib/wpa/src/eap_common/chap.h b/freebsd/contrib/wpa/src/eap_common/chap.h new file mode 100644 index 00000000..a791505f --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_common/chap.h @@ -0,0 +1,17 @@ +/* + * CHAP-MD5 (RFC 1994) + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef CHAP_H +#define CHAP_H + +#define CHAP_MD5_LEN 16 + +int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, + size_t challenge_len, u8 *response); + +#endif /* CHAP_H */ diff --git a/freebsd/contrib/wpa/src/eap_common/eap_common.c b/freebsd/contrib/wpa/src/eap_common/eap_common.c new file mode 100644 index 00000000..fc1909a3 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_common/eap_common.c @@ -0,0 +1,290 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP common peer/server definitions + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_defs.h" +#include "eap_common.h" + +/** + * eap_hdr_len_valid - Validate EAP header length field + * @msg: EAP frame (starting with EAP header) + * @min_payload: Minimum payload length needed + * Returns: 1 for valid header, 0 for invalid + * + * This is a helper function that does minimal validation of EAP messages. The + * length field is verified to be large enough to include the header and not + * too large to go beyond the end of the buffer. + */ +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload) +{ + const struct eap_hdr *hdr; + size_t len; + + if (msg == NULL) + return 0; + + hdr = wpabuf_head(msg); + + if (wpabuf_len(msg) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); + return 0; + } + + len = be_to_host16(hdr->length); + if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) { + wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); + return 0; + } + + return 1; +} + + +/** + * eap_hdr_validate - Validate EAP header + * @vendor: Expected EAP Vendor-Id (0 = IETF) + * @eap_type: Expected EAP type number + * @msg: EAP frame (starting with EAP header) + * @plen: Pointer to variable to contain the returned payload length + * Returns: Pointer to EAP payload (after type field), or %NULL on failure + * + * This is a helper function for EAP method implementations. This is usually + * called in the beginning of struct eap_method::process() function to verify + * that the received EAP request packet has a valid header. This function is + * able to process both legacy and expanded EAP headers and in most cases, the + * caller can just use the returned payload pointer (into *plen) for processing + * the payload regardless of whether the packet used the expanded EAP header or + * not. + */ +const u8 * eap_hdr_validate(int vendor, EapType eap_type, + const struct wpabuf *msg, size_t *plen) +{ + const struct eap_hdr *hdr; + const u8 *pos; + size_t len; + + if (!eap_hdr_len_valid(msg, 1)) + return NULL; + + hdr = wpabuf_head(msg); + len = be_to_host16(hdr->length); + pos = (const u8 *) (hdr + 1); + + if (*pos == EAP_TYPE_EXPANDED) { + int exp_vendor; + u32 exp_type; + if (len < sizeof(*hdr) + 8) { + wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP " + "length"); + return NULL; + } + pos++; + exp_vendor = WPA_GET_BE24(pos); + pos += 3; + exp_type = WPA_GET_BE32(pos); + pos += 4; + if (exp_vendor != vendor || exp_type != (u32) eap_type) { + wpa_printf(MSG_INFO, "EAP: Invalid expanded frame " + "type"); + return NULL; + } + + *plen = len - sizeof(*hdr) - 8; + return pos; + } else { + if (vendor != EAP_VENDOR_IETF || *pos != eap_type) { + wpa_printf(MSG_INFO, "EAP: Invalid frame type"); + return NULL; + } + *plen = len - sizeof(*hdr) - 1; + return pos + 1; + } +} + + +/** + * eap_msg_alloc - Allocate a buffer for an EAP message + * @vendor: Vendor-Id (0 = IETF) + * @type: EAP type + * @payload_len: Payload length in bytes (data after Type) + * @code: Message Code (EAP_CODE_*) + * @identifier: Identifier + * Returns: Pointer to the allocated message buffer or %NULL on error + * + * This function can be used to allocate a buffer for an EAP message and fill + * in the EAP header. This function is automatically using expanded EAP header + * if the selected Vendor-Id is not IETF. In other words, most EAP methods do + * not need to separately select which header type to use when using this + * function to allocate the message buffers. The returned buffer has room for + * payload_len bytes and has the EAP header and Type field already filled in. + */ +struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + struct wpabuf *buf; + struct eap_hdr *hdr; + size_t len; + + len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) + + payload_len; + buf = wpabuf_alloc(len); + if (buf == NULL) + return NULL; + + hdr = wpabuf_put(buf, sizeof(*hdr)); + hdr->code = code; + hdr->identifier = identifier; + hdr->length = host_to_be16(len); + + if (vendor == EAP_VENDOR_IETF) { + wpabuf_put_u8(buf, type); + } else { + wpabuf_put_u8(buf, EAP_TYPE_EXPANDED); + wpabuf_put_be24(buf, vendor); + wpabuf_put_be32(buf, type); + } + + return buf; +} + + +/** + * eap_update_len - Update EAP header length + * @msg: EAP message from eap_msg_alloc + * + * This function updates the length field in the EAP header to match with the + * current length for the buffer. This allows eap_msg_alloc() to be used to + * allocate a larger buffer than the exact message length (e.g., if exact + * message length is not yet known). + */ +void eap_update_len(struct wpabuf *msg) +{ + struct eap_hdr *hdr; + hdr = wpabuf_mhead(msg); + if (wpabuf_len(msg) < sizeof(*hdr)) + return; + hdr->length = host_to_be16(wpabuf_len(msg)); +} + + +/** + * eap_get_id - Get EAP Identifier from wpabuf + * @msg: Buffer starting with an EAP header + * Returns: The Identifier field from the EAP header + */ +u8 eap_get_id(const struct wpabuf *msg) +{ + const struct eap_hdr *eap; + + if (wpabuf_len(msg) < sizeof(*eap)) + return 0; + + eap = wpabuf_head(msg); + return eap->identifier; +} + + +/** + * eap_get_type - 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)]; +} + + +#ifdef CONFIG_ERP +int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs, + int stop_at_keyname) +{ + os_memset(tlvs, 0, sizeof(*tlvs)); + + while (pos < end) { + u8 tlv_type, tlv_len; + + tlv_type = *pos++; + switch (tlv_type) { + case EAP_ERP_TV_RRK_LIFETIME: + case EAP_ERP_TV_RMSK_LIFETIME: + /* 4-octet TV */ + if (pos + 4 > end) { + wpa_printf(MSG_DEBUG, "EAP: Too short TV"); + return -1; + } + pos += 4; + break; + case EAP_ERP_TLV_DOMAIN_NAME: + case EAP_ERP_TLV_KEYNAME_NAI: + case EAP_ERP_TLV_CRYPTOSUITES: + case EAP_ERP_TLV_AUTHORIZATION_INDICATION: + case EAP_ERP_TLV_CALLED_STATION_ID: + case EAP_ERP_TLV_CALLING_STATION_ID: + case EAP_ERP_TLV_NAS_IDENTIFIER: + case EAP_ERP_TLV_NAS_IP_ADDRESS: + case EAP_ERP_TLV_NAS_IPV6_ADDRESS: + if (pos >= end) { + wpa_printf(MSG_DEBUG, "EAP: Too short TLV"); + return -1; + } + tlv_len = *pos++; + if (tlv_len > (unsigned) (end - pos)) { + wpa_printf(MSG_DEBUG, "EAP: Truncated TLV"); + return -1; + } + if (tlv_type == EAP_ERP_TLV_KEYNAME_NAI) { + if (tlvs->keyname) { + wpa_printf(MSG_DEBUG, + "EAP: More than one keyName-NAI"); + return -1; + } + tlvs->keyname = pos; + tlvs->keyname_len = tlv_len; + if (stop_at_keyname) + return 0; + } else if (tlv_type == EAP_ERP_TLV_DOMAIN_NAME) { + tlvs->domain = pos; + tlvs->domain_len = tlv_len; + } + pos += tlv_len; + break; + default: + if (tlv_type >= 128 && tlv_type <= 191) { + /* Undefined TLV */ + if (pos >= end) { + wpa_printf(MSG_DEBUG, + "EAP: Too short TLV"); + return -1; + } + tlv_len = *pos++; + if (tlv_len > (unsigned) (end - pos)) { + wpa_printf(MSG_DEBUG, + "EAP: Truncated TLV"); + return -1; + } + pos += tlv_len; + break; + } + wpa_printf(MSG_DEBUG, "EAP: Unknown TV/TLV type %u", + tlv_type); + pos = end; + break; + } + } + + return 0; +} +#endif /* CONFIG_ERP */ diff --git a/freebsd/contrib/wpa/src/eap_common/eap_common.h b/freebsd/contrib/wpa/src/eap_common/eap_common.h new file mode 100644 index 00000000..e62f1676 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_common/eap_common.h @@ -0,0 +1,33 @@ +/* + * EAP common peer/server definitions + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_COMMON_H +#define EAP_COMMON_H + +#include "wpabuf.h" + +struct erp_tlvs { + const u8 *keyname; + const u8 *domain; + + u8 keyname_len; + u8 domain_len; +}; + +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload); +const u8 * eap_hdr_validate(int vendor, EapType eap_type, + const struct wpabuf *msg, size_t *plen); +struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, + u8 code, u8 identifier); +void eap_update_len(struct wpabuf *msg); +u8 eap_get_id(const struct wpabuf *msg); +EapType eap_get_type(const struct wpabuf *msg); +int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs, + int stop_at_keyname); + +#endif /* EAP_COMMON_H */ diff --git a/freebsd/contrib/wpa/src/eap_common/eap_defs.h b/freebsd/contrib/wpa/src/eap_common/eap_defs.h new file mode 100644 index 00000000..54f26ca3 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_common/eap_defs.h @@ -0,0 +1,118 @@ +/* + * EAP server/peer: Shared EAP definitions + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_DEFS_H +#define EAP_DEFS_H + +/* RFC 3748 - Extensible Authentication Protocol (EAP) */ + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_hdr { + u8 code; + u8 identifier; + be16 length; /* including code and identifier; network byte order */ + /* followed by length-4 octets of data */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, + EAP_CODE_FAILURE = 4, EAP_CODE_INITIATE = 5, EAP_CODE_FINISH = 6 }; + +/* EAP Request and Response data begins with one octet Type. Success and + * Failure do not have additional data. */ + +/* Type field in EAP-Initiate and EAP-Finish messages */ +enum eap_erp_type { + EAP_ERP_TYPE_REAUTH_START = 1, + EAP_ERP_TYPE_REAUTH = 2, +}; + +/* ERP TV/TLV types */ +enum eap_erp_tlv_type { + EAP_ERP_TLV_KEYNAME_NAI = 1, + EAP_ERP_TV_RRK_LIFETIME = 2, + EAP_ERP_TV_RMSK_LIFETIME = 3, + EAP_ERP_TLV_DOMAIN_NAME = 4, + EAP_ERP_TLV_CRYPTOSUITES = 5, + EAP_ERP_TLV_AUTHORIZATION_INDICATION = 6, + EAP_ERP_TLV_CALLED_STATION_ID = 128, + EAP_ERP_TLV_CALLING_STATION_ID = 129, + EAP_ERP_TLV_NAS_IDENTIFIER = 130, + EAP_ERP_TLV_NAS_IP_ADDRESS = 131, + EAP_ERP_TLV_NAS_IPV6_ADDRESS = 132, +}; + +/* ERP Cryptosuite */ +enum eap_erp_cryptosuite { + EAP_ERP_CS_HMAC_SHA256_64 = 1, + EAP_ERP_CS_HMAC_SHA256_128 = 2, + EAP_ERP_CS_HMAC_SHA256_256 = 3, +}; + +/* + * EAP Method Types as allocated by IANA: + * http://www.iana.org/assignments/eap-numbers + */ +typedef enum { + EAP_TYPE_NONE = 0, + EAP_TYPE_IDENTITY = 1 /* RFC 3748 */, + EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */, + EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */, + EAP_TYPE_MD5 = 4, /* RFC 3748 */ + EAP_TYPE_OTP = 5 /* RFC 3748 */, + EAP_TYPE_GTC = 6, /* RFC 3748 */ + EAP_TYPE_TLS = 13 /* RFC 2716 */, + EAP_TYPE_LEAP = 17 /* Cisco proprietary */, + EAP_TYPE_SIM = 18 /* RFC 4186 */, + EAP_TYPE_TTLS = 21 /* RFC 5281 */, + EAP_TYPE_AKA = 23 /* RFC 4187 */, + EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */, + EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */, + EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */, + EAP_TYPE_TNC = 38 /* TNC IF-T v1.0-r3; note: tentative assignment; + * type 38 has previously been allocated for + * EAP-HTTP Digest, (funk.com) */, + EAP_TYPE_FAST = 43 /* RFC 4851 */, + EAP_TYPE_PAX = 46 /* RFC 4746 */, + EAP_TYPE_PSK = 47 /* RFC 4764 */, + EAP_TYPE_SAKE = 48 /* RFC 4763 */, + EAP_TYPE_IKEV2 = 49 /* RFC 5106 */, + EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */, + EAP_TYPE_GPSK = 51 /* RFC 5433 */, + EAP_TYPE_PWD = 52 /* RFC 5931 */, + EAP_TYPE_EKE = 53 /* RFC 6124 */, + EAP_TYPE_EXPANDED = 254 /* RFC 3748 */ +} EapType; + + +/* SMI Network Management Private Enterprise Code for vendor specific types */ +enum { + EAP_VENDOR_IETF = 0, + EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */, + EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance (moved to WBA) */, + EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */, + EAP_VENDOR_WFA_NEW = 40808 /* Wi-Fi Alliance */ +}; + +#define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP +#define EAP_VENDOR_TYPE_UNAUTH_TLS 1 + +#define EAP_VENDOR_WFA_UNAUTH_TLS 13 + +#define EAP_MSK_LEN 64 +#define EAP_EMSK_LEN 64 +#define EAP_EMSK_NAME_LEN 8 +#define ERP_MAX_KEY_LEN 64 + +#endif /* EAP_DEFS_H */ diff --git a/freebsd/contrib/wpa/src/eap_common/eap_peap_common.c b/freebsd/contrib/wpa/src/eap_common/eap_peap_common.c new file mode 100644 index 00000000..08474793 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_common/eap_peap_common.c @@ -0,0 +1,87 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP-PEAP common routines + * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/sha1.h" +#include "eap_peap_common.h" + +int peap_prfplus(int version, const u8 *key, size_t key_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *buf, size_t buf_len) +{ + unsigned char counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label); + u8 extra[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = hash; + len[0] = 0; + addr[1] = (unsigned char *) label; + len[1] = label_len; + addr[2] = seed; + len[2] = seed_len; + + if (version == 0) { + /* + * PRF+(K, S, LEN) = T1 | T2 | ... | Tn + * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00) + * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00) + * ... + * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00) + */ + + extra[0] = 0; + extra[1] = 0; + + addr[3] = &counter; + len[3] = 1; + addr[4] = extra; + len[4] = 2; + } else { + /* + * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where: + * T1 = HMAC-SHA1(K, S | LEN | 0x01) + * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02) + * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03) + * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04) + * ... + */ + + extra[0] = buf_len & 0xff; + + addr[3] = extra; + len[3] = 1; + addr[4] = &counter; + len[4] = 1; + } + + pos = 0; + while (pos < buf_len) { + counter++; + plen = buf_len - pos; + if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0) + return -1; + if (plen >= SHA1_MAC_LEN) { + os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); + pos += SHA1_MAC_LEN; + } else { + os_memcpy(&buf[pos], hash, plen); + break; + } + len[0] = SHA1_MAC_LEN; + } + + return 0; +} diff --git a/freebsd/contrib/wpa/src/eap_common/eap_peap_common.h b/freebsd/contrib/wpa/src/eap_common/eap_peap_common.h new file mode 100644 index 00000000..7aad0dff --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_common/eap_peap_common.h @@ -0,0 +1,16 @@ +/* + * EAP-PEAP common routines + * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_PEAP_COMMON_H +#define EAP_PEAP_COMMON_H + +int peap_prfplus(int version, const u8 *key, size_t key_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *buf, size_t buf_len); + +#endif /* EAP_PEAP_COMMON_H */ diff --git a/freebsd/contrib/wpa/src/eap_common/eap_psk_common.c b/freebsd/contrib/wpa/src/eap_common/eap_psk_common.c new file mode 100644 index 00000000..eaea191b --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_common/eap_psk_common.c @@ -0,0 +1,70 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP server/peer: EAP-PSK shared routines + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/aes_wrap.h" +#include "eap_defs.h" +#include "eap_psk_common.h" + +#define aes_block_size 16 + + +int eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk) +{ + os_memset(ak, 0, aes_block_size); + if (aes_128_encrypt_block(psk, ak, ak)) + return -1; + os_memcpy(kdk, ak, aes_block_size); + ak[aes_block_size - 1] ^= 0x01; + kdk[aes_block_size - 1] ^= 0x02; + if (aes_128_encrypt_block(psk, ak, ak) || + aes_128_encrypt_block(psk, kdk, kdk)) + return -1; + return 0; +} + + +int eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk, + u8 *emsk) +{ + u8 hash[aes_block_size]; + u8 counter = 1; + int i; + + if (aes_128_encrypt_block(kdk, rand_p, hash)) + return -1; + + hash[aes_block_size - 1] ^= counter; + if (aes_128_encrypt_block(kdk, hash, tek)) + return -1; + hash[aes_block_size - 1] ^= counter; + counter++; + + for (i = 0; i < EAP_MSK_LEN / aes_block_size; i++) { + hash[aes_block_size - 1] ^= counter; + if (aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size])) + return -1; + hash[aes_block_size - 1] ^= counter; + counter++; + } + + for (i = 0; i < EAP_EMSK_LEN / aes_block_size; i++) { + hash[aes_block_size - 1] ^= counter; + if (aes_128_encrypt_block(kdk, hash, + &emsk[i * aes_block_size])) + return -1; + hash[aes_block_size - 1] ^= counter; + counter++; + } + + return 0; +} diff --git a/freebsd/contrib/wpa/src/eap_common/eap_psk_common.h b/freebsd/contrib/wpa/src/eap_common/eap_psk_common.h new file mode 100644 index 00000000..8bc2c3c4 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_common/eap_psk_common.h @@ -0,0 +1,72 @@ +/* + * EAP server/peer: EAP-PSK shared routines + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_PSK_COMMON_H +#define EAP_PSK_COMMON_H + + +#define EAP_PSK_RAND_LEN 16 +#define EAP_PSK_MAC_LEN 16 +#define EAP_PSK_TEK_LEN 16 +#define EAP_PSK_PSK_LEN 16 +#define EAP_PSK_AK_LEN 16 +#define EAP_PSK_KDK_LEN 16 + +#define EAP_PSK_R_FLAG_CONT 1 +#define EAP_PSK_R_FLAG_DONE_SUCCESS 2 +#define EAP_PSK_R_FLAG_DONE_FAILURE 3 +#define EAP_PSK_E_FLAG 0x20 + +#define EAP_PSK_FLAGS_GET_T(flags) (((flags) & 0xc0) >> 6) +#define EAP_PSK_FLAGS_SET_T(t) ((u8) (t) << 6) + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +/* EAP-PSK First Message (AS -> Supplicant) */ +struct eap_psk_hdr_1 { + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + /* Followed by variable length ID_S */ +} STRUCT_PACKED; + +/* EAP-PSK Second Message (Supplicant -> AS) */ +struct eap_psk_hdr_2 { + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 rand_p[EAP_PSK_RAND_LEN]; + u8 mac_p[EAP_PSK_MAC_LEN]; + /* Followed by variable length ID_P */ +} STRUCT_PACKED; + +/* EAP-PSK Third Message (AS -> Supplicant) */ +struct eap_psk_hdr_3 { + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 mac_s[EAP_PSK_MAC_LEN]; + /* Followed by variable length PCHANNEL */ +} STRUCT_PACKED; + +/* EAP-PSK Fourth Message (Supplicant -> AS) */ +struct eap_psk_hdr_4 { + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + /* Followed by variable length PCHANNEL */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +int __must_check eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk); +int __must_check eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, + u8 *msk, u8 *emsk); + +#endif /* EAP_PSK_COMMON_H */ diff --git a/freebsd/contrib/wpa/src/eap_common/eap_tlv_common.h b/freebsd/contrib/wpa/src/eap_common/eap_tlv_common.h new file mode 100644 index 00000000..3286055a --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_common/eap_tlv_common.h @@ -0,0 +1,112 @@ +/* + * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TLV_COMMON_H +#define EAP_TLV_COMMON_H + +/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */ +#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */ +#define EAP_TLV_NAK_TLV 4 +#define EAP_TLV_ERROR_CODE_TLV 5 +#define EAP_TLV_CONNECTION_BINDING_TLV 6 +#define EAP_TLV_VENDOR_SPECIFIC_TLV 7 +#define EAP_TLV_URI_TLV 8 +#define EAP_TLV_EAP_PAYLOAD_TLV 9 +#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10 +#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */ +#define EAP_TLV_CRYPTO_BINDING_TLV 12 +#define EAP_TLV_CALLING_STATION_ID_TLV 13 +#define EAP_TLV_CALLED_STATION_ID_TLV 14 +#define EAP_TLV_NAS_PORT_TYPE_TLV 15 +#define EAP_TLV_SERVER_IDENTIFIER_TLV 16 +#define EAP_TLV_IDENTITY_TYPE_TLV 17 +#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18 +#define EAP_TLV_REQUEST_ACTION_TLV 19 +#define EAP_TLV_PKCS7_TLV 20 + +#define EAP_TLV_RESULT_SUCCESS 1 +#define EAP_TLV_RESULT_FAILURE 2 + +#define EAP_TLV_TYPE_MANDATORY 0x8000 +#define EAP_TLV_TYPE_MASK 0x3fff + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_tlv_hdr { + be16 tlv_type; + be16 length; +} STRUCT_PACKED; + +struct eap_tlv_nak_tlv { + be16 tlv_type; + be16 length; + be32 vendor_id; + be16 nak_type; +} STRUCT_PACKED; + +struct eap_tlv_result_tlv { + be16 tlv_type; + be16 length; + be16 status; +} STRUCT_PACKED; + +/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */ +struct eap_tlv_intermediate_result_tlv { + be16 tlv_type; + be16 length; + be16 status; + /* Followed by optional TLVs */ +} STRUCT_PACKED; + +/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */ +struct eap_tlv_crypto_binding_tlv { + be16 tlv_type; + be16 length; + u8 reserved; + u8 version; + u8 received_version; + u8 subtype; + u8 nonce[32]; + u8 compound_mac[20]; +} STRUCT_PACKED; + +struct eap_tlv_pac_ack_tlv { + be16 tlv_type; + be16 length; + be16 pac_type; + be16 pac_len; + be16 result; +} STRUCT_PACKED; + +/* RFC 4851, Section 4.2.9 - Request-Action TLV */ +struct eap_tlv_request_action_tlv { + be16 tlv_type; + be16 length; + be16 action; +} STRUCT_PACKED; + +/* RFC 5422, Section 4.2.6 - PAC-Type TLV */ +struct eap_tlv_pac_type_tlv { + be16 tlv_type; /* PAC_TYPE_PAC_TYPE */ + be16 length; + be16 pac_type; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0 +#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1 + +#define EAP_TLV_ACTION_PROCESS_TLV 1 +#define EAP_TLV_ACTION_NEGOTIATE_EAP 2 + +#endif /* EAP_TLV_COMMON_H */ diff --git a/freebsd/contrib/wpa/src/eap_common/eap_ttls.h b/freebsd/contrib/wpa/src/eap_common/eap_ttls.h new file mode 100644 index 00000000..17901d42 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_common/eap_ttls.h @@ -0,0 +1,65 @@ +/* + * EAP server/peer: EAP-TTLS (RFC 5281) + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TTLS_H +#define EAP_TTLS_H + +struct ttls_avp { + be32 avp_code; + be32 avp_length; /* 8-bit flags, 24-bit length; + * length includes AVP header */ + /* optional 32-bit Vendor-ID */ + /* Data */ +}; + +struct ttls_avp_vendor { + be32 avp_code; + be32 avp_length; /* 8-bit flags, 24-bit length; + * length includes AVP header */ + be32 vendor_id; + /* Data */ +}; + +#define AVP_FLAGS_VENDOR 0x80 +#define AVP_FLAGS_MANDATORY 0x40 + +#define AVP_PAD(start, pos) \ +do { \ + int __pad; \ + __pad = (4 - (((pos) - (start)) & 3)) & 3; \ + os_memset((pos), 0, __pad); \ + pos += __pad; \ +} while (0) + + +/* RFC 2865 */ +#define RADIUS_ATTR_USER_NAME 1 +#define RADIUS_ATTR_USER_PASSWORD 2 +#define RADIUS_ATTR_CHAP_PASSWORD 3 +#define RADIUS_ATTR_REPLY_MESSAGE 18 +#define RADIUS_ATTR_CHAP_CHALLENGE 60 +#define RADIUS_ATTR_EAP_MESSAGE 79 + +/* RFC 2548 */ +#define RADIUS_VENDOR_ID_MICROSOFT 311 +#define RADIUS_ATTR_MS_CHAP_RESPONSE 1 +#define RADIUS_ATTR_MS_CHAP_ERROR 2 +#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6 +#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11 +#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25 +#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26 +#define RADIUS_ATTR_MS_CHAP2_CPW 27 + +#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16 +#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50 +#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8 +#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50 +#define EAP_TTLS_CHAP_CHALLENGE_LEN 16 +#define EAP_TTLS_CHAP_PASSWORD_LEN 16 + +#endif /* EAP_TTLS_H */ diff --git a/freebsd/contrib/wpa/src/eap_common/eap_wsc_common.h b/freebsd/contrib/wpa/src/eap_common/eap_wsc_common.h new file mode 100644 index 00000000..0e7b6530 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_common/eap_wsc_common.h @@ -0,0 +1,27 @@ +/* + * EAP-WSC definitions for Wi-Fi Protected Setup + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_WSC_COMMON_H +#define EAP_WSC_COMMON_H + +#define EAP_VENDOR_TYPE_WSC 1 + +#define WSC_FLAGS_MF 0x01 +#define WSC_FLAGS_LF 0x02 + +#define WSC_ID_REGISTRAR "WFA-SimpleConfig-Registrar-1-0" +#define WSC_ID_REGISTRAR_LEN 30 +#define WSC_ID_ENROLLEE "WFA-SimpleConfig-Enrollee-1-0" +#define WSC_ID_ENROLLEE_LEN 29 + +#define WSC_FRAGMENT_SIZE 1400 + + +struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code); + +#endif /* EAP_WSC_COMMON_H */ diff --git a/freebsd/contrib/wpa/src/eap_peer/eap.c b/freebsd/contrib/wpa/src/eap_peer/eap.c new file mode 100644 index 00000000..cfaf201a --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap.c @@ -0,0 +1,2952 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP peer state machines (RFC 4137) + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This file implements the Peer State Machine as defined in RFC 4137. The used + * states and state transitions match mostly with the RFC. However, there are + * couple of additional transitions for working around small issues noticed + * during testing. These exceptions are explained in comments within the + * functions in this file. The method functions, m.func(), are similar to the + * ones used in RFC 4137, but some small changes have used here to optimize + * operations and to add functionality needed for fast re-authentication + * (session resumption). + */ + +#include "includes.h" + +#include "common.h" +#include "pcsc_funcs.h" +#include "state_machine.h" +#include "ext_password.h" +#include "crypto/crypto.h" +#include "crypto/tls.h" +#include "crypto/sha256.h" +#include "common/wpa_ctrl.h" +#include "eap_common/eap_wsc_common.h" +#include "eap_i.h" +#include "eap_config.h" + +#define STATE_MACHINE_DATA struct eap_sm +#define STATE_MACHINE_DEBUG_PREFIX "EAP" + +#define EAP_MAX_AUTH_ROUNDS 50 +#define EAP_CLIENT_TIMEOUT_DEFAULT 60 + + +static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, + EapType method); +static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id); +static void eap_sm_processIdentity(struct eap_sm *sm, + const struct wpabuf *req); +static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req); +static struct wpabuf * eap_sm_buildNotify(int id); +static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req); +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static const char * eap_sm_method_state_txt(EapMethodState state); +static const char * eap_sm_decision_txt(EapDecision decision); +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + + +static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) +{ + return sm->eapol_cb->get_bool(sm->eapol_ctx, var); +} + + +static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var, + Boolean value) +{ + sm->eapol_cb->set_bool(sm->eapol_ctx, var, value); +} + + +static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var) +{ + return sm->eapol_cb->get_int(sm->eapol_ctx, var); +} + + +static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var, + unsigned int value) +{ + sm->eapol_cb->set_int(sm->eapol_ctx, var, value); +} + + +static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm) +{ + return sm->eapol_cb->get_eapReqData(sm->eapol_ctx); +} + + +static void eap_notify_status(struct eap_sm *sm, const char *status, + const char *parameter) +{ + wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)", + status, parameter); + if (sm->eapol_cb->notify_status) + sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter); +} + + +static void eap_sm_free_key(struct eap_sm *sm) +{ + if (sm->eapKeyData) { + bin_clear_free(sm->eapKeyData, sm->eapKeyDataLen); + sm->eapKeyData = NULL; + } +} + + +static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) +{ + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = NULL; + + if (sm->m == NULL || sm->eap_method_priv == NULL) + return; + + wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method " + "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt); + sm->m->deinit(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + sm->m = NULL; +} + + +/** + * eap_allowed_method - Check whether EAP method is allowed + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types + * @method: EAP type + * Returns: 1 = allowed EAP method, 0 = not allowed + */ +int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method) +{ + struct eap_peer_config *config = eap_get_config(sm); + int i; + struct eap_method_type *m; + + if (config == NULL || config->eap_methods == NULL) + return 1; + + m = config->eap_methods; + for (i = 0; m[i].vendor != EAP_VENDOR_IETF || + m[i].method != EAP_TYPE_NONE; i++) { + if (m[i].vendor == vendor && m[i].method == method) + return 1; + } + return 0; +} + + +/* + * This state initializes state machine variables when the machine is + * activated (portEnabled = TRUE). This is also used when re-starting + * authentication (eapRestart == TRUE). + */ +SM_STATE(EAP, INITIALIZE) +{ + SM_ENTRY(EAP, INITIALIZE); + if (sm->fast_reauth && sm->m && sm->m->has_reauth_data && + sm->m->has_reauth_data(sm, sm->eap_method_priv) && + !sm->prev_failure && + sm->last_config == eap_get_config(sm)) { + wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for " + "fast reauthentication"); + sm->m->deinit_for_reauth(sm, sm->eap_method_priv); + } else { + sm->last_config = eap_get_config(sm); + eap_deinit_prev_method(sm, "INITIALIZE"); + } + sm->selectedMethod = EAP_TYPE_NONE; + sm->methodState = METHOD_NONE; + sm->allowNotifications = TRUE; + sm->decision = DECISION_FAIL; + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; + eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); + eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); + eapol_set_bool(sm, EAPOL_eapFail, FALSE); + eap_sm_free_key(sm); + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; + sm->eapKeyAvailable = FALSE; + eapol_set_bool(sm, EAPOL_eapRestart, FALSE); + sm->lastId = -1; /* new session - make sure this does not match with + * the first EAP-Packet */ + /* + * RFC 4137 does not reset eapResp and eapNoResp here. However, this + * seemed to be able to trigger cases where both were set and if EAPOL + * state machine uses eapNoResp first, it may end up not sending a real + * reply correctly. This occurred when the workaround in FAIL state set + * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do + * something else(?) + */ + eapol_set_bool(sm, EAPOL_eapResp, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); + sm->num_rounds = 0; + sm->prev_failure = 0; + sm->expected_failure = 0; + sm->reauthInit = FALSE; + sm->erp_seq = (u32) -1; +} + + +/* + * This state is reached whenever service from the lower layer is interrupted + * or unavailable (portEnabled == FALSE). Immediate transition to INITIALIZE + * occurs when the port becomes enabled. + */ +SM_STATE(EAP, DISABLED) +{ + SM_ENTRY(EAP, DISABLED); + sm->num_rounds = 0; + /* + * RFC 4137 does not describe clearing of idleWhile here, but doing so + * allows the timer tick to be stopped more quickly when EAP is not in + * use. + */ + eapol_set_int(sm, EAPOL_idleWhile, 0); +} + + +/* + * The state machine spends most of its time here, waiting for something to + * happen. This state is entered unconditionally from INITIALIZE, DISCARD, and + * SEND_RESPONSE states. + */ +SM_STATE(EAP, IDLE) +{ + SM_ENTRY(EAP, IDLE); +} + + +/* + * This state is entered when an EAP packet is received (eapReq == TRUE) to + * parse the packet header. + */ +SM_STATE(EAP, RECEIVED) +{ + const struct wpabuf *eapReqData; + + SM_ENTRY(EAP, RECEIVED); + eapReqData = eapol_get_eapReqData(sm); + /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */ + eap_sm_parseEapReq(sm, eapReqData); + sm->num_rounds++; +} + + +/* + * This state is entered when a request for a new type comes in. Either the + * correct method is started, or a Nak response is built. + */ +SM_STATE(EAP, GET_METHOD) +{ + int reinit; + EapType method; + const struct eap_method *eap_method; + + SM_ENTRY(EAP, GET_METHOD); + + if (sm->reqMethod == EAP_TYPE_EXPANDED) + method = sm->reqVendorMethod; + else + method = sm->reqMethod; + + eap_method = eap_peer_get_eap_method(sm->reqVendor, method); + + if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) { + wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed", + sm->reqVendor, method); + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD + "vendor=%u method=%u -> NAK", + sm->reqVendor, method); + eap_notify_status(sm, "refuse proposed method", + eap_method ? eap_method->name : "unknown"); + goto nak; + } + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD + "vendor=%u method=%u", sm->reqVendor, method); + + eap_notify_status(sm, "accept proposed method", + eap_method ? eap_method->name : "unknown"); + /* + * RFC 4137 does not define specific operation for fast + * re-authentication (session resumption). The design here is to allow + * the previously used method data to be maintained for + * re-authentication if the method support session resumption. + * Otherwise, the previously used method data is freed and a new method + * is allocated here. + */ + if (sm->fast_reauth && + sm->m && sm->m->vendor == sm->reqVendor && + sm->m->method == method && + sm->m->has_reauth_data && + sm->m->has_reauth_data(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: Using previous method data" + " for fast re-authentication"); + reinit = 1; + } else { + eap_deinit_prev_method(sm, "GET_METHOD"); + reinit = 0; + } + + sm->selectedMethod = sm->reqMethod; + if (sm->m == NULL) + sm->m = eap_method; + if (!sm->m) { + wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: " + "vendor %d method %d", + sm->reqVendor, method); + goto nak; + } + + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; + + wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: " + "vendor %u method %u (%s)", + sm->reqVendor, method, sm->m->name); + if (reinit) + sm->eap_method_priv = sm->m->init_for_reauth( + sm, sm->eap_method_priv); + else + sm->eap_method_priv = sm->m->init(sm); + + if (sm->eap_method_priv == NULL) { + struct eap_peer_config *config = eap_get_config(sm); + wpa_msg(sm->msg_ctx, MSG_INFO, + "EAP: Failed to initialize EAP method: vendor %u " + "method %u (%s)", + sm->reqVendor, method, sm->m->name); + sm->m = NULL; + sm->methodState = METHOD_NONE; + sm->selectedMethod = EAP_TYPE_NONE; + if (sm->reqMethod == EAP_TYPE_TLS && config && + (config->pending_req_pin || + config->pending_req_passphrase)) { + /* + * Return without generating Nak in order to allow + * entering of PIN code or passphrase to retry the + * current EAP packet. + */ + wpa_printf(MSG_DEBUG, "EAP: Pending PIN/passphrase " + "request - skip Nak"); + return; + } + + goto nak; + } + + sm->methodState = METHOD_INIT; + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_METHOD + "EAP vendor %u method %u (%s) selected", + sm->reqVendor, method, sm->m->name); + return; + +nak: + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + sm->eapRespData = eap_sm_buildNak(sm, sm->reqId); +} + + +#ifdef CONFIG_ERP + +static char * eap_home_realm(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + char *realm; + size_t i, realm_len; + + if (!config) + return NULL; + + if (config->identity) { + for (i = 0; i < config->identity_len; i++) { + if (config->identity[i] == '@') + break; + } + if (i < config->identity_len) { + realm_len = config->identity_len - i - 1; + realm = os_malloc(realm_len + 1); + if (realm == NULL) + return NULL; + os_memcpy(realm, &config->identity[i + 1], realm_len); + realm[realm_len] = '\0'; + return realm; + } + } + + if (config->anonymous_identity) { + for (i = 0; i < config->anonymous_identity_len; i++) { + if (config->anonymous_identity[i] == '@') + break; + } + if (i < config->anonymous_identity_len) { + realm_len = config->anonymous_identity_len - i - 1; + realm = os_malloc(realm_len + 1); + if (realm == NULL) + return NULL; + os_memcpy(realm, &config->anonymous_identity[i + 1], + realm_len); + realm[realm_len] = '\0'; + return realm; + } + } + + return os_strdup(""); +} + + +static struct eap_erp_key * +eap_erp_get_key(struct eap_sm *sm, const char *realm) +{ + struct eap_erp_key *erp; + + dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) { + char *pos; + + pos = os_strchr(erp->keyname_nai, '@'); + if (!pos) + continue; + pos++; + if (os_strcmp(pos, realm) == 0) + return erp; + } + + return NULL; +} + + +static struct eap_erp_key * +eap_erp_get_key_nai(struct eap_sm *sm, const char *nai) +{ + struct eap_erp_key *erp; + + dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) { + if (os_strcmp(erp->keyname_nai, nai) == 0) + return erp; + } + + return NULL; +} + + +static void eap_peer_erp_free_key(struct eap_erp_key *erp) +{ + dl_list_del(&erp->list); + bin_clear_free(erp, sizeof(*erp)); +} + + +static void eap_erp_remove_keys_realm(struct eap_sm *sm, const char *realm) +{ + struct eap_erp_key *erp; + + while ((erp = eap_erp_get_key(sm, realm)) != NULL) { + wpa_printf(MSG_DEBUG, "EAP: Delete old ERP key %s", + erp->keyname_nai); + eap_peer_erp_free_key(erp); + } +} + +#endif /* CONFIG_ERP */ + + +void eap_peer_erp_free_keys(struct eap_sm *sm) +{ +#ifdef CONFIG_ERP + struct eap_erp_key *erp, *tmp; + + dl_list_for_each_safe(erp, tmp, &sm->erp_keys, struct eap_erp_key, list) + eap_peer_erp_free_key(erp); +#endif /* CONFIG_ERP */ +} + + +static void eap_peer_erp_init(struct eap_sm *sm) +{ +#ifdef CONFIG_ERP + u8 *emsk = NULL; + size_t emsk_len = 0; + u8 EMSKname[EAP_EMSK_NAME_LEN]; + u8 len[2]; + char *realm; + size_t realm_len, nai_buf_len; + struct eap_erp_key *erp = NULL; + int pos; + + realm = eap_home_realm(sm); + if (!realm) + return; + realm_len = os_strlen(realm); + wpa_printf(MSG_DEBUG, "EAP: Realm for ERP keyName-NAI: %s", realm); + eap_erp_remove_keys_realm(sm, realm); + + nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + realm_len; + if (nai_buf_len > 253) { + /* + * keyName-NAI has a maximum length of 253 octet to fit in + * RADIUS attributes. + */ + wpa_printf(MSG_DEBUG, + "EAP: Too long realm for ERP keyName-NAI maximum length"); + goto fail; + } + nai_buf_len++; /* null termination */ + erp = os_zalloc(sizeof(*erp) + nai_buf_len); + if (erp == NULL) + goto fail; + + emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len); + if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) { + wpa_printf(MSG_DEBUG, + "EAP: No suitable EMSK available for ERP"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len); + + WPA_PUT_BE16(len, 8); + if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK", + len, sizeof(len), + EMSKname, EAP_EMSK_NAME_LEN) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN); + + pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len, + EMSKname, EAP_EMSK_NAME_LEN); + erp->keyname_nai[pos] = '@'; + os_memcpy(&erp->keyname_nai[pos + 1], realm, realm_len); + + WPA_PUT_BE16(len, emsk_len); + if (hmac_sha256_kdf(emsk, emsk_len, + "EAP Re-authentication Root Key@ietf.org", + len, sizeof(len), erp->rRK, emsk_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP"); + goto fail; + } + erp->rRK_len = emsk_len; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len); + + if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, + "EAP Re-authentication Integrity Key@ietf.org", + len, sizeof(len), erp->rIK, erp->rRK_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP"); + goto fail; + } + erp->rIK_len = erp->rRK_len; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len); + + wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s", erp->keyname_nai); + dl_list_add(&sm->erp_keys, &erp->list); + erp = NULL; +fail: + bin_clear_free(emsk, emsk_len); + bin_clear_free(erp, sizeof(*erp)); + os_free(realm); +#endif /* CONFIG_ERP */ +} + + +#ifdef CONFIG_ERP +static int eap_peer_erp_reauth_start(struct eap_sm *sm, + const struct eap_hdr *hdr, size_t len) +{ + char *realm; + struct eap_erp_key *erp; + struct wpabuf *msg; + u8 hash[SHA256_MAC_LEN]; + + realm = eap_home_realm(sm); + if (!realm) + return -1; + + erp = eap_erp_get_key(sm, realm); + os_free(realm); + realm = NULL; + if (!erp) + return -1; + + if (erp->next_seq >= 65536) + return -1; /* SEQ has range of 0..65535 */ + + /* TODO: check rRK lifetime expiration */ + + wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)", + erp->keyname_nai, erp->next_seq); + + msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH, + 1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16, + EAP_CODE_INITIATE, hdr->identifier); + if (msg == NULL) + return -1; + + wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */ + wpabuf_put_be16(msg, erp->next_seq); + + wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI); + wpabuf_put_u8(msg, os_strlen(erp->keyname_nai)); + wpabuf_put_str(msg, erp->keyname_nai); + + wpabuf_put_u8(msg, EAP_ERP_CS_HMAC_SHA256_128); /* Cryptosuite */ + + if (hmac_sha256(erp->rIK, erp->rIK_len, + wpabuf_head(msg), wpabuf_len(msg), hash) < 0) { + wpabuf_free(msg); + return -1; + } + wpabuf_put_data(msg, hash, 16); + + wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth"); + sm->erp_seq = erp->next_seq; + erp->next_seq++; + wpabuf_free(sm->eapRespData); + sm->eapRespData = msg; + sm->reauthInit = TRUE; + return 0; +} +#endif /* CONFIG_ERP */ + + +/* + * The method processing happens here. The request from the authenticator is + * processed, and an appropriate response packet is built. + */ +SM_STATE(EAP, METHOD) +{ + struct wpabuf *eapReqData; + struct eap_method_ret ret; + int min_len = 1; + + SM_ENTRY(EAP, METHOD); + if (sm->m == NULL) { + wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected"); + return; + } + + eapReqData = eapol_get_eapReqData(sm); + if (sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_LEAP) + min_len = 0; /* LEAP uses EAP-Success without payload */ + if (!eap_hdr_len_valid(eapReqData, min_len)) + return; + + /* + * Get ignore, methodState, decision, allowNotifications, and + * eapRespData. RFC 4137 uses three separate method procedure (check, + * process, and buildResp) in this state. These have been combined into + * a single function call to m->process() in order to optimize EAP + * method implementation interface a bit. These procedures are only + * used from within this METHOD state, so there is no need to keep + * these as separate C functions. + * + * The RFC 4137 procedures return values as follows: + * ignore = m.check(eapReqData) + * (methodState, decision, allowNotifications) = m.process(eapReqData) + * eapRespData = m.buildResp(reqId) + */ + os_memset(&ret, 0, sizeof(ret)); + ret.ignore = sm->ignore; + ret.methodState = sm->methodState; + ret.decision = sm->decision; + ret.allowNotifications = sm->allowNotifications; + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret, + eapReqData); + wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s " + "methodState=%s decision=%s eapRespData=%p", + ret.ignore ? "TRUE" : "FALSE", + eap_sm_method_state_txt(ret.methodState), + eap_sm_decision_txt(ret.decision), + sm->eapRespData); + + 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)) { + struct eap_peer_config *config = eap_get_config(sm); + + eap_sm_free_key(sm); + sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, + &sm->eapKeyDataLen); + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; + if (sm->m->getSessionId) { + sm->eapSessionId = sm->m->getSessionId( + sm, sm->eap_method_priv, + &sm->eapSessionIdLen); + wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", + sm->eapSessionId, sm->eapSessionIdLen); + } + if (config->erp && sm->m->get_emsk && sm->eapSessionId) + eap_peer_erp_init(sm); + } +} + + +/* + * 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_sha1, sm->req_sha1, 20); + sm->lastId = sm->reqId; + sm->lastRespData = wpabuf_dup(sm->eapRespData); + eapol_set_bool(sm, EAPOL_eapResp, TRUE); + } else { + wpa_printf(MSG_DEBUG, "EAP: No eapRespData available"); + sm->lastRespData = NULL; + } + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); + sm->reauthInit = FALSE; +} + + +/* + * This state signals the lower layer that the request was discarded, and no + * response packet will be sent at this time. + */ +SM_STATE(EAP, DISCARD) +{ + SM_ENTRY(EAP, DISCARD); + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); +} + + +/* + * Handles requests for Identity method and builds a response. + */ +SM_STATE(EAP, IDENTITY) +{ + const struct wpabuf *eapReqData; + + SM_ENTRY(EAP, IDENTITY); + eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; + eap_sm_processIdentity(sm, eapReqData); + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, 0); +} + + +/* + * Handles requests for Notification method and builds a response. + */ +SM_STATE(EAP, NOTIFICATION) +{ + const struct wpabuf *eapReqData; + + SM_ENTRY(EAP, NOTIFICATION); + eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; + eap_sm_processNotify(sm, eapReqData); + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + sm->eapRespData = eap_sm_buildNotify(sm->reqId); +} + + +/* + * This state retransmits the previous response packet. + */ +SM_STATE(EAP, RETRANSMIT) +{ + SM_ENTRY(EAP, RETRANSMIT); + wpabuf_free(sm->eapRespData); + if (sm->lastRespData) + sm->eapRespData = wpabuf_dup(sm->lastRespData); + else + sm->eapRespData = NULL; +} + + +/* + * This state is entered in case of a successful completion of authentication + * and state machine waits here until port is disabled or EAP authentication is + * restarted. + */ +SM_STATE(EAP, SUCCESS) +{ + SM_ENTRY(EAP, SUCCESS); + if (sm->eapKeyData != NULL) + sm->eapKeyAvailable = TRUE; + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + + /* + * RFC 4137 does not clear eapReq here, but this seems to be required + * to avoid processing the same request twice when state machine is + * initialized. + */ + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + + /* + * RFC 4137 does not set eapNoResp here, but this seems to be required + * to get EAPOL Supplicant backend state machine into SUCCESS state. In + * addition, either eapResp or eapNoResp is required to be set after + * processing the received EAP frame. + */ + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + "EAP authentication completed successfully"); +} + + +/* + * This state is entered in case of a failure and state machine waits here + * until port is disabled or EAP authentication is restarted. + */ +SM_STATE(EAP, FAILURE) +{ + SM_ENTRY(EAP, FAILURE); + eapol_set_bool(sm, EAPOL_eapFail, TRUE); + + /* + * RFC 4137 does not clear eapReq here, but this seems to be required + * to avoid processing the same request twice when state machine is + * initialized. + */ + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + + /* + * RFC 4137 does not set eapNoResp here. However, either eapResp or + * eapNoResp is required to be set after processing the received EAP + * frame. + */ + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE + "EAP authentication failed"); + + sm->prev_failure = 1; +} + + +static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId) +{ + /* + * At least Microsoft IAS and Meetinghouse Aegis seem to be sending + * EAP-Success/Failure with lastId + 1 even though RFC 3748 and + * RFC 4137 require that reqId == lastId. In addition, it looks like + * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success. + * + * Accept this kind of Id if EAP workarounds are enabled. These are + * unauthenticated plaintext messages, so this should have minimal + * security implications (bit easier to fake EAP-Success/Failure). + */ + if (sm->workaround && (reqId == ((lastId + 1) & 0xff) || + reqId == ((lastId + 2) & 0xff))) { + wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected " + "identifier field in EAP Success: " + "reqId=%d lastId=%d (these are supposed to be " + "same)", reqId, lastId); + return 1; + } + wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d " + "lastId=%d", reqId, lastId); + return 0; +} + + +/* + * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions + */ + +static void eap_peer_sm_step_idle(struct eap_sm *sm) +{ + /* + * The first three transitions are from RFC 4137. The last two are + * local additions to handle special cases with LEAP and PEAP server + * not sending EAP-Success in some cases. + */ + if (eapol_get_bool(sm, EAPOL_eapReq)) + SM_ENTER(EAP, RECEIVED); + else if ((eapol_get_bool(sm, EAPOL_altAccept) && + sm->decision != DECISION_FAIL) || + (eapol_get_int(sm, EAPOL_idleWhile) == 0 && + sm->decision == DECISION_UNCOND_SUCC)) + SM_ENTER(EAP, SUCCESS); + else if (eapol_get_bool(sm, EAPOL_altReject) || + (eapol_get_int(sm, EAPOL_idleWhile) == 0 && + sm->decision != DECISION_UNCOND_SUCC) || + (eapol_get_bool(sm, EAPOL_altAccept) && + sm->methodState != METHOD_CONT && + sm->decision == DECISION_FAIL)) + SM_ENTER(EAP, FAILURE); + else if (sm->selectedMethod == EAP_TYPE_LEAP && + sm->leap_done && sm->decision != DECISION_FAIL && + sm->methodState == METHOD_DONE) + SM_ENTER(EAP, SUCCESS); + else if (sm->selectedMethod == EAP_TYPE_PEAP && + sm->peap_done && sm->decision != DECISION_FAIL && + sm->methodState == METHOD_DONE) + SM_ENTER(EAP, SUCCESS); +} + + +static int eap_peer_req_is_duplicate(struct eap_sm *sm) +{ + int duplicate; + + duplicate = (sm->reqId == sm->lastId) && sm->rxReq; + if (sm->workaround && duplicate && + os_memcmp(sm->req_sha1, sm->last_sha1, 20) != 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 SHA1 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 int eap_peer_sm_allow_canned(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + + return config && config->phase1 && + os_strstr(config->phase1, "allow_canned_success=1"); +} + + +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->workaround && sm->lastId == -1 && sm->rxSuccess && + !sm->rxFailure && !sm->rxReq && eap_peer_sm_allow_canned(sm)) + SM_ENTER(EAP, SUCCESS); /* EAP-Success prior any EAP method */ + else if (sm->workaround && sm->lastId == -1 && sm->rxFailure && + !sm->rxReq && sm->methodState != METHOD_CONT && + eap_peer_sm_allow_canned(sm)) + SM_ENTER(EAP, FAILURE); /* EAP-Failure prior any EAP method */ + else if (sm->workaround && sm->rxSuccess && !sm->rxFailure && + !sm->rxReq && sm->methodState != METHOD_CONT && + eap_peer_sm_allow_canned(sm)) + SM_ENTER(EAP, SUCCESS); /* EAP-Success after Identity */ + 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 if (sm->reauthInit) + SM_ENTER(EAP, SEND_RESPONSE); + 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: + /* + * Note: RFC 4137 uses methodState == DONE && decision == FAIL + * as the condition. eapRespData == NULL here is used to allow + * final EAP method response to be sent without having to change + * all methods to either use methodState MAY_CONT or leaving + * decision to something else than FAIL in cases where the only + * expected response is EAP-Failure. + */ + if (sm->ignore) + SM_ENTER(EAP, DISCARD); + else if (sm->methodState == METHOD_DONE && + sm->decision == DECISION_FAIL && !sm->eapRespData) + SM_ENTER(EAP, FAILURE); + else + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_SEND_RESPONSE: + SM_ENTER(EAP, IDLE); + break; + case EAP_DISCARD: + SM_ENTER(EAP, IDLE); + break; + case EAP_IDENTITY: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_NOTIFICATION: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_RETRANSMIT: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_SUCCESS: + break; + case EAP_FAILURE: + break; + } +} + + +SM_STEP(EAP) +{ + /* Global transitions */ + if (eapol_get_bool(sm, EAPOL_eapRestart) && + eapol_get_bool(sm, EAPOL_portEnabled)) + SM_ENTER_GLOBAL(EAP, INITIALIZE); + else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled) + SM_ENTER_GLOBAL(EAP, DISABLED); + else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { + /* RFC 4137 does not place any limit on number of EAP messages + * in an authentication session. However, some error cases have + * ended up in a state were EAP messages were sent between the + * peer and server in a loop (e.g., TLS ACK frame in both + * direction). Since this is quite undesired outcome, limit the + * total number of EAP round-trips and abort authentication if + * this limit is exceeded. + */ + if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { + wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d " + "authentication rounds - abort", + EAP_MAX_AUTH_ROUNDS); + sm->num_rounds++; + SM_ENTER_GLOBAL(EAP, FAILURE); + } + } else { + /* Local transitions */ + eap_peer_sm_step_local(sm); + } +} + + +static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, + EapType method) +{ + if (!eap_allowed_method(sm, vendor, method)) { + wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: " + "vendor %u method %u", vendor, method); + return FALSE; + } + if (eap_peer_get_eap_method(vendor, method)) + return TRUE; + wpa_printf(MSG_DEBUG, "EAP: not included in build: " + "vendor %u method %u", vendor, method); + return FALSE; +} + + +static struct wpabuf * eap_sm_build_expanded_nak( + struct eap_sm *sm, int id, const struct eap_method *methods, + size_t count) +{ + struct wpabuf *resp; + int found = 0; + const struct eap_method *m; + + wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak"); + + /* RFC 3748 - 5.3.2: Expanded Nak */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED, + 8 + 8 * (count + 1), EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_be24(resp, EAP_VENDOR_IETF); + wpabuf_put_be32(resp, EAP_TYPE_NAK); + + for (m = methods; m; m = m->next) { + if (sm->reqVendor == m->vendor && + sm->reqVendorMethod == m->method) + continue; /* do not allow the current method again */ + if (eap_allowed_method(sm, m->vendor, m->method)) { + wpa_printf(MSG_DEBUG, "EAP: allowed type: " + "vendor=%u method=%u", + m->vendor, m->method); + wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); + wpabuf_put_be24(resp, m->vendor); + wpabuf_put_be32(resp, m->method); + + found++; + } + } + if (!found) { + wpa_printf(MSG_DEBUG, "EAP: no more allowed methods"); + wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); + wpabuf_put_be24(resp, EAP_VENDOR_IETF); + wpabuf_put_be32(resp, EAP_TYPE_NONE); + } + + eap_update_len(resp); + + return resp; +} + + +static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id) +{ + struct wpabuf *resp; + u8 *start; + int found = 0, expanded_found = 0; + size_t count; + const struct eap_method *methods, *m; + + wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u " + "vendor=%u method=%u not allowed)", sm->reqMethod, + sm->reqVendor, sm->reqVendorMethod); + methods = eap_peer_get_methods(&count); + if (methods == NULL) + return NULL; + if (sm->reqMethod == EAP_TYPE_EXPANDED) + return eap_sm_build_expanded_nak(sm, id, methods, count); + + /* RFC 3748 - 5.3.1: Legacy Nak */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, + sizeof(struct eap_hdr) + 1 + count + 1, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + start = wpabuf_put(resp, 0); + for (m = methods; m; m = m->next) { + if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod) + continue; /* do not allow the current method again */ + if (eap_allowed_method(sm, m->vendor, m->method)) { + if (m->vendor != EAP_VENDOR_IETF) { + if (expanded_found) + continue; + expanded_found = 1; + wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); + } else + wpabuf_put_u8(resp, m->method); + found++; + } + } + if (!found) + wpabuf_put_u8(resp, EAP_TYPE_NONE); + wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found); + + eap_update_len(resp); + + return resp; +} + + +static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) +{ + const u8 *pos; + size_t msg_len; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED + "EAP authentication started"); + eap_notify_status(sm, "started", ""); + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req, + &msg_len); + if (pos == NULL) + return; + + /* + * RFC 3748 - 5.1: Identity + * Data field may contain a displayable message in UTF-8. If this + * includes NUL-character, only the data before that should be + * displayed. Some EAP implementasitons may piggy-back additional + * options after the NUL. + */ + /* TODO: could save displayable message so that it can be shown to the + * user in case of interaction is required */ + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", + pos, msg_len); +} + + +#ifdef PCSC_FUNCS + +/* + * Rules for figuring out MNC length based on IMSI for SIM cards that do not + * include MNC length field. + */ +static int mnc_len_from_imsi(const char *imsi) +{ + char mcc_str[4]; + unsigned int mcc; + + os_memcpy(mcc_str, imsi, 3); + mcc_str[3] = '\0'; + mcc = atoi(mcc_str); + + if (mcc == 228) + return 2; /* Networks in Switzerland use 2-digit MNC */ + if (mcc == 244) + return 2; /* Networks in Finland use 2-digit MNC */ + + return -1; +} + + +static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi, + size_t max_len, size_t *imsi_len) +{ + int mnc_len; + char *pos, mnc[4]; + + if (*imsi_len + 36 > max_len) { + wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer"); + return -1; + } + + /* MNC (2 or 3 digits) */ + mnc_len = scard_get_mnc_len(sm->scard_ctx); + if (mnc_len < 0) + mnc_len = mnc_len_from_imsi(imsi); + if (mnc_len < 0) { + wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM " + "assuming 3"); + mnc_len = 3; + } + + if (mnc_len == 2) { + mnc[0] = '0'; + mnc[1] = imsi[3]; + mnc[2] = imsi[4]; + } else if (mnc_len == 3) { + mnc[0] = imsi[3]; + mnc[1] = imsi[4]; + mnc[2] = imsi[5]; + } + mnc[3] = '\0'; + + pos = imsi + *imsi_len; + pos += os_snprintf(pos, imsi + max_len - pos, + "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org", + mnc, imsi[0], imsi[1], imsi[2]); + *imsi_len = pos - imsi; + + return 0; +} + + +static int eap_sm_imsi_identity(struct eap_sm *sm, + struct eap_peer_config *conf) +{ + enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM; + char imsi[100]; + size_t imsi_len; + struct eap_method_type *m = conf->eap_methods; + int i; + + imsi_len = sizeof(imsi); + if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) { + wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); + return -1; + } + + wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len); + + if (imsi_len < 7) { + wpa_printf(MSG_WARNING, "Too short IMSI for SIM identity"); + return -1; + } + + if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) { + wpa_printf(MSG_WARNING, "Could not add realm to SIM identity"); + return -1; + } + wpa_hexdump_ascii(MSG_DEBUG, "IMSI + realm", (u8 *) imsi, imsi_len); + + for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF || + m[i].method != EAP_TYPE_NONE); i++) { + if (m[i].vendor == EAP_VENDOR_IETF && + m[i].method == EAP_TYPE_AKA_PRIME) { + method = EAP_SM_AKA_PRIME; + break; + } + + if (m[i].vendor == EAP_VENDOR_IETF && + m[i].method == EAP_TYPE_AKA) { + method = EAP_SM_AKA; + break; + } + } + + os_free(conf->identity); + conf->identity = os_malloc(1 + imsi_len); + if (conf->identity == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate buffer for " + "IMSI-based identity"); + return -1; + } + + switch (method) { + case EAP_SM_SIM: + conf->identity[0] = '1'; + break; + case EAP_SM_AKA: + conf->identity[0] = '0'; + break; + case EAP_SM_AKA_PRIME: + conf->identity[0] = '6'; + break; + } + os_memcpy(conf->identity + 1, imsi, imsi_len); + conf->identity_len = 1 + imsi_len; + + return 0; +} + +#endif /* PCSC_FUNCS */ + + +static int eap_sm_set_scard_pin(struct eap_sm *sm, + struct eap_peer_config *conf) +{ +#ifdef PCSC_FUNCS + if (scard_set_pin(sm->scard_ctx, conf->pin)) { + /* + * Make sure the same PIN is not tried again in order to avoid + * blocking SIM. + */ + os_free(conf->pin); + conf->pin = NULL; + + wpa_printf(MSG_WARNING, "PIN validation failed"); + eap_sm_request_pin(sm); + return -1; + } + return 0; +#else /* PCSC_FUNCS */ + return -1; +#endif /* PCSC_FUNCS */ +} + +static int eap_sm_get_scard_identity(struct eap_sm *sm, + struct eap_peer_config *conf) +{ +#ifdef PCSC_FUNCS + if (eap_sm_set_scard_pin(sm, conf)) + return -1; + + return eap_sm_imsi_identity(sm, conf); +#else /* PCSC_FUNCS */ + return -1; +#endif /* PCSC_FUNCS */ +} + + +/** + * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @id: EAP identifier for the packet + * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2) + * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on + * failure + * + * This function allocates and builds an EAP-Identity/Response packet for the + * current network. The caller is responsible for freeing the returned data. + */ +struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted) +{ + struct eap_peer_config *config = eap_get_config(sm); + struct wpabuf *resp; + const u8 *identity; + size_t identity_len; + + if (config == NULL) { + wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration " + "was not available"); + return NULL; + } + + if (sm->m && sm->m->get_identity && + (identity = sm->m->get_identity(sm, sm->eap_method_priv, + &identity_len)) != NULL) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth " + "identity", identity, identity_len); + } else if (!encrypted && config->anonymous_identity) { + identity = config->anonymous_identity; + identity_len = config->anonymous_identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity", + identity, identity_len); + } else { + identity = config->identity; + identity_len = config->identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity", + identity, identity_len); + } + + if (identity == NULL) { + wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " + "configuration was not available"); + if (config->pcsc) { + if (eap_sm_get_scard_identity(sm, config) < 0) + return NULL; + identity = config->identity; + identity_len = config->identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " + "IMSI", identity, identity_len); + } else { + eap_sm_request_identity(sm); + return NULL; + } + } else if (config->pcsc) { + if (eap_sm_set_scard_pin(sm, config) < 0) + return NULL; + } + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_data(resp, identity, identity_len); + + return resp; +} + + +static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req) +{ + const u8 *pos; + char *msg; + size_t i, msg_len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req, + &msg_len); + if (pos == NULL) + return; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data", + pos, msg_len); + + msg = os_malloc(msg_len + 1); + if (msg == NULL) + return; + for (i = 0; i < msg_len; i++) + msg[i] = isprint(pos[i]) ? (char) pos[i] : '_'; + msg[msg_len] = '\0'; + wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s", + WPA_EVENT_EAP_NOTIFICATION, msg); + os_free(msg); +} + + +static struct wpabuf * eap_sm_buildNotify(int id) +{ + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification"); + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + return resp; +} + + +static void eap_peer_initiate(struct eap_sm *sm, const struct eap_hdr *hdr, + size_t len) +{ +#ifdef CONFIG_ERP + const u8 *pos = (const u8 *) (hdr + 1); + const u8 *end = ((const u8 *) hdr) + len; + struct erp_tlvs parse; + + if (len < sizeof(*hdr) + 1) { + wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Initiate"); + return; + } + + if (*pos != EAP_ERP_TYPE_REAUTH_START) { + wpa_printf(MSG_DEBUG, + "EAP: Ignored unexpected EAP-Initiate Type=%u", + *pos); + return; + } + + pos++; + if (pos >= end) { + wpa_printf(MSG_DEBUG, + "EAP: Too short EAP-Initiate/Re-auth-Start"); + return; + } + pos++; /* Reserved */ + wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth-Start TVs/TLVs", + pos, end - pos); + + if (erp_parse_tlvs(pos, end, &parse, 0) < 0) + goto invalid; + + if (parse.domain) { + wpa_hexdump_ascii(MSG_DEBUG, + "EAP: EAP-Initiate/Re-auth-Start - Domain name", + parse.domain, parse.domain_len); + /* TODO: Derivation of domain specific keys for local ER */ + } + + if (eap_peer_erp_reauth_start(sm, hdr, len) == 0) + return; + +invalid: +#endif /* CONFIG_ERP */ + wpa_printf(MSG_DEBUG, + "EAP: EAP-Initiate/Re-auth-Start - No suitable ERP keys available - try to start full EAP authentication"); + eapol_set_bool(sm, EAPOL_eapTriggerStart, TRUE); +} + + +static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, + size_t len) +{ +#ifdef CONFIG_ERP + const u8 *pos = (const u8 *) (hdr + 1); + const u8 *end = ((const u8 *) hdr) + len; + const u8 *start; + struct erp_tlvs parse; + u8 flags; + u16 seq; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + struct eap_erp_key *erp; + int max_len; + char nai[254]; + u8 seed[4]; + int auth_tag_ok = 0; + + if (len < sizeof(*hdr) + 1) { + wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Finish"); + return; + } + + if (*pos != EAP_ERP_TYPE_REAUTH) { + wpa_printf(MSG_DEBUG, + "EAP: Ignored unexpected EAP-Finish Type=%u", *pos); + return; + } + + if (len < sizeof(*hdr) + 4) { + wpa_printf(MSG_DEBUG, + "EAP: Ignored too short EAP-Finish/Re-auth"); + return; + } + + pos++; + flags = *pos++; + seq = WPA_GET_BE16(pos); + pos += 2; + wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq); + + if (seq != sm->erp_seq) { + wpa_printf(MSG_DEBUG, + "EAP: Unexpected EAP-Finish/Re-auth SEQ=%u", seq); + return; + } + + /* + * Parse TVs/TLVs. Since we do not yet know the length of the + * Authentication Tag, stop parsing if an unknown TV/TLV is seen and + * just try to find the keyName-NAI first so that we can check the + * Authentication Tag. + */ + if (erp_parse_tlvs(pos, end, &parse, 1) < 0) + return; + + if (!parse.keyname) { + wpa_printf(MSG_DEBUG, + "EAP: No keyName-NAI in EAP-Finish/Re-auth Packet"); + return; + } + + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Finish/Re-auth - keyName-NAI", + parse.keyname, parse.keyname_len); + if (parse.keyname_len > 253) { + wpa_printf(MSG_DEBUG, + "EAP: Too long keyName-NAI in EAP-Finish/Re-auth"); + return; + } + os_memcpy(nai, parse.keyname, parse.keyname_len); + nai[parse.keyname_len] = '\0'; + + erp = eap_erp_get_key_nai(sm, nai); + if (!erp) { + wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s", + nai); + return; + } + + /* Is there enough room for Cryptosuite and Authentication Tag? */ + start = parse.keyname + parse.keyname_len; + max_len = end - start; + hash_len = 16; + if (max_len < 1 + (int) hash_len) { + wpa_printf(MSG_DEBUG, + "EAP: Not enough room for Authentication Tag"); + if (flags & 0x80) + goto no_auth_tag; + return; + } + if (end[-17] != EAP_ERP_CS_HMAC_SHA256_128) { + wpa_printf(MSG_DEBUG, "EAP: Different Cryptosuite used"); + if (flags & 0x80) + goto no_auth_tag; + return; + } + + if (hmac_sha256(erp->rIK, erp->rIK_len, (const u8 *) hdr, + end - ((const u8 *) hdr) - hash_len, hash) < 0) + return; + if (os_memcmp(end - hash_len, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, + "EAP: Authentication Tag mismatch"); + return; + } + auth_tag_ok = 1; + end -= 1 + hash_len; + +no_auth_tag: + /* + * Parse TVs/TLVs again now that we know the exact part of the buffer + * that contains them. + */ + wpa_hexdump(MSG_DEBUG, "EAP: EAP-Finish/Re-Auth TVs/TLVs", + pos, end - pos); + if (erp_parse_tlvs(pos, end, &parse, 0) < 0) + return; + + if (flags & 0x80 || !auth_tag_ok) { + wpa_printf(MSG_DEBUG, + "EAP: EAP-Finish/Re-auth indicated failure"); + eapol_set_bool(sm, EAPOL_eapFail, TRUE); + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + 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; + wpa_printf(MSG_DEBUG, + "EAP: Drop ERP key to try full authentication on next attempt"); + eap_peer_erp_free_key(erp); + return; + } + + eap_sm_free_key(sm); + sm->eapKeyDataLen = 0; + sm->eapKeyData = os_malloc(erp->rRK_len); + if (!sm->eapKeyData) + return; + sm->eapKeyDataLen = erp->rRK_len; + + WPA_PUT_BE16(seed, seq); + WPA_PUT_BE16(&seed[2], erp->rRK_len); + if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, + "Re-authentication Master Session Key@ietf.org", + seed, sizeof(seed), + sm->eapKeyData, erp->rRK_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP"); + eap_sm_free_key(sm); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK", + sm->eapKeyData, sm->eapKeyDataLen); + sm->eapKeyAvailable = TRUE; + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + "EAP re-authentication completed successfully"); +#endif /* CONFIG_ERP */ +} + + +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); + sha1_vector(1, addr, &plen, sm->req_sha1); + } + + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (plen < sizeof(*hdr) + 1) { + wpa_printf(MSG_DEBUG, "EAP: Too short EAP-Request - " + "no Type field"); + return; + } + sm->rxReq = TRUE; + pos = (const u8 *) (hdr + 1); + sm->reqMethod = *pos++; + if (sm->reqMethod == EAP_TYPE_EXPANDED) { + if (plen < sizeof(*hdr) + 8) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated " + "expanded EAP-Packet (plen=%lu)", + (unsigned long) plen); + return; + } + sm->reqVendor = WPA_GET_BE24(pos); + pos += 3; + sm->reqVendorMethod = WPA_GET_BE32(pos); + } + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request id=%d " + "method=%u vendor=%u vendorMethod=%u", + sm->reqId, sm->reqMethod, sm->reqVendor, + sm->reqVendorMethod); + break; + case EAP_CODE_RESPONSE: + if (sm->selectedMethod == EAP_TYPE_LEAP) { + /* + * LEAP differs from RFC 4137 by using reversed roles + * for mutual authentication and because of this, we + * need to accept EAP-Response frames if LEAP is used. + */ + if (plen < sizeof(*hdr) + 1) { + wpa_printf(MSG_DEBUG, "EAP: Too short " + "EAP-Response - no Type field"); + return; + } + sm->rxResp = TRUE; + pos = (const u8 *) (hdr + 1); + sm->reqMethod = *pos; + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for " + "LEAP method=%d id=%d", + sm->reqMethod, sm->reqId); + break; + } + wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response"); + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success"); + eap_notify_status(sm, "completion", "success"); + sm->rxSuccess = TRUE; + break; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); + eap_notify_status(sm, "completion", "failure"); + sm->rxFailure = TRUE; + break; + case EAP_CODE_INITIATE: + eap_peer_initiate(sm, hdr, plen); + break; + case EAP_CODE_FINISH: + eap_peer_finish(sm, hdr, plen); + break; + default: + wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown " + "code %d", hdr->code); + break; + } +} + + +static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, + union tls_event_data *data) +{ + struct eap_sm *sm = ctx; + char *hash_hex = NULL; + + switch (ev) { + case TLS_CERT_CHAIN_SUCCESS: + eap_notify_status(sm, "remote certificate verification", + "success"); + break; + case TLS_CERT_CHAIN_FAILURE: + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR + "reason=%d depth=%d subject='%s' err='%s'", + data->cert_fail.reason, + data->cert_fail.depth, + data->cert_fail.subject, + data->cert_fail.reason_txt); + eap_notify_status(sm, "remote certificate verification", + data->cert_fail.reason_txt); + break; + case TLS_PEER_CERTIFICATE: + if (!sm->eapol_cb->notify_cert) + break; + + if (data->peer_cert.hash) { + size_t len = data->peer_cert.hash_len * 2 + 1; + hash_hex = os_malloc(len); + if (hash_hex) { + wpa_snprintf_hex(hash_hex, len, + data->peer_cert.hash, + data->peer_cert.hash_len); + } + } + + sm->eapol_cb->notify_cert(sm->eapol_ctx, + data->peer_cert.depth, + data->peer_cert.subject, + data->peer_cert.altsubject, + data->peer_cert.num_altsubject, + hash_hex, data->peer_cert.cert); + break; + case TLS_ALERT: + if (data->alert.is_local) + eap_notify_status(sm, "local TLS alert", + data->alert.description); + else + eap_notify_status(sm, "remote TLS alert", + data->alert.description); + break; + } + + os_free(hash_hex); +} + + +/** + * eap_peer_sm_init - Allocate and initialize EAP peer state machine + * @eapol_ctx: Context data to be used with eapol_cb calls + * @eapol_cb: Pointer to EAPOL callback functions + * @msg_ctx: Context data for wpa_msg() calls + * @conf: EAP configuration + * Returns: Pointer to the allocated EAP state machine or %NULL on failure + * + * This function allocates and initializes an EAP state machine. In addition, + * this initializes TLS library for the new EAP state machine. eapol_cb pointer + * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP + * state machine. Consequently, the caller must make sure that this data + * structure remains alive while the EAP state machine is active. + */ +struct eap_sm * eap_peer_sm_init(void *eapol_ctx, + const 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; + dl_list_init(&sm->erp_keys); + + 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; + tlsconf.openssl_ciphers = conf->openssl_ciphers; +#ifdef CONFIG_FIPS + tlsconf.fips_mode = 1; +#endif /* CONFIG_FIPS */ + tlsconf.event_cb = eap_peer_sm_tls_event; + tlsconf.cb_ctx = sm; + tlsconf.cert_in_cb = conf->cert_in_cb; + sm->ssl_ctx = tls_init(&tlsconf); + if (sm->ssl_ctx == NULL) { + wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " + "context."); + os_free(sm); + return NULL; + } + + sm->ssl_ctx2 = tls_init(&tlsconf); + if (sm->ssl_ctx2 == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS " + "context (2)."); + /* Run without separate TLS context within TLS tunnel */ + } + + return sm; +} + + +/** + * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * This function deinitializes EAP state machine and frees all allocated + * resources. + */ +void eap_peer_sm_deinit(struct eap_sm *sm) +{ + if (sm == NULL) + return; + eap_deinit_prev_method(sm, "EAP deinit"); + eap_sm_abort(sm); + if (sm->ssl_ctx2) + tls_deinit(sm->ssl_ctx2); + tls_deinit(sm->ssl_ctx); + eap_peer_erp_free_keys(sm); + 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; + eap_sm_free_key(sm); + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; + + /* This is not clearly specified in the EAP statemachines draft, but + * it seems necessary to make sure that some of the EAPOL variables get + * cleared for the next authentication. */ + eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); +} + + +#ifdef CONFIG_CTRL_IFACE +static const char * eap_sm_state_txt(int state) +{ + switch (state) { + case EAP_INITIALIZE: + return "INITIALIZE"; + case EAP_DISABLED: + return "DISABLED"; + case EAP_IDLE: + return "IDLE"; + case EAP_RECEIVED: + return "RECEIVED"; + case EAP_GET_METHOD: + return "GET_METHOD"; + case EAP_METHOD: + return "METHOD"; + case EAP_SEND_RESPONSE: + return "SEND_RESPONSE"; + case EAP_DISCARD: + return "DISCARD"; + case EAP_IDENTITY: + return "IDENTITY"; + case EAP_NOTIFICATION: + return "NOTIFICATION"; + case EAP_RETRANSMIT: + return "RETRANSMIT"; + case EAP_SUCCESS: + return "SUCCESS"; + case EAP_FAILURE: + return "FAILURE"; + default: + return "UNKNOWN"; + } +} +#endif /* CONFIG_CTRL_IFACE */ + + +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static const char * eap_sm_method_state_txt(EapMethodState state) +{ + switch (state) { + case METHOD_NONE: + return "NONE"; + case METHOD_INIT: + return "INIT"; + case METHOD_CONT: + return "CONT"; + case METHOD_MAY_CONT: + return "MAY_CONT"; + case METHOD_DONE: + return "DONE"; + default: + return "UNKNOWN"; + } +} + + +static const char * eap_sm_decision_txt(EapDecision decision) +{ + switch (decision) { + case DECISION_FAIL: + return "FAIL"; + case DECISION_COND_SUCC: + return "COND_SUCC"; + case DECISION_UNCOND_SUCC: + return "UNCOND_SUCC"; + default: + return "UNKNOWN"; + } +} +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + +#ifdef CONFIG_CTRL_IFACE + +/** + * eap_sm_get_status - Get EAP state machine status + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query EAP state machine for status information. This function fills in a + * text area with current status information from the EAPOL state machine. If + * the buffer (buf) is not large enough, status information will be truncated + * to fit the buffer. + */ +int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) +{ + int len, ret; + + if (sm == NULL) + return 0; + + len = os_snprintf(buf, buflen, + "EAP state=%s\n", + eap_sm_state_txt(sm->EAP_state)); + if (os_snprintf_error(buflen, len)) + 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 (os_snprintf_error(buflen - len, ret)) + 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 (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + return len; +} +#endif /* CONFIG_CTRL_IFACE */ + + +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, + const char *msg, size_t msglen) +{ + struct eap_peer_config *config; + const char *txt = NULL; + char *tmp; + + if (sm == NULL) + return; + config = eap_get_config(sm); + if (config == NULL) + return; + + switch (field) { + case WPA_CTRL_REQ_EAP_IDENTITY: + config->pending_req_identity++; + break; + case WPA_CTRL_REQ_EAP_PASSWORD: + config->pending_req_password++; + break; + case WPA_CTRL_REQ_EAP_NEW_PASSWORD: + config->pending_req_new_password++; + break; + case WPA_CTRL_REQ_EAP_PIN: + config->pending_req_pin++; + break; + case WPA_CTRL_REQ_EAP_OTP: + if (msg) { + tmp = os_malloc(msglen + 3); + if (tmp == NULL) + return; + tmp[0] = '['; + os_memcpy(tmp + 1, msg, msglen); + tmp[msglen + 1] = ']'; + tmp[msglen + 2] = '\0'; + txt = tmp; + os_free(config->pending_req_otp); + config->pending_req_otp = tmp; + config->pending_req_otp_len = msglen + 3; + } else { + if (config->pending_req_otp == NULL) + return; + txt = config->pending_req_otp; + } + break; + case WPA_CTRL_REQ_EAP_PASSPHRASE: + config->pending_req_passphrase++; + break; + case WPA_CTRL_REQ_SIM: + txt = msg; + break; + default: + return; + } + + if (sm->eapol_cb->eap_param_needed) + sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt); +} +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +#define eap_sm_request(sm, type, msg, msglen) do { } while (0) +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + +const char * eap_sm_get_method_name(struct eap_sm *sm) +{ + if (sm->m == NULL) + return "UNKNOWN"; + return sm->m->name; +} + + +/** + * eap_sm_request_identity - Request identity from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request identity information for the + * current network. This is normally called when the identity is not included + * in the network configuration. The request will be sent to monitor programs + * through the control interface. + */ +void eap_sm_request_identity(struct eap_sm *sm) +{ + eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0); +} + + +/** + * eap_sm_request_password - Request password from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request password information for the + * current network. This is normally called when the password is not included + * in the network configuration. The request will be sent to monitor programs + * through the control interface. + */ +void eap_sm_request_password(struct eap_sm *sm) +{ + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0); +} + + +/** + * eap_sm_request_new_password - Request new password from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request new password information for + * the current network. This is normally called when the EAP method indicates + * that the current password has expired and password change is required. The + * request will be sent to monitor programs through the control interface. + */ +void eap_sm_request_new_password(struct eap_sm *sm) +{ + eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0); +} + + +/** + * eap_sm_request_pin - Request SIM or smart card PIN from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request SIM or smart card PIN + * information for the current network. This is normally called when the PIN is + * not included in the network configuration. The request will be sent to + * monitor programs through the control interface. + */ +void eap_sm_request_pin(struct eap_sm *sm) +{ + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0); +} + + +/** + * eap_sm_request_otp - Request one time password from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @msg: Message to be displayed to the user when asking for OTP + * @msg_len: Length of the user displayable message + * + * EAP methods can call this function to request open time password (OTP) for + * the current network. The request will be sent to monitor programs through + * the control interface. + */ +void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len) +{ + eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len); +} + + +/** + * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request passphrase for a private key + * for the current network. This is normally called when the passphrase is not + * included in the network configuration. The request will be sent to monitor + * programs through the control interface. + */ +void eap_sm_request_passphrase(struct eap_sm *sm) +{ + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0); +} + + +/** + * eap_sm_request_sim - Request external SIM processing + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @req: EAP method specific request + */ +void eap_sm_request_sim(struct eap_sm *sm, const char *req) +{ + eap_sm_request(sm, WPA_CTRL_REQ_SIM, req, os_strlen(req)); +} + + +/** + * eap_sm_notify_ctrl_attached - Notification of attached monitor + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * Notify EAP state machines that a monitor was attached to the control + * interface to trigger re-sending of pending requests for user input. + */ +void eap_sm_notify_ctrl_attached(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + + if (config == NULL) + return; + + /* Re-send any pending requests for user data since a new control + * interface was added. This handles cases where the EAP authentication + * starts immediately after system startup when the user interface is + * not yet running. */ + if (config->pending_req_identity) + eap_sm_request_identity(sm); + if (config->pending_req_password) + eap_sm_request_password(sm); + if (config->pending_req_new_password) + eap_sm_request_new_password(sm); + if (config->pending_req_otp) + eap_sm_request_otp(sm, NULL, 0); + if (config->pending_req_pin) + eap_sm_request_pin(sm); + if (config->pending_req_passphrase) + eap_sm_request_passphrase(sm); +} + + +static int eap_allowed_phase2_type(int vendor, int type) +{ + if (vendor != EAP_VENDOR_IETF) + return 0; + return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS && + type != EAP_TYPE_FAST; +} + + +/** + * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name + * @name: EAP method name, e.g., MD5 + * @vendor: Buffer for returning EAP Vendor-Id + * Returns: EAP method type or %EAP_TYPE_NONE if not found + * + * This function maps EAP type names into EAP type numbers that are allowed for + * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with + * EAP-PEAP, EAP-TTLS, and EAP-FAST. + */ +u32 eap_get_phase2_type(const char *name, int *vendor) +{ + int v; + u32 type = eap_peer_get_type(name, &v); + if (eap_allowed_phase2_type(v, type)) { + *vendor = v; + return type; + } + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + + +/** + * eap_get_phase2_types - Get list of allowed EAP phase 2 types + * @config: Pointer to a network configuration + * @count: Pointer to a variable to be filled with number of returned EAP types + * Returns: Pointer to allocated type list or %NULL on failure + * + * This function generates an array of allowed EAP phase 2 (tunneled) types for + * the given network configuration. + */ +struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, + size_t *count) +{ + struct eap_method_type *buf; + u32 method; + int vendor; + size_t mcount; + const struct eap_method *methods, *m; + + methods = eap_peer_get_methods(&mcount); + if (methods == NULL) + return NULL; + *count = 0; + buf = os_malloc(mcount * sizeof(struct eap_method_type)); + if (buf == NULL) + return NULL; + + for (m = methods; m; m = m->next) { + vendor = m->vendor; + method = m->method; + if (eap_allowed_phase2_type(vendor, method)) { + if (vendor == EAP_VENDOR_IETF && + method == EAP_TYPE_TLS && config && + config->private_key2 == NULL) + continue; + buf[*count].vendor = vendor; + buf[*count].method = method; + (*count)++; + } + } + + return buf; +} + + +/** + * eap_set_fast_reauth - Update fast_reauth setting + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @enabled: 1 = Fast reauthentication is enabled, 0 = Disabled + */ +void eap_set_fast_reauth(struct eap_sm *sm, int enabled) +{ + sm->fast_reauth = enabled; +} + + +/** + * eap_set_workaround - Update EAP workarounds setting + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds + */ +void eap_set_workaround(struct eap_sm *sm, unsigned int workaround) +{ + sm->workaround = workaround; +} + + +/** + * eap_get_config - Get current network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the current network configuration or %NULL if not found + * + * EAP peer methods should avoid using this function if they can use other + * access functions, like eap_get_config_identity() and + * eap_get_config_password(), that do not require direct access to + * struct eap_peer_config. + */ +struct eap_peer_config * eap_get_config(struct eap_sm *sm) +{ + return sm->eapol_cb->get_config(sm->eapol_ctx); +} + + +/** + * eap_get_config_identity - Get identity from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the identity + * Returns: Pointer to the identity or %NULL if not found + */ +const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->identity_len; + return config->identity; +} + + +static int eap_get_ext_password(struct eap_sm *sm, + struct eap_peer_config *config) +{ + char *name; + + if (config->password == NULL) + return -1; + + name = os_zalloc(config->password_len + 1); + if (name == NULL) + return -1; + os_memcpy(name, config->password, config->password_len); + + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = ext_password_get(sm->ext_pw, name); + os_free(name); + + return sm->ext_pw_buf == NULL ? -1 : 0; +} + + +/** + * eap_get_config_password - Get password from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the password + * Returns: Pointer to the password or %NULL if not found + */ +const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if (eap_get_ext_password(sm, config) < 0) + return NULL; + *len = wpabuf_len(sm->ext_pw_buf); + return wpabuf_head(sm->ext_pw_buf); + } + + *len = config->password_len; + return config->password; +} + + +/** + * eap_get_config_password2 - Get password from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the password + * @hash: Buffer for returning whether the password is stored as a + * NtPasswordHash instead of plaintext password; can be %NULL if this + * information is not needed + * Returns: Pointer to the password or %NULL if not found + */ +const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if (eap_get_ext_password(sm, config) < 0) + return NULL; + if (hash) + *hash = 0; + *len = wpabuf_len(sm->ext_pw_buf); + return wpabuf_head(sm->ext_pw_buf); + } + + *len = config->password_len; + if (hash) + *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); + return config->password; +} + + +/** + * eap_get_config_new_password - Get new password from network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the new password + * Returns: Pointer to the new password or %NULL if not found + */ +const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->new_password_len; + return config->new_password; +} + + +/** + * eap_get_config_otp - Get one-time password from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the one-time password + * Returns: Pointer to the one-time password or %NULL if not found + */ +const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->otp_len; + return config->otp; +} + + +/** + * eap_clear_config_otp - Clear used one-time password + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * This function clears a used one-time password (OTP) from the current network + * configuration. This should be called when the OTP has been used and is not + * needed anymore. + */ +void eap_clear_config_otp(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return; + os_memset(config->otp, 0, config->otp_len); + os_free(config->otp); + config->otp = NULL; + config->otp_len = 0; +} + + +/** + * eap_get_config_phase1 - Get phase1 data from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the phase1 data or %NULL if not found + */ +const char * eap_get_config_phase1(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + return config->phase1; +} + + +/** + * eap_get_config_phase2 - Get phase2 data from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the phase1 data or %NULL if not found + */ +const char * eap_get_config_phase2(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + return config->phase2; +} + + +int eap_get_config_fragment_size(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return -1; + return config->fragment_size; +} + + +/** + * eap_key_available - Get key availability (eapKeyAvailable variable) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: 1 if EAP keying material is available, 0 if not + */ +int eap_key_available(struct eap_sm *sm) +{ + return sm ? sm->eapKeyAvailable : 0; +} + + +/** + * eap_notify_success - Notify EAP state machine about external success trigger + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * This function is called when external event, e.g., successful completion of + * WPA-PSK key handshake, is indicating that EAP state machine should move to + * success state. This is mainly used with security modes that do not use EAP + * state machine (e.g., WPA-PSK). + */ +void eap_notify_success(struct eap_sm *sm) +{ + if (sm) { + sm->decision = DECISION_COND_SUCC; + sm->EAP_state = EAP_SUCCESS; + } +} + + +/** + * eap_notify_lower_layer_success - Notification of lower layer success + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * Notify EAP state machines that a lower layer has detected a successful + * authentication. This is used to recover from dropped EAP-Success messages. + */ +void eap_notify_lower_layer_success(struct eap_sm *sm) +{ + if (sm == NULL) + return; + + if (eapol_get_bool(sm, EAPOL_eapSuccess) || + sm->decision == DECISION_FAIL || + (sm->methodState != METHOD_MAY_CONT && + sm->methodState != METHOD_DONE)) + return; + + if (sm->eapKeyData != NULL) + sm->eapKeyAvailable = TRUE; + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + "EAP authentication completed successfully (based on lower " + "layer success)"); +} + + +/** + * eap_get_eapSessionId - Get Session-Id from EAP state machine + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Pointer to variable that will be set to number of bytes in the session + * Returns: Pointer to the EAP Session-Id or %NULL on failure + * + * Fetch EAP Session-Id from the EAP state machine. The Session-Id is available + * only after a successful authentication. EAP state machine continues to manage + * the Session-Id and the caller must not change or free the returned data. + */ +const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len) +{ + if (sm == NULL || sm->eapSessionId == NULL) { + *len = 0; + return NULL; + } + + *len = sm->eapSessionIdLen; + return sm->eapSessionId; +} + + +/** + * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Pointer to variable that will be set to number of bytes in the key + * Returns: Pointer to the EAP keying data or %NULL on failure + * + * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The + * key is available only after a successful authentication. EAP state machine + * continues to manage the key data and the caller must not change or free the + * returned data. + */ +const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len) +{ + if (sm == NULL || sm->eapKeyData == NULL) { + *len = 0; + return NULL; + } + + *len = sm->eapKeyDataLen; + return sm->eapKeyData; +} + + +/** + * eap_get_eapKeyData - Get EAP response data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure + * + * Fetch EAP response (eapRespData) from the EAP state machine. This data is + * available when EAP state machine has processed an incoming EAP request. The + * EAP state machine does not maintain a reference to the response after this + * function is called and the caller is responsible for freeing the data. + */ +struct wpabuf * eap_get_eapRespData(struct eap_sm *sm) +{ + struct wpabuf *resp; + + if (sm == NULL || sm->eapRespData == NULL) + return NULL; + + resp = sm->eapRespData; + sm->eapRespData = NULL; + + return resp; +} + + +/** + * eap_sm_register_scard_ctx - Notification of smart card context + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @ctx: Context data for smart card operations + * + * Notify EAP state machines of context data for smart card operations. This + * context data will be used as a parameter for scard_*() functions. + */ +void eap_register_scard_ctx(struct eap_sm *sm, void *ctx) +{ + if (sm) + sm->scard_ctx = ctx; +} + + +/** + * eap_set_config_blob - Set or add a named configuration blob + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an existing + * blob. + */ +void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob); +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + +/** + * eap_get_config_blob - Get a named configuration blob + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ +const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, + const char *name) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name); +#else /* CONFIG_NO_CONFIG_BLOBS */ + return NULL; +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + +/** + * eap_set_force_disabled - Set force_disabled flag + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @disabled: 1 = EAP disabled, 0 = EAP enabled + * + * This function is used to force EAP state machine to be disabled when it is + * not in use (e.g., with WPA-PSK or plaintext connections). + */ +void eap_set_force_disabled(struct eap_sm *sm, int disabled) +{ + sm->force_disabled = disabled; +} + + +/** + * eap_set_external_sim - Set external_sim flag + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @external_sim: Whether external SIM/USIM processing is used + */ +void eap_set_external_sim(struct eap_sm *sm, int external_sim) +{ + sm->external_sim = external_sim; +} + + + /** + * eap_notify_pending - Notify that EAP method is ready to re-process a request + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * An EAP method can perform a pending operation (e.g., to get a response from + * an external process). Once the response is available, this function can be + * used to request EAPOL state machine to retry delivering the previously + * received (and still unanswered) EAP request to EAP state machine. + */ +void eap_notify_pending(struct eap_sm *sm) +{ + sm->eapol_cb->notify_pending(sm->eapol_ctx); +} + + +/** + * eap_invalidate_cached_session - Mark cached session data invalid + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + */ +void eap_invalidate_cached_session(struct eap_sm *sm) +{ + if (sm) + eap_deinit_prev_method(sm, "invalidate"); +} + + +int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf) +{ + if (conf->identity_len != WSC_ID_ENROLLEE_LEN || + os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)) + return 0; /* Not a WPS Enrollee */ + + if (conf->phase1 == NULL || os_strstr(conf->phase1, "pbc=1") == NULL) + return 0; /* Not using PBC */ + + return 1; +} + + +int eap_is_wps_pin_enrollee(struct eap_peer_config *conf) +{ + if (conf->identity_len != WSC_ID_ENROLLEE_LEN || + os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)) + return 0; /* Not a WPS Enrollee */ + + if (conf->phase1 == NULL || os_strstr(conf->phase1, "pin=") == NULL) + return 0; /* Not using PIN */ + + return 1; +} + + +void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext) +{ + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = NULL; + sm->ext_pw = ext; +} + + +/** + * eap_set_anon_id - Set or add anonymous identity + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear + * @len: Length of anonymous identity in octets + */ +void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len) +{ + if (sm->eapol_cb->set_anon_id) + sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len); +} + + +int eap_peer_was_failure_expected(struct eap_sm *sm) +{ + return sm->expected_failure; +} diff --git a/freebsd/contrib/wpa/src/eap_peer/eap.h b/freebsd/contrib/wpa/src/eap_peer/eap.h new file mode 100644 index 00000000..1a645af8 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap.h @@ -0,0 +1,354 @@ +/* + * EAP peer state machine functions (RFC 4137) + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_H +#define EAP_H + +#include "common/defs.h" +#include "eap_common/eap_defs.h" +#include "eap_peer/eap_methods.h" + +struct eap_sm; +struct wpa_config_blob; +struct wpabuf; + +struct eap_method_type { + int vendor; + u32 method; +}; + +#ifdef IEEE8021X_EAPOL + +/** + * enum eapol_bool_var - EAPOL boolean state variables for EAP state machine + * + * These variables are used in the interface between EAP peer state machine and + * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is + * expected to maintain these variables and register a callback functions for + * EAP state machine to get and set the variables. + */ +enum eapol_bool_var { + /** + * EAPOL_eapSuccess - EAP SUCCESS state reached + * + * EAP state machine reads and writes this value. + */ + EAPOL_eapSuccess, + + /** + * EAPOL_eapRestart - Lower layer request to restart authentication + * + * Set to TRUE in lower layer, FALSE in EAP state machine. + */ + EAPOL_eapRestart, + + /** + * EAPOL_eapFail - EAP FAILURE state reached + * + * EAP state machine writes this value. + */ + EAPOL_eapFail, + + /** + * EAPOL_eapResp - Response to send + * + * Set to TRUE in EAP state machine, FALSE in lower layer. + */ + EAPOL_eapResp, + + /** + * EAPOL_eapNoResp - Request has been process; no response to send + * + * Set to TRUE in EAP state machine, FALSE in lower layer. + */ + EAPOL_eapNoResp, + + /** + * EAPOL_eapReq - EAP request available from lower layer + * + * Set to TRUE in lower layer, FALSE in EAP state machine. + */ + EAPOL_eapReq, + + /** + * EAPOL_portEnabled - Lower layer is ready for communication + * + * EAP state machines reads this value. + */ + EAPOL_portEnabled, + + /** + * EAPOL_altAccept - Alternate indication of success (RFC3748) + * + * EAP state machines reads this value. + */ + EAPOL_altAccept, + + /** + * EAPOL_altReject - Alternate indication of failure (RFC3748) + * + * EAP state machines reads this value. + */ + EAPOL_altReject, + + /** + * EAPOL_eapTriggerStart - EAP-based trigger to send EAPOL-Start + * + * EAP state machine writes this value. + */ + EAPOL_eapTriggerStart +}; + +/** + * enum eapol_int_var - EAPOL integer state variables for EAP state machine + * + * These variables are used in the interface between EAP peer state machine and + * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is + * expected to maintain these variables and register a callback functions for + * EAP state machine to get and set the variables. + */ +enum eapol_int_var { + /** + * EAPOL_idleWhile - Outside time for EAP peer timeout + * + * This integer variable is used to provide an outside timer that the + * external (to EAP state machine) code must decrement by one every + * second until the value reaches zero. This is used in the same way as + * EAPOL state machine timers. EAP state machine reads and writes this + * value. + */ + EAPOL_idleWhile +}; + +/** + * struct eapol_callbacks - Callback functions from EAP to lower layer + * + * This structure defines the callback functions that EAP state machine + * requires from the lower layer (usually EAPOL state machine) for updating + * state variables and requesting information. eapol_ctx from + * eap_peer_sm_init() call will be used as the ctx parameter for these + * callback functions. + */ +struct eapol_callbacks { + /** + * get_config - Get pointer to the current network configuration + * @ctx: eapol_ctx from eap_peer_sm_init() call + */ + struct eap_peer_config * (*get_config)(void *ctx); + + /** + * get_bool - Get a boolean EAPOL state variable + * @variable: EAPOL boolean variable to get + * Returns: Value of the EAPOL variable + */ + Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable); + + /** + * set_bool - Set a boolean EAPOL state variable + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @variable: EAPOL boolean variable to set + * @value: Value for the EAPOL variable + */ + void (*set_bool)(void *ctx, enum eapol_bool_var variable, + Boolean value); + + /** + * get_int - Get an integer EAPOL state variable + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @variable: EAPOL integer variable to get + * Returns: Value of the EAPOL variable + */ + unsigned int (*get_int)(void *ctx, enum eapol_int_var variable); + + /** + * set_int - Set an integer EAPOL state variable + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @variable: EAPOL integer variable to set + * @value: Value for the EAPOL variable + */ + void (*set_int)(void *ctx, enum eapol_int_var variable, + unsigned int value); + + /** + * get_eapReqData - Get EAP-Request data + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @len: Pointer to variable that will be set to eapReqDataLen + * Returns: Reference to eapReqData (EAP state machine will not free + * this) or %NULL if eapReqData not available. + */ + struct wpabuf * (*get_eapReqData)(void *ctx); + + /** + * set_config_blob - Set named configuration blob + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an + * existing blob. + */ + void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); + + /** + * get_config_blob - Get a named configuration blob + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ + const struct wpa_config_blob * (*get_config_blob)(void *ctx, + const char *name); + + /** + * notify_pending - Notify that a pending request can be retried + * @ctx: eapol_ctx from eap_peer_sm_init() call + * + * An EAP method can perform a pending operation (e.g., to get a + * response from an external process). Once the response is available, + * this callback function can be used to request EAPOL state machine to + * retry delivering the previously received (and still unanswered) EAP + * request to EAP state machine. + */ + void (*notify_pending)(void *ctx); + + /** + * eap_param_needed - Notify that EAP parameter is needed + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY) + * @txt: User readable text describing the required parameter + */ + void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field, + const char *txt); + + /** + * notify_cert - Notification of a peer certificate + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @depth: Depth in certificate chain (0 = server) + * @subject: Subject of the peer certificate + * @altsubject: Select fields from AltSubject of the peer certificate + * @num_altsubject: Number of altsubject values + * @cert_hash: SHA-256 hash of the certificate + * @cert: Peer certificate + */ + void (*notify_cert)(void *ctx, int depth, const char *subject, + const char *altsubject[], int num_altsubject, + const char *cert_hash, const struct wpabuf *cert); + + /** + * notify_status - Notification of the current EAP state + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @status: Step in the process of EAP authentication + * @parameter: Step-specific parameter, e.g., EAP method name + */ + void (*notify_status)(void *ctx, const char *status, + const char *parameter); + +#ifdef CONFIG_EAP_PROXY + /** + * eap_proxy_cb - Callback signifying any updates from eap_proxy + * @ctx: eapol_ctx from eap_peer_sm_init() call + */ + void (*eap_proxy_cb)(void *ctx); +#endif /* CONFIG_EAP_PROXY */ + + /** + * set_anon_id - Set or add anonymous identity + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear + * @len: Length of anonymous identity in octets + */ + void (*set_anon_id)(void *ctx, const u8 *id, size_t len); +}; + +/** + * struct eap_config - Configuration for EAP state machine + */ +struct eap_config { + /** + * opensc_engine_path - OpenSC engine for OpenSSL engine support + * + * Usually, path to engine_opensc.so. + */ + const char *opensc_engine_path; + /** + * pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support + * + * Usually, path to engine_pkcs11.so. + */ + const char *pkcs11_engine_path; + /** + * pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine + * + * Usually, path to opensc-pkcs11.so. + */ + const char *pkcs11_module_path; + /** + * openssl_ciphers - OpenSSL cipher string + * + * This is an OpenSSL specific configuration option for configuring the + * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the + * default. + */ + const char *openssl_ciphers; + /** + * wps - WPS context data + * + * This is only used by EAP-WSC and can be left %NULL if not available. + */ + struct wps_context *wps; + + /** + * cert_in_cb - Include server certificates in callback + */ + int cert_in_cb; +}; + +struct eap_sm * eap_peer_sm_init(void *eapol_ctx, + const struct eapol_callbacks *eapol_cb, + void *msg_ctx, struct eap_config *conf); +void eap_peer_sm_deinit(struct eap_sm *sm); +int eap_peer_sm_step(struct eap_sm *sm); +void eap_sm_abort(struct eap_sm *sm); +int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, + int verbose); +const char * eap_sm_get_method_name(struct eap_sm *sm); +struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted); +void eap_sm_request_identity(struct eap_sm *sm); +void eap_sm_request_password(struct eap_sm *sm); +void eap_sm_request_new_password(struct eap_sm *sm); +void eap_sm_request_pin(struct eap_sm *sm); +void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len); +void eap_sm_request_passphrase(struct eap_sm *sm); +void eap_sm_request_sim(struct eap_sm *sm, const char *req); +void eap_sm_notify_ctrl_attached(struct eap_sm *sm); +u32 eap_get_phase2_type(const char *name, int *vendor); +struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, + size_t *count); +void eap_set_fast_reauth(struct eap_sm *sm, int enabled); +void eap_set_workaround(struct eap_sm *sm, unsigned int workaround); +void eap_set_force_disabled(struct eap_sm *sm, int disabled); +void eap_set_external_sim(struct eap_sm *sm, int external_sim); +int eap_key_available(struct eap_sm *sm); +void eap_notify_success(struct eap_sm *sm); +void eap_notify_lower_layer_success(struct eap_sm *sm); +const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len); +const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len); +struct wpabuf * eap_get_eapRespData(struct eap_sm *sm); +void eap_register_scard_ctx(struct eap_sm *sm, void *ctx); +void eap_invalidate_cached_session(struct eap_sm *sm); + +int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf); +int eap_is_wps_pin_enrollee(struct eap_peer_config *conf); + +struct ext_password_data; +void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext); +void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len); +int eap_peer_was_failure_expected(struct eap_sm *sm); +void eap_peer_erp_free_keys(struct eap_sm *sm); + +#endif /* IEEE8021X_EAPOL */ + +#endif /* EAP_H */ diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_config.h b/freebsd/contrib/wpa/src/eap_peer/eap_config.h new file mode 100644 index 00000000..2b1a1d5e --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_config.h @@ -0,0 +1,774 @@ +/* + * EAP peer configuration data + * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_CONFIG_H +#define EAP_CONFIG_H + +/** + * struct eap_peer_config - EAP peer configuration/credentials + */ +struct eap_peer_config { + /** + * identity - EAP Identity + * + * This field is used to set the real user identity or NAI (for + * EAP-PSK/PAX/SAKE/GPSK). + */ + u8 *identity; + + /** + * identity_len - EAP Identity length + */ + size_t identity_len; + + /** + * anonymous_identity - Anonymous EAP Identity + * + * This field is used for unencrypted use with EAP types that support + * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the + * real identity (identity field) only to the authentication server. + * + * If not set, the identity field will be used for both unencrypted and + * protected fields. + * + * This field can also be used with EAP-SIM/AKA/AKA' to store the + * pseudonym identity. + */ + u8 *anonymous_identity; + + /** + * anonymous_identity_len - Length of anonymous_identity + */ + size_t anonymous_identity_len; + + /** + * password - Password string for EAP + * + * This field can include either the plaintext password (default + * option) or a NtPasswordHash (16-byte MD4 hash of the unicode + * presentation of the password) if flags field has + * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can + * only be used with authentication mechanism that use this hash as the + * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2, + * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP). + * + * In addition, this field is used to configure a pre-shared key for + * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK + * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length + * PSK. + */ + u8 *password; + + /** + * password_len - Length of password field + */ + size_t password_len; + + /** + * ca_cert - File path to CA certificate file (PEM/DER) + * + * This file can have one or more trusted CA certificates. If ca_cert + * and ca_path are not included, server certificate will not be + * verified. This is insecure and a trusted CA certificate should + * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + * + * Alternatively, this can be used to only perform matching of the + * server certificate (SHA-256 hash of the DER encoded X.509 + * certificate). In this case, the possible CA certificates in the + * server certificate chain are ignored and only the server certificate + * is verified. This is configured with the following format: + * hash:://server/sha256/cert_hash_in_hex + * For example: "hash://server/sha256/ + * 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a" + * + * On Windows, trusted CA certificates can be loaded from the system + * certificate store by setting this to cert_store://name, e.g., + * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT". + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + */ + u8 *ca_cert; + + /** + * ca_path - Directory path for CA certificate files (PEM) + * + * This path may contain multiple CA certificates in OpenSSL format. + * Common use for this is to point to system trusted CA list which is + * often installed into directory like /etc/ssl/certs. If configured, + * these certificates are added to the list of trusted CAs. ca_cert + * may also be included in that case, but it is not required. + */ + u8 *ca_path; + + /** + * client_cert - File path to client certificate file (PEM/DER) + * + * This field is used with EAP method that use TLS authentication. + * Usually, this is only configured for EAP-TLS, even though this could + * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *client_cert; + + /** + * private_key - File path to client private key file (PEM/DER/PFX) + * + * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be + * commented out. Both the private key and certificate will be read + * from the PKCS#12 file in this case. Full path to the file should be + * used since working directory may change when wpa_supplicant is run + * in the background. + * + * Windows certificate store can be used by leaving client_cert out and + * configuring private_key in one of the following formats: + * + * cert://substring_to_match + * + * hash://certificate_thumbprint_in_hex + * + * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" + * + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *private_key; + + /** + * private_key_passwd - Password for private key file + * + * If left out, this will be asked through control interface. + */ + char *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 + * + * Note: Since this is a substring match, this cannot be used securily + * to do a suffix match against a possible domain name in the CN entry. + * For such a use case, domain_suffix_match should be used instead. + */ + u8 *subject_match; + + /** + * altsubject_match - Constraint for server certificate alt. subject + * + * Semicolon separated string of entries to be matched against the + * alternative subject name of the authentication server certificate. + * If this string is set, the server sertificate is only accepted if it + * contains one of the entries in an alternative subject name + * extension. + * + * altSubjectName string is in following format: TYPE:VALUE + * + * Example: EMAIL:server@example.com + * Example: DNS:server.example.com;DNS:server2.example.com + * + * Following types are supported: EMAIL, DNS, URI + */ + u8 *altsubject_match; + + /** + * domain_suffix_match - Constraint for server domain name + * + * If set, this FQDN is used as a suffix match requirement for the + * server certificate in SubjectAltName dNSName element(s). If a + * matching dNSName is found, this constraint is met. If no dNSName + * values are present, this constraint is matched against SubjectName CN + * using same suffix match comparison. Suffix match here means that the + * host/domain name is compared one label at a time starting from the + * top-level domain and all the labels in domain_suffix_match shall be + * included in the certificate. The certificate may include additional + * sub-level labels in addition to the required labels. + * + * For example, domain_suffix_match=example.com would match + * test.example.com but would not match test-example.com. + */ + char *domain_suffix_match; + + /** + * domain_match - Constraint for server domain name + * + * If set, this FQDN is used as a full match requirement for the + * server certificate in SubjectAltName dNSName element(s). If a + * matching dNSName is found, this constraint is met. If no dNSName + * values are present, this constraint is matched against SubjectName CN + * using same full match comparison. This behavior is similar to + * domain_suffix_match, but has the requirement of a full match, i.e., + * no subdomains or wildcard matches are allowed. Case-insensitive + * comparison is used, so "Example.com" matches "example.com", but would + * not match "test.Example.com". + */ + char *domain_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. + */ + char *private_key2_passwd; + + /** + * dh_file2 - File path to DH/DSA parameters file (in PEM format) + * + * This field is like dh_file, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *dh_file2; + + /** + * subject_match2 - Constraint for server certificate subject + * + * This field is like subject_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + u8 *subject_match2; + + /** + * altsubject_match2 - Constraint for server certificate alt. subject + * + * This field is like altsubject_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + u8 *altsubject_match2; + + /** + * domain_suffix_match2 - Constraint for server domain name + * + * This field is like domain_suffix_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + char *domain_suffix_match2; + + /** + * domain_match2 - Constraint for server domain name + * + * This field is like domain_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + char *domain_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 + * + * For wired IEEE 802.1X authentication, "allow_canned_success=1" can be + * used to configure a mode that allows EAP-Success (and EAP-Failure) + * without going through authentication step. Some switches use such + * sequence when forcing the port to be authorized/unauthorized or as a + * fallback option if the authentication server is unreachable. By + * default, wpa_supplicant discards such frames to protect against + * potential attacks by rogue devices, but this option can be used to + * disable that protection for cases where the server/authenticator does + * not need to be authenticated. + */ + 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. "mschapv2_retry=0" can + * be used to disable MSCHAPv2 password retry in authentication failure + * cases. + */ + char *phase2; + + /** + * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM + * + * This field is used to configure PC/SC smartcard interface. + * Currently, the only configuration is whether this field is %NULL (do + * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC. + * + * This field is used for EAP-SIM and EAP-AKA. + */ + char *pcsc; + + /** + * pin - PIN for USIM, GSM SIM, and smartcards + * + * This field is used to configure PIN for SIM and smartcards for + * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a + * smartcard is used for private key operations. + * + * If left out, this will be asked through control interface. + */ + char *pin; + + /** + * engine - Enable OpenSSL engine (e.g., for smartcard access) + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + int engine; + + /** + * engine_id - Engine ID for OpenSSL engine + * + * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11 + * engine. + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + char *engine_id; + + /** + * engine2 - Enable OpenSSL engine (e.g., for smartcard) (Phase 2) + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + * + * This field is like engine, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + int engine2; + + + /** + * pin2 - PIN for USIM, GSM SIM, and smartcards (Phase 2) + * + * This field is used to configure PIN for SIM and smartcards for + * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a + * smartcard is used for private key operations. + * + * This field is like pin2, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + * + * If left out, this will be asked through control interface. + */ + char *pin2; + + /** + * engine2_id - Engine ID for OpenSSL engine (Phase 2) + * + * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11 + * engine. + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + * + * This field is like engine_id, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + char *engine2_id; + + + /** + * key_id - Key ID for OpenSSL engine + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + char *key_id; + + /** + * cert_id - Cert ID for OpenSSL engine + * + * This is used if the certificate operations for EAP-TLS are performed + * using a smartcard. + */ + char *cert_id; + + /** + * ca_cert_id - CA Cert ID for OpenSSL engine + * + * This is used if the CA certificate for EAP-TLS is on a smartcard. + */ + char *ca_cert_id; + + /** + * key2_id - Key ID for OpenSSL engine (phase2) + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + char *key2_id; + + /** + * cert2_id - Cert ID for OpenSSL engine (phase2) + * + * This is used if the certificate operations for EAP-TLS are performed + * using a smartcard. + */ + char *cert2_id; + + /** + * ca_cert2_id - CA Cert ID for OpenSSL engine (phase2) + * + * This is used if the CA certificate for EAP-TLS is on a smartcard. + */ + char *ca_cert2_id; + + /** + * otp - One-time-password + * + * This field should not be set in configuration step. It is only used + * internally when OTP is entered through the control interface. + */ + u8 *otp; + + /** + * otp_len - Length of the otp field + */ + size_t otp_len; + + /** + * pending_req_identity - Whether there is a pending identity request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_identity; + + /** + * pending_req_password - Whether there is a pending password request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_password; + + /** + * pending_req_pin - Whether there is a pending PIN request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_pin; + + /** + * pending_req_new_password - Pending password update request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_new_password; + + /** + * pending_req_passphrase - Pending passphrase request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_passphrase; + + /** + * pending_req_otp - Whether there is a pending OTP request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + char *pending_req_otp; + + /** + * pending_req_otp_len - Length of the pending OTP request + */ + size_t pending_req_otp_len; + + /** + * pac_file - File path or blob name for the PAC entries (EAP-FAST) + * + * wpa_supplicant will need to be able to create this file and write + * updates to it when PAC is being provisioned or refreshed. Full path + * to the file should be used since working directory may change when + * wpa_supplicant is run in the background. + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + char *pac_file; + + /** + * mschapv2_retry - MSCHAPv2 retry in progress + * + * This field is used internally by EAP-MSCHAPv2 and should not be set + * as part of configuration. + */ + int mschapv2_retry; + + /** + * new_password - New password for password update + * + * This field is used during MSCHAPv2 password update. This is normally + * requested from the user through the control interface and not set + * from configuration. + */ + u8 *new_password; + + /** + * new_password_len - Length of new_password field + */ + size_t new_password_len; + + /** + * fragment_size - Maximum EAP fragment size in bytes (default 1398) + * + * This value limits the fragment size for EAP methods that support + * fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set + * small enough to make the EAP messages fit in MTU of the network + * interface used for EAPOL. The default value is suitable for most + * cases. + */ + int fragment_size; + +#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0) +#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1) + /** + * flags - Network configuration flags (bitfield) + * + * This variable is used for internal flags to describe further details + * for the network parameters. + * bit 0 = password is represented as a 16-byte NtPasswordHash value + * instead of plaintext password + * bit 1 = password is stored in external storage; the value in the + * password field is the name of that external entry + */ + u32 flags; + + /** + * ocsp - Whether to use/require OCSP to check server certificate + * + * 0 = do not use OCSP stapling (TLS certificate status extension) + * 1 = try to use OCSP stapling, but not require response + * 2 = require valid OCSP stapling response + */ + int ocsp; + + /** + * external_sim_resp - Response from external SIM processing + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request external + * SIM/USIM processing. + */ + char *external_sim_resp; + + /** + * sim_num - User selected SIM identifier + * + * This variable is used for identifying which SIM is used if the system + * has more than one. + */ + int sim_num; + + /** + * openssl_ciphers - OpenSSL cipher string + * + * This is an OpenSSL specific configuration option for configuring the + * ciphers for this connection. If not set, the default cipher suite + * list is used. + */ + char *openssl_ciphers; + + /** + * erp - Whether EAP Re-authentication Protocol (ERP) is enabled + */ + int erp; +}; + + +/** + * 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/freebsd/contrib/wpa/src/eap_peer/eap_gtc.c b/freebsd/contrib/wpa/src/eap_peer/eap_gtc.c new file mode 100644 index 00000000..2ffc142c --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_gtc.c @@ -0,0 +1,147 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP peer method: EAP-GTC (RFC 3748) + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" + + +struct eap_gtc_data { + int prefix; +}; + + +static void * eap_gtc_init(struct eap_sm *sm) +{ + struct eap_gtc_data *data; + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (sm->m && sm->m->vendor == EAP_VENDOR_IETF && + sm->m->method == EAP_TYPE_FAST) { + wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix " + "with challenge/response"); + data->prefix = 1; + } + return data; +} + + +static void eap_gtc_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_gtc_data *data = priv; + os_free(data); +} + + +static struct wpabuf * eap_gtc_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_gtc_data *data = priv; + struct wpabuf *resp; + const u8 *pos, *password, *identity; + size_t password_len, identity_len, len, plen; + int otp; + u8 id; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, reqData, &len); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + id = eap_get_id(reqData); + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", pos, len); + if (data->prefix && + (len < 10 || os_memcmp(pos, "CHALLENGE=", 10) != 0)) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with " + "expected prefix"); + + /* Send an empty response in order to allow tunneled + * acknowledgement of the failure. This will also cover the + * error case which seems to use EAP-MSCHAPv2 like error + * reporting with EAP-GTC inside EAP-FAST tunnel. */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, + 0, EAP_CODE_RESPONSE, id); + return resp; + } + + password = eap_get_config_otp(sm, &password_len); + if (password) + otp = 1; + else { + password = eap_get_config_password(sm, &password_len); + otp = 0; + } + + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-GTC: Password not configured"); + eap_sm_request_otp(sm, (const char *) pos, len); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + + ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + plen = password_len; + identity = eap_get_config_identity(sm, &identity_len); + if (identity == NULL) + return NULL; + if (data->prefix) + plen += 9 + identity_len + 1; + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, plen, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + if (data->prefix) { + wpabuf_put_data(resp, "RESPONSE=", 9); + wpabuf_put_data(resp, identity, identity_len); + wpabuf_put_u8(resp, '\0'); + } + wpabuf_put_data(resp, password, password_len); + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", + wpabuf_head_u8(resp) + sizeof(struct eap_hdr) + + 1, plen); + + if (otp) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password"); + eap_clear_config_otp(sm); + } + + return resp; +} + + +int eap_peer_gtc_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC"); + if (eap == NULL) + return -1; + + eap->init = eap_gtc_init; + eap->deinit = eap_gtc_deinit; + eap->process = eap_gtc_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_i.h b/freebsd/contrib/wpa/src/eap_peer/eap_i.h new file mode 100644 index 00000000..99b44dae --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_i.h @@ -0,0 +1,389 @@ +/* + * EAP peer state machines internal structures (RFC 4137) + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_I_H +#define EAP_I_H + +#include "wpabuf.h" +#include "utils/list.h" +#include "eap_peer/eap.h" +#include "eap_common/eap_common.h" + +/* RFC 4137 - EAP Peer state machine */ + +typedef enum { + DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC +} EapDecision; + +typedef enum { + METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE +} EapMethodState; + +/** + * struct eap_method_ret - EAP return values from struct eap_method::process() + * + * These structure contains OUT variables for the interface between peer state + * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as + * the return value of struct eap_method::process() so it is not included in + * this structure. + */ +struct eap_method_ret { + /** + * ignore - Whether method decided to drop the current packed (OUT) + */ + Boolean ignore; + + /** + * methodState - Method-specific state (IN/OUT) + */ + EapMethodState methodState; + + /** + * decision - Authentication decision (OUT) + */ + EapDecision decision; + + /** + * allowNotifications - Whether method allows notifications (OUT) + */ + Boolean allowNotifications; +}; + + +/** + * struct eap_method - EAP method interface + * This structure defines the EAP method interface. Each method will need to + * register its own EAP type, EAP name, and set of function pointers for method + * specific operations. This interface is based on section 4.4 of RFC 4137. + */ +struct eap_method { + /** + * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF) + */ + int vendor; + + /** + * method - EAP type number (EAP_TYPE_*) + */ + EapType method; + + /** + * name - Name of the method (e.g., "TLS") + */ + const char *name; + + /** + * init - Initialize an EAP method + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to allocated private data, or %NULL on failure + * + * This function is used to initialize the EAP method explicitly + * instead of using METHOD_INIT state as specific in RFC 4137. The + * method is expected to initialize it method-specific state and return + * a pointer that will be used as the priv argument to other calls. + */ + void * (*init)(struct eap_sm *sm); + + /** + * deinit - Deinitialize an EAP method + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * + * Deinitialize the EAP method and free any allocated private data. + */ + void (*deinit)(struct eap_sm *sm, void *priv); + + /** + * process - Process an EAP request + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @ret: Return values from EAP request validation and processing + * @reqData: EAP request to be processed (eapReqData) + * Returns: Pointer to allocated EAP response packet (eapRespData) + * + * This function is a combination of m.check(), m.process(), and + * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other + * words, this function validates the incoming request, processes it, + * and build a response packet. m.check() and m.process() return values + * are returned through struct eap_method_ret *ret variable. Caller is + * responsible for freeing the returned EAP response packet. + */ + struct wpabuf * (*process)(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData); + + /** + * isKeyAvailable - Find out whether EAP method has keying material + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * Returns: %TRUE if key material (eapKeyData) is available + */ + Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv); + + /** + * getKey - Get EAP method specific keying material (eapKeyData) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to variable to store key length (eapKeyDataLen) + * Returns: Keying material (eapKeyData) or %NULL if not available + * + * This function can be used to get the keying material from the EAP + * method. The key may already be stored in the method-specific private + * data or this function may derive the key. + */ + u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * get_status - Get EAP method status + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf + * + * Query EAP method for status information. This function fills in a + * text area with current status information from the EAP method. If + * the buffer (buf) is not large enough, status information will be + * truncated to fit the buffer. + */ + int (*get_status)(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose); + + /** + * has_reauth_data - Whether method is ready for fast reauthentication + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * Returns: %TRUE or %FALSE based on whether fast reauthentication is + * possible + * + * This function is an optional handler that only EAP methods + * supporting fast re-authentication need to implement. + */ + Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv); + + /** + * deinit_for_reauth - Release data that is not needed for fast re-auth + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * + * This function is an optional handler that only EAP methods + * supporting fast re-authentication need to implement. This is called + * when authentication has been completed and EAP state machine is + * requesting that enough state information is maintained for fast + * re-authentication + */ + void (*deinit_for_reauth)(struct eap_sm *sm, void *priv); + + /** + * init_for_reauth - Prepare for start of fast re-authentication + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * + * This function is an optional handler that only EAP methods + * supporting fast re-authentication need to implement. This is called + * when EAP authentication is started and EAP state machine is + * requesting fast re-authentication to be used. + */ + void * (*init_for_reauth)(struct eap_sm *sm, void *priv); + + /** + * get_identity - Get method specific identity for re-authentication + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Length of the returned identity + * Returns: Pointer to the method specific identity or %NULL if default + * identity is to be used + * + * This function is an optional handler that only EAP methods + * that use method specific identity need to implement. + */ + const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * free - Free EAP method data + * @method: Pointer to the method data registered with + * eap_peer_method_register(). + * + * This function will be called when the EAP method is being + * unregistered. If the EAP method allocated resources during + * registration (e.g., allocated struct eap_method), they should be + * freed in this function. No other method functions will be called + * after this call. If this function is not defined (i.e., function + * pointer is %NULL), a default handler is used to release the method + * data with free(method). This is suitable for most cases. + */ + void (*free)(struct eap_method *method); + +#define EAP_PEER_METHOD_INTERFACE_VERSION 1 + /** + * version - Version of the EAP peer method interface + * + * The EAP peer method implementation should set this variable to + * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the + * EAP method is using supported API version when using dynamically + * loadable EAP methods. + */ + int version; + + /** + * next - Pointer to the next EAP method + * + * This variable is used internally in the EAP method registration code + * to create a linked list of registered EAP methods. + */ + struct eap_method *next; + +#ifdef CONFIG_DYNAMIC_EAP_METHODS + /** + * dl_handle - Handle for the dynamic library + * + * This variable is used internally in the EAP method registration code + * to store a handle for the dynamic library. If the method is linked + * in statically, this is %NULL. + */ + void *dl_handle; +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + + /** + * get_emsk - Get EAP method specific keying extended material (EMSK) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to a variable to store EMSK length + * Returns: EMSK or %NULL if not available + * + * This function can be used to get the extended keying material from + * the EAP method. The key may already be stored in the method-specific + * private data or this function may derive the key. + */ + u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * getSessionId - Get EAP method specific Session-Id + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to a variable to store Session-Id length + * Returns: Session-Id or %NULL if not available + * + * This function can be used to get the Session-Id from the EAP method. + * The Session-Id may already be stored in the method-specific private + * data or this function may derive the Session-Id. + */ + u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len); +}; + + +struct eap_erp_key { + struct dl_list list; + size_t rRK_len; + size_t rIK_len; + u8 rRK[ERP_MAX_KEY_LEN]; + u8 rIK[ERP_MAX_KEY_LEN]; + u32 next_seq; + char keyname_nai[]; +}; + +/** + * struct eap_sm - EAP state machine data + */ +struct eap_sm { + enum { + EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED, + EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD, + EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS, + EAP_FAILURE + } EAP_state; + /* Long-term local variables */ + EapType selectedMethod; + EapMethodState methodState; + int lastId; + struct wpabuf *lastRespData; + EapDecision decision; + /* Short-term local variables */ + Boolean rxReq; + Boolean rxSuccess; + Boolean rxFailure; + int reqId; + EapType reqMethod; + int reqVendor; + u32 reqVendorMethod; + Boolean ignore; + /* Constants */ + int ClientTimeout; + + /* Miscellaneous variables */ + Boolean allowNotifications; /* peer state machine <-> methods */ + struct wpabuf *eapRespData; /* peer to lower layer */ + Boolean eapKeyAvailable; /* peer to lower layer */ + u8 *eapKeyData; /* peer to lower layer */ + size_t eapKeyDataLen; /* peer to lower layer */ + u8 *eapSessionId; /* peer to lower layer */ + size_t eapSessionIdLen; /* peer to lower layer */ + const struct eap_method *m; /* selected EAP method */ + /* not defined in RFC 4137 */ + Boolean changed; + void *eapol_ctx; + const struct eapol_callbacks *eapol_cb; + void *eap_method_priv; + int init_phase2; + int fast_reauth; + Boolean reauthInit; /* send EAP-Identity/Re-auth */ + u32 erp_seq; + + Boolean rxResp /* LEAP only */; + Boolean leap_done; + Boolean peap_done; + u8 req_sha1[20]; /* SHA1() of the current EAP packet */ + u8 last_sha1[20]; /* SHA1() of the previously received EAP packet; used + * in duplicate request detection. */ + + void *msg_ctx; + void *scard_ctx; + void *ssl_ctx; + void *ssl_ctx2; + + unsigned int workaround; + + /* Optional challenges generated in Phase 1 (EAP-FAST) */ + u8 *peer_challenge, *auth_challenge; + + int num_rounds; + int force_disabled; + + struct wps_context *wps; + + int prev_failure; + struct eap_peer_config *last_config; + + struct ext_password_data *ext_pw; + struct wpabuf *ext_pw_buf; + + int external_sim; + + unsigned int expected_failure:1; + + struct dl_list erp_keys; /* struct eap_erp_key */ +}; + +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/freebsd/contrib/wpa/src/eap_peer/eap_leap.c b/freebsd/contrib/wpa/src/eap_peer/eap_leap.c new file mode 100644 index 00000000..e765af14 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_leap.c @@ -0,0 +1,415 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP peer method: LEAP + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/ms_funcs.h" +#include "crypto/crypto.h" +#include "crypto/random.h" +#include "eap_i.h" + +#define LEAP_VERSION 1 +#define LEAP_CHALLENGE_LEN 8 +#define LEAP_RESPONSE_LEN 24 +#define LEAP_KEY_LEN 16 + + +struct eap_leap_data { + enum { + LEAP_WAIT_CHALLENGE, + LEAP_WAIT_SUCCESS, + LEAP_WAIT_RESPONSE, + LEAP_DONE + } state; + + u8 peer_challenge[LEAP_CHALLENGE_LEN]; + u8 peer_response[LEAP_RESPONSE_LEN]; + + u8 ap_challenge[LEAP_CHALLENGE_LEN]; + u8 ap_response[LEAP_RESPONSE_LEN]; +}; + + +static void * eap_leap_init(struct eap_sm *sm) +{ + struct eap_leap_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = LEAP_WAIT_CHALLENGE; + + sm->leap_done = FALSE; + return data; +} + + +static void eap_leap_deinit(struct eap_sm *sm, void *priv) +{ + os_free(priv); +} + + +static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_leap_data *data = priv; + struct wpabuf *resp; + const u8 *pos, *challenge, *identity, *password; + u8 challenge_len, *rpos; + size_t identity_len, password_len, len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (identity == NULL || password == NULL) + return NULL; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); + if (pos == NULL || len < 3) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame"); + ret->ignore = TRUE; + return NULL; + } + + if (*pos != LEAP_VERSION) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " + "%d", *pos); + ret->ignore = TRUE; + return NULL; + } + pos++; + + pos++; /* skip unused byte */ + + challenge_len = *pos++; + if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge " + "(challenge_len=%d reqDataLen=%lu)", + challenge_len, (unsigned long) wpabuf_len(reqData)); + ret->ignore = TRUE; + return NULL; + } + challenge = pos; + os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP", + challenge, LEAP_CHALLENGE_LEN); + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response"); + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP, + 3 + LEAP_RESPONSE_LEN + identity_len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + wpabuf_put_u8(resp, LEAP_VERSION); + wpabuf_put_u8(resp, 0); /* unused */ + wpabuf_put_u8(resp, LEAP_RESPONSE_LEN); + rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN); + if (pwhash) + challenge_response(challenge, password, rpos); + else + nt_challenge_response(challenge, password, password_len, rpos); + os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", + rpos, LEAP_RESPONSE_LEN); + wpabuf_put_data(resp, identity, identity_len); + + data->state = LEAP_WAIT_SUCCESS; + + return resp; +} + + +static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_leap_data *data = priv; + struct wpabuf *resp; + u8 *pos; + const u8 *identity; + size_t identity_len; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success"); + + identity = eap_get_config_identity(sm, &identity_len); + if (identity == NULL) + return NULL; + + if (data->state != LEAP_WAIT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in " + "unexpected state (%d) - ignored", data->state); + ret->ignore = TRUE; + return NULL; + } + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP, + 3 + LEAP_CHALLENGE_LEN + identity_len, + EAP_CODE_REQUEST, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + wpabuf_put_u8(resp, LEAP_VERSION); + wpabuf_put_u8(resp, 0); /* unused */ + wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN); + pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN); + if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data " + "for challenge"); + wpabuf_free(resp); + ret->ignore = TRUE; + return NULL; + } + os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos, + LEAP_CHALLENGE_LEN); + wpabuf_put_data(resp, identity, identity_len); + + data->state = LEAP_WAIT_RESPONSE; + + return resp; +} + + +static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_leap_data *data = priv; + const u8 *pos, *password; + u8 response_len, pw_hash[16], pw_hash_hash[16], + expected[LEAP_RESPONSE_LEN]; + size_t password_len, len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response"); + + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (password == NULL) + return NULL; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); + if (pos == NULL || len < 3) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame"); + ret->ignore = TRUE; + return NULL; + } + + if (*pos != LEAP_VERSION) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " + "%d", *pos); + ret->ignore = TRUE; + return NULL; + } + pos++; + + pos++; /* skip unused byte */ + + response_len = *pos++; + if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response " + "(response_len=%d reqDataLen=%lu)", + response_len, (unsigned long) wpabuf_len(reqData)); + ret->ignore = TRUE; + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP", + pos, LEAP_RESPONSE_LEN); + os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN); + + if (pwhash) { + if (hash_nt_password_hash(password, pw_hash_hash)) { + ret->ignore = TRUE; + return NULL; + } + } else { + if (nt_password_hash(password, password_len, pw_hash) || + hash_nt_password_hash(pw_hash, pw_hash_hash)) { + ret->ignore = TRUE; + return NULL; + } + } + challenge_response(data->ap_challenge, pw_hash_hash, expected); + + ret->methodState = METHOD_DONE; + ret->allowNotifications = FALSE; + + if (os_memcmp_const(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; + + os_memset(pw_hash, 0, sizeof(pw_hash)); + os_memset(pw_hash_hash, 0, sizeof(pw_hash_hash)); + + 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/freebsd/contrib/wpa/src/eap_peer/eap_md5.c b/freebsd/contrib/wpa/src/eap_peer/eap_md5.c new file mode 100644 index 00000000..145bfa8f --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_md5.c @@ -0,0 +1,122 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994) + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "eap_common/chap.h" + + +static void * eap_md5_init(struct eap_sm *sm) +{ + /* No need for private data. However, must return non-NULL to indicate + * success. */ + return (void *) 1; +} + + +static void eap_md5_deinit(struct eap_sm *sm, void *priv) +{ +} + + +static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct wpabuf *resp; + const u8 *pos, *challenge, *password; + u8 *rpos, id; + size_t len, challenge_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-MD5: Password not configured"); + eap_sm_request_password(sm); + ret->ignore = TRUE; + return NULL; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, reqData, &len); + if (pos == NULL || len == 0) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame (pos=%p len=%lu)", + pos, (unsigned long) len); + ret->ignore = TRUE; + return NULL; + } + + /* + * CHAP Challenge: + * Value-Size (1 octet) | Value(Challenge) | Name(optional) + */ + challenge_len = *pos++; + if (challenge_len == 0 || challenge_len > len - 1) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge " + "(challenge_len=%lu len=%lu)", + (unsigned long) challenge_len, (unsigned long) len); + ret->ignore = TRUE; + return NULL; + } + ret->ignore = FALSE; + challenge = pos; + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", + challenge, challenge_len); + + wpa_printf(MSG_DEBUG, "EAP-MD5: Generating Challenge Response"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = TRUE; + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHAP_MD5_LEN, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + + /* + * CHAP Response: + * Value-Size (1 octet) | Value(Response) | Name(optional) + */ + wpabuf_put_u8(resp, CHAP_MD5_LEN); + + id = eap_get_id(resp); + rpos = wpabuf_put(resp, CHAP_MD5_LEN); + if (chap_md5(id, password, password_len, challenge, challenge_len, + rpos)) { + wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed"); + ret->ignore = TRUE; + wpabuf_free(resp); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN); + + return resp; +} + + +int eap_peer_md5_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5"); + if (eap == NULL) + return -1; + + eap->init = eap_md5_init; + eap->deinit = eap_md5_deinit; + eap->process = eap_md5_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_methods.c b/freebsd/contrib/wpa/src/eap_peer/eap_methods.c new file mode 100644 index 00000000..7af5a535 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_methods.c @@ -0,0 +1,371 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP peer: Method registration + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#ifdef CONFIG_DYNAMIC_EAP_METHODS +#include <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; + if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED) + return "expanded"; + for (m = eap_methods; m; m = m->next) { + if (m->vendor == vendor && m->method == type) + return m->name; + } + return NULL; +} + + +/** + * eap_get_names - Get space separated list of names for supported EAP methods + * @buf: Buffer for names + * @buflen: Buffer length + * Returns: Number of characters written into buf (not including nul + * termination) + */ +size_t eap_get_names(char *buf, size_t buflen) +{ + char *pos, *end; + struct eap_method *m; + int ret; + + if (buflen == 0) + return 0; + + pos = buf; + end = pos + buflen; + + for (m = eap_methods; m; m = m->next) { + ret = os_snprintf(pos, end - pos, "%s%s", + m == eap_methods ? "" : " ", m->name); + if (os_snprintf_error(end - pos, ret)) + 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_calloc(array_len + 1, sizeof(char *)); + 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/freebsd/contrib/wpa/src/eap_peer/eap_methods.h b/freebsd/contrib/wpa/src/eap_peer/eap_methods.h new file mode 100644 index 00000000..e35c919a --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_methods.h @@ -0,0 +1,111 @@ +/* + * EAP peer: Method registration + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_METHODS_H +#define EAP_METHODS_H + +#include "eap_common/eap_defs.h" + +const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method); +const struct eap_method * eap_peer_get_methods(size_t *count); + +struct eap_method * eap_peer_method_alloc(int version, int vendor, + EapType method, const char *name); +void eap_peer_method_free(struct eap_method *method); +int eap_peer_method_register(struct eap_method *method); + + +#ifdef IEEE8021X_EAPOL + +EapType eap_peer_get_type(const char *name, int *vendor); +const char * eap_get_name(int vendor, EapType type); +size_t eap_get_names(char *buf, size_t buflen); +char ** eap_get_names_as_string_array(size_t *num); +void eap_peer_unregister_methods(void); + +#else /* IEEE8021X_EAPOL */ + +static inline EapType eap_peer_get_type(const char *name, int *vendor) +{ + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + +static inline const char * eap_get_name(int vendor, EapType type) +{ + return NULL; +} + +static inline size_t eap_get_names(char *buf, size_t buflen) +{ + return 0; +} + +static inline int eap_peer_register_methods(void) +{ + return 0; +} + +static inline void eap_peer_unregister_methods(void) +{ +} + +static inline char ** eap_get_names_as_string_array(size_t *num) +{ + return NULL; +} + +#endif /* IEEE8021X_EAPOL */ + + +#ifdef CONFIG_DYNAMIC_EAP_METHODS + +int eap_peer_method_load(const char *so); +int eap_peer_method_unload(struct eap_method *method); + +#else /* CONFIG_DYNAMIC_EAP_METHODS */ + +static inline int eap_peer_method_load(const char *so) +{ + return 0; +} + +static inline int eap_peer_method_unload(struct eap_method *method) +{ + return 0; +} + +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + +/* EAP peer method registration calls for statically linked in methods */ +int eap_peer_md5_register(void); +int eap_peer_tls_register(void); +int eap_peer_unauth_tls_register(void); +int eap_peer_wfa_unauth_tls_register(void); +int eap_peer_mschapv2_register(void); +int eap_peer_peap_register(void); +int eap_peer_ttls_register(void); +int eap_peer_gtc_register(void); +int eap_peer_otp_register(void); +int eap_peer_sim_register(void); +int eap_peer_leap_register(void); +int eap_peer_psk_register(void); +int eap_peer_aka_register(void); +int eap_peer_aka_prime_register(void); +int eap_peer_fast_register(void); +int eap_peer_pax_register(void); +int eap_peer_sake_register(void); +int eap_peer_gpsk_register(void); +int eap_peer_wsc_register(void); +int eap_peer_ikev2_register(void); +int eap_peer_vendor_test_register(void); +int eap_peer_tnc_register(void); +int eap_peer_pwd_register(void); +int eap_peer_eke_register(void); + +#endif /* EAP_METHODS_H */ diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_mschapv2.c b/freebsd/contrib/wpa/src/eap_peer/eap_mschapv2.c new file mode 100644 index 00000000..18bb04c2 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_mschapv2.c @@ -0,0 +1,903 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26). + * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP + * Extensions Protocol, Version 2, for mutual authentication and key + * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in + * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in + * RFC 3079. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/ms_funcs.h" +#include "crypto/random.h" +#include "common/wpa_ctrl.h" +#include "mschapv2.h" +#include "eap_i.h" +#include "eap_config.h" + + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_mschapv2_hdr { + u8 op_code; /* MSCHAPV2_OP_* */ + u8 mschapv2_id; /* usually same as EAP identifier; must be changed + * for challenges, but not for success/failure */ + u8 ms_length[2]; /* Note: misaligned; length - 5 */ + /* followed by data */ +} STRUCT_PACKED; + +/* Response Data field */ +struct ms_response { + u8 peer_challenge[MSCHAPV2_CHAL_LEN]; + u8 reserved[8]; + u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; + u8 flags; +} STRUCT_PACKED; + +/* Change-Password Data field */ +struct ms_change_password { + u8 encr_password[516]; + u8 encr_hash[16]; + u8 peer_challenge[MSCHAPV2_CHAL_LEN]; + u8 reserved[8]; + u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; + u8 flags[2]; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define MSCHAPV2_OP_CHALLENGE 1 +#define MSCHAPV2_OP_RESPONSE 2 +#define MSCHAPV2_OP_SUCCESS 3 +#define MSCHAPV2_OP_FAILURE 4 +#define MSCHAPV2_OP_CHANGE_PASSWORD 7 + +#define ERROR_RESTRICTED_LOGON_HOURS 646 +#define ERROR_ACCT_DISABLED 647 +#define ERROR_PASSWD_EXPIRED 648 +#define ERROR_NO_DIALIN_PERMISSION 649 +#define ERROR_AUTHENTICATION_FAILURE 691 +#define ERROR_CHANGING_PASSWORD 709 + +#define PASSWD_CHANGE_CHAL_LEN 16 +#define MSCHAPV2_KEY_LEN 16 + + +struct eap_mschapv2_data { + u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; + int auth_response_valid; + + int prev_error; + u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN]; + int passwd_change_challenge_valid; + int passwd_change_version; + + /* Optional challenge values generated in EAP-FAST Phase 1 negotiation + */ + u8 *peer_challenge; + u8 *auth_challenge; + + int phase2; + u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; + int master_key_valid; + int success; + + struct wpabuf *prev_challenge; +}; + + +static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_mschapv2_init(struct eap_sm *sm) +{ + struct eap_mschapv2_data *data; + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (sm->peer_challenge) { + data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN); + if (data->peer_challenge == NULL) { + eap_mschapv2_deinit(sm, data); + return NULL; + } + os_memcpy(data->peer_challenge, sm->peer_challenge, + MSCHAPV2_CHAL_LEN); + } + + if (sm->auth_challenge) { + data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN); + if (data->auth_challenge == NULL) { + eap_mschapv2_deinit(sm, data); + return NULL; + } + os_memcpy(data->auth_challenge, sm->auth_challenge, + MSCHAPV2_CHAL_LEN); + } + + data->phase2 = sm->init_phase2; + + return data; +} + + +static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + os_free(data->peer_challenge); + os_free(data->auth_challenge); + wpabuf_free(data->prev_challenge); + bin_clear_free(data, sizeof(*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; + bin_clear_free(config->password, config->password_len); + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + /* TODO: update external storage */ + } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { + config->password = os_malloc(16); + config->password_len = 16; + if (config->password && + nt_password_hash(config->new_password, + config->new_password_len, + config->password)) { + bin_clear_free(config->password, + config->password_len); + config->password = NULL; + config->password_len = 0; + } + bin_clear_free(config->new_password, + config->new_password_len); + } 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; + } + if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry && + config && config->phase2 && + os_strstr(config->phase2, "mschapv2_retry=0")) { + wpa_printf(MSG_DEBUG, + "EAP-MSCHAPV2: mark password retry disabled based on local configuration"); + retry = 0; + } + 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) +{ +#ifdef CONFIG_NO_RC4 + wpa_printf(MSG_ERROR, + "EAP-MSCHAPV2: RC4 not support in the build - cannot change password"); + return NULL; +#else /* CONFIG_NO_RC4 */ + 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]; + if (nt_password_hash(new_password, new_password_len, + new_password_hash)) + goto fail; + nt_password_hash_encrypted_with_block(password, + new_password_hash, + cp->encr_hash); + } else { + if (old_nt_password_hash_encrypted_with_new_nt_password_hash( + new_password, new_password_len, + password, password_len, cp->encr_hash)) + goto fail; + } + + /* 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. */ + if (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->auth_response_valid = 0; + goto fail; + } + 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; +#endif /* CONFIG_NO_RC4 */ +} + + +/** + * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Pointer to private EAP method data from eap_mschapv2_init() + * @ret: Return values from EAP request validation and processing + * @req: Pointer to EAP-MSCHAPv2 header from the request + * @req_len: Length of the EAP-MSCHAPv2 data + * @id: EAP identifier used in th erequest + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if + * no reply available + */ +static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + const struct eap_mschapv2_hdr *req, + size_t req_len, u8 id) +{ + struct wpabuf *resp; + const u8 *msdata = (const u8 *) (req + 1); + char *buf; + size_t len = req_len - sizeof(*req); + int retry = 0; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure"); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data", + msdata, len); + /* + * eap_mschapv2_failure_txt() expects a nul terminated string, so we + * must allocate a large enough temporary buffer to create that since + * the received message does not include nul termination. + */ + buf = dup_binstr(msdata, len); + if (buf) { + retry = eap_mschapv2_failure_txt(sm, data, buf); + os_free(buf); + } + + ret->ignore = FALSE; + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + if (data->prev_error == ERROR_PASSWD_EXPIRED && + data->passwd_change_version == 3) { + struct eap_peer_config *config = eap_get_config(sm); + if (config && config->new_password) + return eap_mschapv2_change_password(sm, data, ret, req, + id); + if (config && config->pending_req_new_password) + return NULL; + } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) { + /* TODO: could try to retry authentication, e.g, after having + * changed the username/password. In this case, EAP MS-CHAP-v2 + * Failure Response would not be sent here. */ + return NULL; + } + + /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure + * message. */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */ + + return resp; +} + + +static int eap_mschapv2_check_config(struct eap_sm *sm) +{ + size_t len; + + if (eap_get_config_identity(sm, &len) == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured"); + eap_sm_request_identity(sm); + return -1; + } + + if (eap_get_config_password(sm, &len) == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); + eap_sm_request_password(sm); + return -1; + } + + return 0; +} + + +static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len, + const struct eap_mschapv2_hdr *ms) +{ + size_t ms_len = WPA_GET_BE16(ms->ms_length); + + if (ms_len == len) + return 0; + + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu " + "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len); + if (sm->workaround) { + /* Some authentication servers use invalid ms_len, + * ignore it for interoperability. */ + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore" + " invalid ms_len %lu (len %lu)", + (unsigned long) ms_len, + (unsigned long) len); + return 0; + } + + return -1; +} + + +static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data, + const struct wpabuf *reqData) +{ + /* + * Store a copy of the challenge message, so that it can be processed + * again in case retry is allowed after a possible failure. + */ + wpabuf_free(data->prev_challenge); + data->prev_challenge = wpabuf_dup(reqData); +} + + +/** + * eap_mschapv2_process - Process an EAP-MSCHAPv2 request + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_mschapv2_init() + * @ret: Return values from EAP request validation and processing + * @reqData: EAP request to be processed (eapReqData) + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if + * no reply available + */ +static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_mschapv2_data *data = priv; + struct eap_peer_config *config = eap_get_config(sm); + const struct eap_mschapv2_hdr *ms; + int using_prev_challenge = 0; + const u8 *pos; + size_t len; + u8 id; + + if (eap_mschapv2_check_config(sm)) { + ret->ignore = TRUE; + return NULL; + } + + if (config->mschapv2_retry && data->prev_challenge && + data->prev_error == ERROR_AUTHENTICATION_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet " + "with the previous challenge"); + + reqData = data->prev_challenge; + using_prev_challenge = 1; + config->mschapv2_retry = 0; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData, + &len); + if (pos == NULL || len < sizeof(*ms) + 1) { + ret->ignore = TRUE; + return NULL; + } + + ms = (const struct eap_mschapv2_hdr *) pos; + if (eap_mschapv2_check_mslen(sm, len, ms)) { + ret->ignore = TRUE; + return NULL; + } + + id = eap_get_id(reqData); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d", + id, ms->mschapv2_id); + + switch (ms->op_code) { + case MSCHAPV2_OP_CHALLENGE: + if (!using_prev_challenge) + eap_mschapv2_copy_challenge(data, reqData); + return eap_mschapv2_challenge(sm, data, ret, ms, len, id); + case MSCHAPV2_OP_SUCCESS: + return eap_mschapv2_success(sm, data, ret, ms, len, id); + case MSCHAPV2_OP_FAILURE: + return eap_mschapv2_failure(sm, data, ret, ms, len, id); + default: + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored", + ms->op_code); + ret->ignore = TRUE; + return NULL; + } +} + + +static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + return data->success && data->master_key_valid; +} + + +static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_mschapv2_data *data = priv; + u8 *key; + int key_len; + + if (!data->master_key_valid || !data->success) + return NULL; + + key_len = 2 * MSCHAPV2_KEY_LEN; + + key = os_malloc(key_len); + if (key == NULL) + return NULL; + + /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e., + * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */ + get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0); + get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 0, 0); + + wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", + key, key_len); + + *len = key_len; + return key; +} + + +/** + * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method + * Returns: 0 on success, -1 on failure + * + * This function is used to register EAP-MSCHAPv2 peer method into the EAP + * method list. + */ +int eap_peer_mschapv2_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, + "MSCHAPV2"); + if (eap == NULL) + return -1; + + eap->init = eap_mschapv2_init; + eap->deinit = eap_mschapv2_deinit; + eap->process = eap_mschapv2_process; + eap->isKeyAvailable = eap_mschapv2_isKeyAvailable; + eap->getKey = eap_mschapv2_getKey; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_otp.c b/freebsd/contrib/wpa/src/eap_peer/eap_otp.c new file mode 100644 index 00000000..d4d8bc1c --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_otp.c @@ -0,0 +1,103 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP peer method: EAP-OTP (RFC 3748) + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" + + +static void * eap_otp_init(struct eap_sm *sm) +{ + /* No need for private data. However, must return non-NULL to indicate + * success. */ + return (void *) 1; +} + + +static void eap_otp_deinit(struct eap_sm *sm, void *priv) +{ +} + + +static struct wpabuf * eap_otp_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct wpabuf *resp; + const u8 *pos, *password; + size_t password_len, len; + int otp; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_OTP, reqData, &len); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message", + pos, len); + + password = eap_get_config_otp(sm, &password_len); + if (password) + otp = 1; + else { + password = eap_get_config_password(sm, &password_len); + otp = 0; + } + + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-OTP: Password not configured"); + eap_sm_request_otp(sm, (const char *) pos, len); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_OTP, password_len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + wpabuf_put_data(resp, password, password_len); + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response", + password, password_len); + + if (otp) { + wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password"); + eap_clear_config_otp(sm); + } + + return resp; +} + + +int eap_peer_otp_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_OTP, "OTP"); + if (eap == NULL) + return -1; + + eap->init = eap_otp_init; + eap->deinit = eap_otp_deinit; + eap->process = eap_otp_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_peap.c b/freebsd/contrib/wpa/src/eap_peer/eap_peap.c new file mode 100644 index 00000000..19afa1cf --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_peap.c @@ -0,0 +1,1264 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" +#include "eap_common/eap_tlv_common.h" +#include "eap_common/eap_peap_common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" +#include "tncc.h" + + +/* 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 + */ +#define EAP_PEAP_VERSION 1 + + +static void eap_peap_deinit(struct eap_sm *sm, void *priv); + + +struct eap_peap_data { + struct eap_ssl_data ssl; + + int peap_version, force_peap_version, force_new_label; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + int phase2_eap_success; + int phase2_eap_started; + + struct eap_method_type phase2_type; + struct eap_method_type *phase2_types; + size_t num_phase2_types; + + int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner + * EAP-Success + * 1 = reply with tunneled EAP-Success to inner + * EAP-Success and expect AS to send outer + * (unencrypted) EAP-Success after this + * 2 = reply with PEAP/TLS ACK to inner + * EAP-Success and expect AS to send outer + * (unencrypted) EAP-Success after this */ + int resuming; /* starting a resumed session */ + int reauth; /* reauthentication */ + u8 *key_data; + u8 *session_id; + size_t id_len; + + struct wpabuf *pending_phase2_req; + enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; + int crypto_binding_used; + u8 binding_nonce[32]; + u8 ipmk[40]; + u8 cmk[20]; + int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP) + * is enabled. */ +}; + + +static int eap_peap_parse_phase1(struct eap_peap_data *data, + const char *phase1) +{ + const char *pos; + + pos = os_strstr(phase1, "peapver="); + if (pos) { + data->force_peap_version = atoi(pos + 8); + data->peap_version = data->force_peap_version; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d", + data->force_peap_version); + } + + if (os_strstr(phase1, "peaplabel=1")) { + data->force_new_label = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key " + "derivation"); + } + + if (os_strstr(phase1, "peap_outer_success=0")) { + data->peap_outer_success = 0; + wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on " + "tunneled EAP-Success"); + } else if (os_strstr(phase1, "peap_outer_success=1")) { + data->peap_outer_success = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success " + "after receiving tunneled EAP-Success"); + } else if (os_strstr(phase1, "peap_outer_success=2")) { + data->peap_outer_success = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after " + "receiving tunneled EAP-Success"); + } + + if (os_strstr(phase1, "crypto_binding=0")) { + data->crypto_binding = NO_BINDING; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding"); + } else if (os_strstr(phase1, "crypto_binding=1")) { + data->crypto_binding = OPTIONAL_BINDING; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding"); + } else if (os_strstr(phase1, "crypto_binding=2")) { + data->crypto_binding = REQUIRE_BINDING; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding"); + } + +#ifdef EAP_TNC + if (os_strstr(phase1, "tnc=soh2")) { + data->soh = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); + } else if (os_strstr(phase1, "tnc=soh1")) { + data->soh = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled"); + } else if (os_strstr(phase1, "tnc=soh")) { + data->soh = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); + } +#endif /* EAP_TNC */ + + return 0; +} + + +static void * eap_peap_init(struct eap_sm *sm) +{ + struct eap_peap_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + sm->peap_done = FALSE; + data->peap_version = EAP_PEAP_VERSION; + data->force_peap_version = -1; + data->peap_outer_success = 2; + data->crypto_binding = OPTIONAL_BINDING; + + if (config && config->phase1 && + eap_peap_parse_phase1(data, config->phase1) < 0) { + eap_peap_deinit(sm, data); + return NULL; + } + + if (eap_peer_select_phase2_methods(config, "auth=", + &data->phase2_types, + &data->num_phase2_types) < 0) { + eap_peap_deinit(sm, data); + return NULL; + } + + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_NONE; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); + eap_peap_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_peap_free_key(struct eap_peap_data *data) +{ + if (data->key_data) { + bin_clear_free(data->key_data, EAP_TLS_KEY_LEN); + data->key_data = NULL; + } +} + + +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); + eap_peap_free_key(data); + os_free(data->session_id); + wpabuf_free(data->pending_phase2_req); + os_free(data); +} + + +/** + * eap_tlv_build_nak - Build EAP-TLV NAK message + * @id: EAP identifier for the header + * @nak_type: TLV type (EAP_TLV_*) + * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure + * + * This function builds an EAP-TLV NAK message. The caller is responsible for + * freeing the returned buffer. + */ +static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type) +{ + struct wpabuf *msg; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10, + EAP_CODE_RESPONSE, id); + if (msg == NULL) + return NULL; + + wpabuf_put_u8(msg, 0x80); /* Mandatory */ + wpabuf_put_u8(msg, EAP_TLV_NAK_TLV); + wpabuf_put_be16(msg, 6); /* Length */ + wpabuf_put_be32(msg, 0); /* Vendor-Id */ + wpabuf_put_be16(msg, nak_type); /* NAK-Type */ + + return msg; +} + + +static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data, + u8 *isk, size_t isk_len) +{ + u8 *key; + size_t key_len; + + os_memset(isk, 0, isk_len); + if (data->phase2_method == NULL || data->phase2_priv == NULL || + data->phase2_method->isKeyAvailable == NULL || + data->phase2_method->getKey == NULL) + return 0; + + if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) || + (key = data->phase2_method->getKey(sm, data->phase2_priv, + &key_len)) == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material " + "from Phase 2"); + return -1; + } + + if (key_len > isk_len) + key_len = isk_len; + os_memcpy(isk, key, key_len); + os_free(key); + + return 0; +} + + +static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) +{ + u8 *tk; + u8 isk[32], imck[60]; + + /* + * Tunnel key (TK) is the first 60 octets of the key generated by + * phase 1 of PEAP (based on TLS). + */ + tk = data->key_data; + if (tk == NULL) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); + + if (data->reauth && + tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + /* Fast-connect: IPMK|CMK = TK */ + os_memcpy(data->ipmk, tk, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK", + data->ipmk, 40); + os_memcpy(data->cmk, tk + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK", + data->cmk, 20); + return 0; + } + + if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); + + /* + * IPMK Seed = "Inner Methods Compound Keys" | ISK + * TempKey = First 40 octets of TK + * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) + * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space + * in the end of the label just before ISK; is that just a typo?) + */ + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); + if (peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", + imck, sizeof(imck)); + + os_memcpy(data->ipmk, imck, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); + os_memcpy(data->cmk, imck + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + + return 0; +} + + +static int eap_tlv_add_cryptobinding(struct eap_sm *sm, + struct eap_peap_data *data, + struct wpabuf *buf) +{ + u8 *mac; + u8 eap_type = EAP_TYPE_PEAP; + const u8 *addr[2]; + size_t len[2]; + u16 tlv_type; + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + addr[0] = wpabuf_put(buf, 0); + len[0] = 60; + addr[1] = &eap_type; + len[1] = 1; + + tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; + wpabuf_put_be16(buf, tlv_type); + wpabuf_put_be16(buf, 56); + + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_u8(buf, data->peap_version); /* Version */ + wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */ + wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */ + wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */ + mac = wpabuf_put(buf, 20); /* Compound_MAC */ + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", + addr[0], len[0]); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", + addr[1], len[1]); + hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN); + data->crypto_binding_used = 1; + + return 0; +} + + +/** + * eap_tlv_build_result - Build EAP-TLV Result message + * @id: EAP identifier for the header + * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE) + * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure + * + * This function builds an EAP-TLV Result message. The caller is responsible + * for freeing the returned buffer. + */ +static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm, + struct eap_peap_data *data, + int crypto_tlv_used, + int id, u16 status) +{ + struct wpabuf *msg; + size_t len; + + if (data->crypto_binding == NO_BINDING) + crypto_tlv_used = 0; + + len = 6; + if (crypto_tlv_used) + len += 60; /* Cryptobinding TLV */ + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, + EAP_CODE_RESPONSE, id); + if (msg == NULL) + return NULL; + + wpabuf_put_u8(msg, 0x80); /* Mandatory */ + wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV); + wpabuf_put_be16(msg, 2); /* Length */ + wpabuf_put_be16(msg, status); /* Status */ + + if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, + struct eap_peap_data *data, + const u8 *crypto_tlv, + size_t crypto_tlv_len) +{ + u8 buf[61], mac[SHA1_MAC_LEN]; + const u8 *pos; + + if (eap_peap_derive_cmk(sm, data) < 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK"); + return -1; + } + + if (crypto_tlv_len != 4 + 56) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " + "length %d", (int) crypto_tlv_len); + return -1; + } + + pos = crypto_tlv; + pos += 4; /* TLV header */ + if (pos[1] != data->peap_version) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " + "mismatch (was %d; expected %d)", + pos[1], data->peap_version); + return -1; + } + + if (pos[3] != 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " + "SubType %d", pos[3]); + return -1; + } + pos += 4; + os_memcpy(data->binding_nonce, pos, 32); + pos += 32; /* Nonce */ + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + os_memcpy(buf, crypto_tlv, 60); + os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ + buf[60] = EAP_TYPE_PEAP; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data", + buf, sizeof(buf)); + hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); + + if (os_memcmp_const(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 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; + } + + 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 (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; + struct wpabuf msg; + + 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 */ + } + + wpabuf_set(&msg, pos, left); + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + 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, &msg, + &resp); + + if (res < 0) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: TLS processing failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return 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"); + eap_peap_free_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->force_new_label) + label = "client PEAP encryption"; + else + label = "client EAP encryption"; + wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in " + "key derivation", label); + data->key_data = + eap_peer_tls_derive_key(sm, &data->ssl, label, + EAP_TLS_KEY_LEN); + if (data->key_data) { + wpa_hexdump_key(MSG_DEBUG, + "EAP-PEAP: Derived key", + data->key_data, + EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to " + "derive key"); + } + + os_free(data->session_id); + data->session_id = + eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_PEAP, + &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, + "EAP-PEAP: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to " + "derive Session-Id"); + } + + if (sm->workaround && data->resuming) { + /* + * At least few RADIUS servers (Aegis v1.1.6; + * but not v1.1.4; and Cisco ACS) seem to be + * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco + * ACS) session resumption with outer + * EAP-Success. This does not seem to follow + * draft-josefsson-pppext-eap-tls-eap-05.txt + * section 4.2, so only allow this if EAP + * workarounds are enabled. + */ + wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - " + "allow outer EAP-Success to " + "terminate PEAP resumption"); + ret->decision = DECISION_COND_SUCC; + data->phase2_success = 1; + } + + data->resuming = 0; + } + + if (res == 2) { + /* + * Application data included in the handshake message. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = resp; + resp = NULL; + 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; + eap_peap_free_key(data); + os_free(data->session_id); + data->session_id = NULL; + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + if (data->phase2_priv && data->phase2_method && + data->phase2_method->init_for_reauth) + data->phase2_method->init_for_reauth(sm, data->phase2_priv); + data->phase2_success = 0; + data->phase2_eap_success = 0; + data->phase2_eap_started = 0; + data->resuming = 1; + data->reauth = 1; + sm->peap_done = FALSE; + return priv; +} + + +static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_peap_data *data = priv; + int len, ret; + + len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); + if (data->phase2_method) { + ret = os_snprintf(buf + len, buflen - len, + "EAP-PEAPv%d Phase2 method=%s\n", + data->peap_version, + data->phase2_method->name); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + return len; +} + + +static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return data->key_data != NULL && data->phase2_success; +} + + +static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *key; + + if (data->key_data == NULL || !data->phase2_success) + return NULL; + + key = os_malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + + if (data->crypto_binding_used) { + u8 csk[128]; + /* + * Note: It looks like Microsoft implementation requires null + * termination for this label while the one used for deriving + * IPMK|CMK did not use null termination. + */ + if (peap_prfplus(data->peap_version, data->ipmk, 40, + "Session Key Generating Function", + (u8 *) "\00", 1, csk, sizeof(csk)) < 0) { + os_free(key); + return NULL; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); + os_memcpy(key, csk, EAP_TLS_KEY_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", + key, EAP_TLS_KEY_LEN); + } else + os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *id; + + if (data->session_id == NULL || !data->phase2_success) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + +int eap_peer_peap_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); + if (eap == NULL) + return -1; + + eap->init = eap_peap_init; + eap->deinit = eap_peap_deinit; + eap->process = eap_peap_process; + eap->isKeyAvailable = eap_peap_isKeyAvailable; + eap->getKey = eap_peap_getKey; + eap->get_status = eap_peap_get_status; + eap->has_reauth_data = eap_peap_has_reauth_data; + eap->deinit_for_reauth = eap_peap_deinit_for_reauth; + eap->init_for_reauth = eap_peap_init_for_reauth; + eap->getSessionId = eap_peap_get_session_id; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_proxy.h b/freebsd/contrib/wpa/src/eap_peer/eap_proxy.h new file mode 100644 index 00000000..23cdbe69 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_proxy.h @@ -0,0 +1,49 @@ +/* + * EAP proxy definitions + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_PROXY_H +#define EAP_PROXY_H + +struct eap_proxy_sm; +struct eapol_callbacks; +struct eap_sm; +struct eap_peer_config; + +enum eap_proxy_status { + EAP_PROXY_FAILURE = 0x00, + EAP_PROXY_SUCCESS +}; + +struct eap_proxy_sm * +eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + void *msg_ctx); + +void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy); + +int eap_proxy_key_available(struct eap_proxy_sm *sm); + +const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len); + +struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm); + +int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm); + +enum eap_proxy_status +eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData, + int eapReqDataLen); + +int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen, + int verbose); + +int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf, + size_t *imsi_len); + +int eap_proxy_notify_config(struct eap_proxy_sm *sm, + struct eap_peer_config *config); + +#endif /* EAP_PROXY_H */ diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_psk.c b/freebsd/contrib/wpa/src/eap_peer/eap_psk.c new file mode 100644 index 00000000..f91c9b39 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_psk.c @@ -0,0 +1,504 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP peer method: EAP-PSK (RFC 4764) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * Note: EAP-PSK is an EAP authentication method and as such, completely + * different from WPA-PSK. This file is not needed for WPA-PSK functionality. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/aes_wrap.h" +#include "crypto/random.h" +#include "eap_common/eap_psk_common.h" +#include "eap_i.h" + + +struct eap_psk_data { + enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state; + u8 rand_p[EAP_PSK_RAND_LEN]; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; + u8 *id_s, *id_p; + size_t id_s_len, id_p_len; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; +}; + + +static void * eap_psk_init(struct eap_sm *sm) +{ + struct eap_psk_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (!password || password_len != 16) { + wpa_printf(MSG_INFO, "EAP-PSK: 16-octet pre-shared key not " + "configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + if (eap_psk_key_setup(password, data->ak, data->kdk)) { + os_free(data); + return NULL; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); + data->state = PSK_INIT; + + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + data->id_p = os_malloc(identity_len); + if (data->id_p) + os_memcpy(data->id_p, identity, identity_len); + data->id_p_len = identity_len; + } + if (data->id_p == NULL) { + wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity"); + os_free(data); + return NULL; + } + + return data; +} + + +static void eap_psk_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + os_free(data->id_s); + os_free(data->id_p); + bin_clear_free(data, sizeof(*data)); +} + + +static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_psk_hdr_1 *hdr1; + struct eap_psk_hdr_2 *hdr2; + struct wpabuf *resp; + u8 *buf, *pos; + size_t buflen, len; + const u8 *cpos; + + wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state"); + + cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); + hdr1 = (const struct eap_psk_hdr_1 *) cpos; + if (cpos == NULL || len < sizeof(*hdr1)) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message " + "length (%lu; expected %lu or more)", + (unsigned long) len, + (unsigned long) sizeof(*hdr1)); + ret->ignore = TRUE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags); + if (EAP_PSK_FLAGS_GET_T(hdr1->flags) != 0) { + wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)", + EAP_PSK_FLAGS_GET_T(hdr1->flags)); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s, + EAP_PSK_RAND_LEN); + os_memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); + os_free(data->id_s); + data->id_s_len = len - sizeof(*hdr1); + data->id_s = os_malloc(data->id_s_len); + if (data->id_s == NULL) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for " + "ID_S (len=%lu)", (unsigned long) data->id_s_len); + ret->ignore = TRUE; + return NULL; + } + os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", + data->id_s, data->id_s_len); + + if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); + ret->ignore = TRUE; + return NULL; + } + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, + sizeof(*hdr2) + data->id_p_len, EAP_CODE_RESPONSE, + eap_get_id(reqData)); + if (resp == NULL) + return NULL; + hdr2 = wpabuf_put(resp, sizeof(*hdr2)); + hdr2->flags = EAP_PSK_FLAGS_SET_T(1); /* T=1 */ + os_memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); + os_memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN); + wpabuf_put_data(resp, data->id_p, data->id_p_len); + /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ + buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; + buf = os_malloc(buflen); + if (buf == NULL) { + wpabuf_free(resp); + return NULL; + } + os_memcpy(buf, data->id_p, data->id_p_len); + pos = buf + data->id_p_len; + os_memcpy(pos, data->id_s, data->id_s_len); + pos += data->id_s_len; + os_memcpy(pos, hdr1->rand_s, EAP_PSK_RAND_LEN); + pos += EAP_PSK_RAND_LEN; + os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); + if (omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p)) { + os_free(buf); + wpabuf_free(resp); + return NULL; + } + os_free(buf); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p, + EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P", + data->id_p, data->id_p_len); + + data->state = PSK_MAC_SENT; + + return resp; +} + + +static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_psk_hdr_3 *hdr3; + struct eap_psk_hdr_4 *hdr4; + struct wpabuf *resp; + u8 *buf, *rpchannel, nonce[16], *decrypted; + const u8 *pchannel, *tag, *msg; + u8 mac[EAP_PSK_MAC_LEN]; + size_t buflen, left, data_len, len, plen; + int failed = 0; + const u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state"); + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, + reqData, &len); + hdr3 = (const struct eap_psk_hdr_3 *) pos; + if (pos == NULL || len < sizeof(*hdr3)) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message " + "length (%lu; expected %lu or more)", + (unsigned long) len, + (unsigned long) sizeof(*hdr3)); + ret->ignore = TRUE; + return NULL; + } + left = len - sizeof(*hdr3); + pchannel = (const u8 *) (hdr3 + 1); + wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags); + if (EAP_PSK_FLAGS_GET_T(hdr3->flags) != 2) { + wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)", + EAP_PSK_FLAGS_GET_T(hdr3->flags)); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s, + EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left); + + if (left < 4 + 16 + 1) { + wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " + "third message (len=%lu, expected 21)", + (unsigned long) left); + ret->ignore = TRUE; + return NULL; + } + + /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ + buflen = data->id_s_len + EAP_PSK_RAND_LEN; + buf = os_malloc(buflen); + if (buf == NULL) + return NULL; + os_memcpy(buf, data->id_s, data->id_s_len); + os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); + if (omac1_aes_128(data->ak, buf, buflen, mac)) { + os_free(buf); + return NULL; + } + os_free(buf); + if (os_memcmp_const(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third " + "message"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully"); + + if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, + data->msk, data->emsk)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN); + + os_memset(nonce, 0, 12); + os_memcpy(nonce + 12, pchannel, 4); + pchannel += 4; + left -= 4; + + tag = pchannel; + pchannel += 16; + left -= 16; + + msg = pchannel; + + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce", + nonce, sizeof(nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr", + wpabuf_head(reqData), 5); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left); + + decrypted = os_malloc(left); + if (decrypted == NULL) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + os_memcpy(decrypted, msg, left); + + if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), + wpabuf_head(reqData), + sizeof(struct eap_hdr) + 1 + + sizeof(*hdr3) - EAP_PSK_MAC_LEN, decrypted, + left, tag)) { + wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); + os_free(decrypted); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", + decrypted, left); + + /* Verify R flag */ + switch (decrypted[0] >> 6) { + case EAP_PSK_R_FLAG_CONT: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); + failed = 1; + break; + case EAP_PSK_R_FLAG_DONE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); + break; + case EAP_PSK_R_FLAG_DONE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); + wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected " + "authentication"); + failed = 1; + break; + } + + data_len = 1; + if ((decrypted[0] & EAP_PSK_E_FLAG) && left > 1) + data_len++; + plen = sizeof(*hdr4) + 4 + 16 + data_len; + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, plen, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) { + os_free(decrypted); + return NULL; + } + hdr4 = wpabuf_put(resp, sizeof(*hdr4)); + hdr4->flags = EAP_PSK_FLAGS_SET_T(3); /* T=3 */ + os_memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN); + rpchannel = wpabuf_put(resp, 4 + 16 + data_len); + + /* nonce++ */ + inc_byte_array(nonce, sizeof(nonce)); + os_memcpy(rpchannel, nonce + 12, 4); + + if (decrypted[0] & EAP_PSK_E_FLAG) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag"); + failed = 1; + rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) | + EAP_PSK_E_FLAG; + if (left > 1) { + /* Add empty EXT_Payload with same EXT_Type */ + rpchannel[4 + 16 + 1] = decrypted[1]; + } + } else if (failed) + rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6; + else + rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; + + wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)", + rpchannel + 4 + 16, data_len); + if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), + wpabuf_head(resp), + sizeof(struct eap_hdr) + 1 + sizeof(*hdr4), + rpchannel + 4 + 16, data_len, rpchannel + 4)) { + os_free(decrypted); + wpabuf_free(resp); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)", + rpchannel, 4 + 16 + data_len); + + wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully", + failed ? "un" : ""); + data->state = PSK_DONE; + ret->methodState = METHOD_DONE; + ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC; + + os_free(decrypted); + + return resp; +} + + +static struct wpabuf * eap_psk_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_psk_data *data = priv; + const u8 *pos; + struct wpabuf *resp = NULL; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (data->state) { + case PSK_INIT: + resp = eap_psk_process_1(data, ret, reqData); + break; + case PSK_MAC_SENT: + resp = eap_psk_process_3(data, ret, reqData); + break; + case PSK_DONE: + wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore " + "unexpected message"); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return resp; +} + + +static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + return data->state == PSK_DONE; +} + + +static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *key; + + if (data->state != PSK_DONE) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_MSK_LEN; + os_memcpy(key, data->msk, EAP_MSK_LEN); + + return key; +} + + +static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *id; + + if (data->state != PSK_DONE) + return NULL; + + *len = 1 + 2 * EAP_PSK_RAND_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_PSK; + os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN); + os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len); + + return id; +} + + +static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *key; + + if (data->state != PSK_DONE) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + + return key; +} + + +int eap_peer_psk_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); + if (eap == NULL) + return -1; + + eap->init = eap_psk_init; + eap->deinit = eap_psk_deinit; + eap->process = eap_psk_process; + eap->isKeyAvailable = eap_psk_isKeyAvailable; + eap->getKey = eap_psk_getKey; + eap->getSessionId = eap_psk_get_session_id; + eap->get_emsk = eap_psk_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_tls.c b/freebsd/contrib/wpa/src/eap_peer/eap_tls.c new file mode 100644 index 00000000..8ca6db9c --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_tls.c @@ -0,0 +1,441 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP peer method: EAP-TLS (RFC 2716) + * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/tls.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" + + +static void eap_tls_deinit(struct eap_sm *sm, void *priv); + + +struct eap_tls_data { + struct eap_ssl_data ssl; + u8 *key_data; + u8 *session_id; + size_t id_len; + void *ssl_ctx; + u8 eap_type; +}; + + +static void * eap_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL || + ((sm->init_phase2 ? config->private_key2 : config->private_key) + == NULL && + (sm->init_phase2 ? config->engine2 : config->engine) == 0)) { + wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_deinit(sm, data); + if (config->engine) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard " + "PIN"); + eap_sm_request_pin(sm); + sm->ignore = TRUE; + } else if (config->private_key && !config->private_key_passwd) + { + wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private " + "key passphrase"); + eap_sm_request_passphrase(sm); + sm->ignore = TRUE; + } + return NULL; + } + + data->eap_type = EAP_TYPE_TLS; + + return data; +} + + +#ifdef EAP_UNAUTH_TLS +static void * eap_unauth_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, + EAP_UNAUTH_TLS_TYPE)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_deinit(sm, data); + return NULL; + } + + data->eap_type = EAP_UNAUTH_TLS_TYPE; + + return data; +} +#endif /* EAP_UNAUTH_TLS */ + + +#ifdef CONFIG_HS20 +static void * eap_wfa_unauth_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, + EAP_WFA_UNAUTH_TLS_TYPE)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_deinit(sm, data); + return NULL; + } + + data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE; + + return data; +} +#endif /* CONFIG_HS20 */ + + +static void eap_tls_free_key(struct eap_tls_data *data) +{ + if (data->key_data) { + bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + data->key_data = NULL; + } +} + + +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); + eap_tls_free_key(data); + os_free(data->session_id); + os_free(data); +} + + +static struct wpabuf * eap_tls_failure(struct eap_sm *sm, + struct eap_tls_data *data, + struct eap_method_ret *ret, int res, + struct wpabuf *resp, u8 id) +{ + wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed"); + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + + if (resp) { + /* + * This is likely an alert message, so send it instead of just + * ACKing the error. + */ + return resp; + } + + return eap_peer_tls_build_ack(id, data->eap_type, 0); +} + + +static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, + struct eap_method_ret *ret) +{ + wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + + eap_tls_free_key(data); + data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN + + EAP_EMSK_LEN); + if (data->key_data) { + wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key", + data->key_data, EAP_TLS_KEY_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK", + data->key_data + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); + } else { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key"); + } + + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_TLS, + &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-TLS: Failed to derive Session-Id"); + } +} + + +static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + size_t left; + int res; + struct wpabuf *resp; + u8 flags, id; + const u8 *pos; + struct eap_tls_data *data = priv; + struct wpabuf msg; + + pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, + reqData, &left, &flags); + if (pos == NULL) + return NULL; + id = eap_get_id(reqData); + + if (flags & EAP_TLS_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Start"); + left = 0; /* make sure that this frame is empty, even though it + * should always be, anyway */ + } + + resp = NULL; + wpabuf_set(&msg, pos, left); + res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0, + id, &msg, &resp); + + if (res < 0) { + return eap_tls_failure(sm, data, ret, res, resp, id); + } + + if (tls_connection_established(data->ssl_ctx, data->ssl.conn)) + eap_tls_success(sm, data, ret); + + if (res == 1) { + wpabuf_free(resp); + return eap_peer_tls_build_ack(id, data->eap_type, 0); + } + + return resp; +} + + +static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return tls_connection_established(data->ssl_ctx, data->ssl.conn); +} + + +static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ +} + + +static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + eap_tls_free_key(data); + os_free(data->session_id); + data->session_id = NULL; + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + return priv; +} + + +static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_tls_data *data = priv; + return eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); +} + + +static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return data->key_data != NULL; +} + + +static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *key; + + if (data->key_data == NULL) + return NULL; + + key = os_malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *key; + + if (data->key_data == NULL) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); + + return key; +} + + +static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *id; + + if (data->session_id == NULL) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + +int eap_peer_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_tls_init; + eap->deinit = eap_tls_deinit; + eap->process = eap_tls_process; + eap->isKeyAvailable = eap_tls_isKeyAvailable; + eap->getKey = eap_tls_getKey; + eap->getSessionId = eap_tls_get_session_id; + eap->get_status = eap_tls_get_status; + eap->has_reauth_data = eap_tls_has_reauth_data; + eap->deinit_for_reauth = eap_tls_deinit_for_reauth; + eap->init_for_reauth = eap_tls_init_for_reauth; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} + + +#ifdef EAP_UNAUTH_TLS +int eap_peer_unauth_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_unauth_tls_init; + eap->deinit = eap_tls_deinit; + eap->process = eap_tls_process; + eap->isKeyAvailable = eap_tls_isKeyAvailable; + eap->getKey = eap_tls_getKey; + eap->get_status = eap_tls_get_status; + eap->has_reauth_data = eap_tls_has_reauth_data; + eap->deinit_for_reauth = eap_tls_deinit_for_reauth; + eap->init_for_reauth = eap_tls_init_for_reauth; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} +#endif /* EAP_UNAUTH_TLS */ + + +#ifdef CONFIG_HS20 +int eap_peer_wfa_unauth_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_WFA_NEW, + EAP_VENDOR_WFA_UNAUTH_TLS, + "WFA-UNAUTH-TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_wfa_unauth_tls_init; + eap->deinit = eap_tls_deinit; + eap->process = eap_tls_process; + eap->isKeyAvailable = eap_tls_isKeyAvailable; + eap->getKey = eap_tls_getKey; + eap->get_status = eap_tls_get_status; + eap->has_reauth_data = eap_tls_has_reauth_data; + eap->deinit_for_reauth = eap_tls_deinit_for_reauth; + eap->init_for_reauth = eap_tls_init_for_reauth; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} +#endif /* CONFIG_HS20 */ diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_tls_common.c b/freebsd/contrib/wpa/src/eap_peer/eap_tls_common.c new file mode 100644 index 00000000..ff6c937c --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_tls_common.c @@ -0,0 +1,1110 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" + + +static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + if (type == EAP_UNAUTH_TLS_TYPE) + return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, + code, identifier); + if (type == EAP_WFA_UNAUTH_TLS_TYPE) + return eap_msg_alloc(EAP_VENDOR_WFA_NEW, + EAP_VENDOR_WFA_UNAUTH_TLS, payload_len, + code, identifier); + return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, + identifier); +} + + +static int eap_tls_check_blob(struct eap_sm *sm, const char **name, + const u8 **data, size_t *data_len) +{ + const struct wpa_config_blob *blob; + + if (*name == NULL || os_strncmp(*name, "blob://", 7) != 0) + return 0; + + blob = eap_get_config_blob(sm, *name + 7); + if (blob == NULL) { + wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not " + "found", __func__, *name + 7); + return -1; + } + + *name = NULL; + *data = blob->data; + *data_len = blob->len; + + return 0; +} + + +static void eap_tls_params_flags(struct tls_connection_params *params, + const char *txt) +{ + if (txt == NULL) + return; + if (os_strstr(txt, "tls_allow_md5=1")) + params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; + if (os_strstr(txt, "tls_disable_time_checks=1")) + params->flags |= TLS_CONN_DISABLE_TIME_CHECKS; + if (os_strstr(txt, "tls_disable_session_ticket=1")) + params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; + if (os_strstr(txt, "tls_disable_session_ticket=0")) + params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET; + if (os_strstr(txt, "tls_disable_tlsv1_0=1")) + params->flags |= TLS_CONN_DISABLE_TLSv1_0; + if (os_strstr(txt, "tls_disable_tlsv1_0=0")) + params->flags &= ~TLS_CONN_DISABLE_TLSv1_0; + if (os_strstr(txt, "tls_disable_tlsv1_1=1")) + params->flags |= TLS_CONN_DISABLE_TLSv1_1; + if (os_strstr(txt, "tls_disable_tlsv1_1=0")) + params->flags &= ~TLS_CONN_DISABLE_TLSv1_1; + if (os_strstr(txt, "tls_disable_tlsv1_2=1")) + params->flags |= TLS_CONN_DISABLE_TLSv1_2; + if (os_strstr(txt, "tls_disable_tlsv1_2=0")) + params->flags &= ~TLS_CONN_DISABLE_TLSv1_2; +} + + +static void eap_tls_params_from_conf1(struct tls_connection_params *params, + struct eap_peer_config *config) +{ + params->ca_cert = (char *) config->ca_cert; + params->ca_path = (char *) config->ca_path; + params->client_cert = (char *) config->client_cert; + params->private_key = (char *) config->private_key; + params->private_key_passwd = (char *) config->private_key_passwd; + params->dh_file = (char *) config->dh_file; + params->subject_match = (char *) config->subject_match; + params->altsubject_match = (char *) config->altsubject_match; + params->suffix_match = config->domain_suffix_match; + params->domain_match = config->domain_match; + params->engine = config->engine; + params->engine_id = config->engine_id; + params->pin = config->pin; + params->key_id = config->key_id; + params->cert_id = config->cert_id; + params->ca_cert_id = config->ca_cert_id; + eap_tls_params_flags(params, config->phase1); +} + + +static void eap_tls_params_from_conf2(struct tls_connection_params *params, + struct eap_peer_config *config) +{ + params->ca_cert = (char *) config->ca_cert2; + params->ca_path = (char *) config->ca_path2; + params->client_cert = (char *) config->client_cert2; + params->private_key = (char *) config->private_key2; + params->private_key_passwd = (char *) config->private_key2_passwd; + params->dh_file = (char *) config->dh_file2; + params->subject_match = (char *) config->subject_match2; + params->altsubject_match = (char *) config->altsubject_match2; + params->suffix_match = config->domain_suffix_match2; + params->domain_match = config->domain_match2; + params->engine = config->engine2; + params->engine_id = config->engine2_id; + params->pin = config->pin2; + params->key_id = config->key2_id; + params->cert_id = config->cert2_id; + params->ca_cert_id = config->ca_cert2_id; + eap_tls_params_flags(params, config->phase2); +} + + +static int eap_tls_params_from_conf(struct eap_sm *sm, + struct eap_ssl_data *data, + struct tls_connection_params *params, + struct eap_peer_config *config, int phase2) +{ + os_memset(params, 0, sizeof(*params)); + if (sm->workaround && data->eap_type != EAP_TYPE_FAST) { + /* + * Some deployed authentication servers seem to be unable to + * handle the TLS Session Ticket extension (they are supposed + * to ignore unrecognized TLS extensions, but end up rejecting + * the ClientHello instead). As a workaround, disable use of + * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and + * EAP-TTLS (EAP-FAST uses session ticket, so any server that + * supports EAP-FAST does not need this workaround). + */ + params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; + } + if (phase2) { + wpa_printf(MSG_DEBUG, "TLS: using phase2 config options"); + eap_tls_params_from_conf2(params, config); + } else { + wpa_printf(MSG_DEBUG, "TLS: using phase1 config options"); + eap_tls_params_from_conf1(params, config); + if (data->eap_type == EAP_TYPE_FAST) + params->flags |= TLS_CONN_EAP_FAST; + } + + /* + * Use blob data, if available. Otherwise, leave reference to external + * file as-is. + */ + if (eap_tls_check_blob(sm, ¶ms->ca_cert, ¶ms->ca_cert_blob, + ¶ms->ca_cert_blob_len) || + eap_tls_check_blob(sm, ¶ms->client_cert, + ¶ms->client_cert_blob, + ¶ms->client_cert_blob_len) || + eap_tls_check_blob(sm, ¶ms->private_key, + ¶ms->private_key_blob, + ¶ms->private_key_blob_len) || + eap_tls_check_blob(sm, ¶ms->dh_file, ¶ms->dh_blob, + ¶ms->dh_blob_len)) { + wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs"); + return -1; + } + + params->openssl_ciphers = config->openssl_ciphers; + + return 0; +} + + +static int eap_tls_init_connection(struct eap_sm *sm, + struct eap_ssl_data *data, + struct eap_peer_config *config, + struct tls_connection_params *params) +{ + int res; + + if (config->ocsp) + params->flags |= TLS_CONN_REQUEST_OCSP; + if (config->ocsp == 2) + params->flags |= TLS_CONN_REQUIRE_OCSP; + data->conn = tls_connection_init(data->ssl_ctx); + if (data->conn == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " + "connection"); + return -1; + } + + res = tls_connection_set_params(data->ssl_ctx, data->conn, params); + if (res == TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN) { + /* + * At this point with the pkcs11 engine the PIN is 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. + */ + wpa_printf(MSG_INFO, + "TLS: Bad PIN provided, requesting a new one"); + os_free(config->pin); + config->pin = NULL; + eap_sm_request_pin(sm); + sm->ignore = TRUE; + } else if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { + wpa_printf(MSG_INFO, "TLS: Failed to initialize engine"); + } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key"); + sm->ignore = TRUE; + } + if (res) { + wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection " + "parameters"); + tls_connection_deinit(data->ssl_ctx, data->conn); + data->conn = NULL; + return -1; + } + + return 0; +} + + +/** + * eap_peer_tls_ssl_init - Initialize shared TLS functionality + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @config: Pointer to the network configuration + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + * Returns: 0 on success, -1 on failure + * + * This function is used to initialize shared TLS functionality for EAP-TLS, + * EAP-PEAP, EAP-TTLS, and EAP-FAST. + */ +int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + struct eap_peer_config *config, u8 eap_type) +{ + struct tls_connection_params params; + + if (config == NULL) + return -1; + + data->eap = sm; + data->eap_type = eap_type; + data->phase2 = sm->init_phase2; + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; + if (eap_tls_params_from_conf(sm, data, ¶ms, config, data->phase2) < + 0) + return -1; + + if (eap_tls_init_connection(sm, data, config, ¶ms) < 0) + return -1; + + data->tls_out_limit = config->fragment_size; + if (data->phase2) { + /* Limit the fragment size in the inner TLS authentication + * since the outer authentication with EAP-PEAP does not yet + * support fragmentation */ + if (data->tls_out_limit > 100) + data->tls_out_limit -= 100; + } + + if (config->phase1 && + os_strstr(config->phase1, "include_tls_length=1")) { + wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in " + "unfragmented packets"); + data->include_tls_length = 1; + } + + return 0; +} + + +/** + * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * + * This function deinitializes shared TLS functionality that was initialized + * with eap_peer_tls_ssl_init(). + */ +void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) +{ + tls_connection_deinit(data->ssl_ctx, data->conn); + eap_peer_tls_reset_input(data); + eap_peer_tls_reset_output(data); +} + + +/** + * eap_peer_tls_derive_key - Derive a key based on TLS session data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @label: Label string for deriving the keys, e.g., "client EAP encryption" + * @len: Length of the key material to generate (usually 64 for MSK) + * Returns: Pointer to allocated key on success or %NULL on failure + * + * This function uses TLS-PRF to generate pseudo-random data based on the TLS + * session data (client/server random and master key). Each key type may use a + * different label to bind the key usage into the generated material. + * + * The caller is responsible for freeing the returned buffer. + */ +u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + const char *label, size_t len) +{ + u8 *out; + + out = os_malloc(len); + if (out == NULL) + return NULL; + + if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0, + out, len)) { + os_free(out); + return NULL; + } + + return out; +} + + +/** + * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + * @len: Pointer to length of the session ID generated + * Returns: Pointer to allocated Session-Id on success or %NULL on failure + * + * This function derive the Session-Id based on the TLS session data + * (client/server random and method type). + * + * The caller is responsible for freeing the returned buffer. + */ +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len) +{ + struct tls_random keys; + u8 *out; + + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) + return NULL; + + if (keys.client_random == NULL || keys.server_random == NULL) + return NULL; + + *len = 1 + keys.client_random_len + keys.server_random_len; + out = os_malloc(*len); + if (out == NULL) + return NULL; + + /* Session-Id = EAP type || client.random || server.random */ + out[0] = eap_type; + os_memcpy(out + 1, keys.client_random, keys.client_random_len); + os_memcpy(out + 1 + keys.client_random_len, keys.server_random, + keys.server_random_len); + + return out; +} + + +/** + * eap_peer_tls_reassemble_fragment - Reassemble a received fragment + * @data: Data for TLS processing + * @in_data: Next incoming TLS segment + * Returns: 0 on success, 1 if more data is needed for the full message, or + * -1 on error + */ +static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, + const struct wpabuf *in_data) +{ + size_t tls_in_len, in_len; + + tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0; + in_len = in_data ? wpabuf_len(in_data) : 0; + + if (tls_in_len + in_len == 0) { + /* No message data received?! */ + wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: " + "tls_in_left=%lu tls_in_len=%lu in_len=%lu", + (unsigned long) data->tls_in_left, + (unsigned long) tls_in_len, + (unsigned long) in_len); + eap_peer_tls_reset_input(data); + return -1; + } + + if (tls_in_len + in_len > 65536) { + /* + * Limit length to avoid rogue servers from causing large + * memory allocations. + */ + wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over " + "64 kB)"); + eap_peer_tls_reset_input(data); + return -1; + } + + if (in_len > data->tls_in_left) { + /* Sender is doing something odd - reject message */ + wpa_printf(MSG_INFO, "SSL: more data than TLS message length " + "indicated"); + eap_peer_tls_reset_input(data); + return -1; + } + + if (wpabuf_resize(&data->tls_in, in_len) < 0) { + wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS " + "data"); + eap_peer_tls_reset_input(data); + return -1; + } + if (in_data) + wpabuf_put_buf(data->tls_in, in_data); + data->tls_in_left -= in_len; + + if (data->tls_in_left > 0) { + wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input " + "data", (unsigned long) data->tls_in_left); + return 1; + } + + return 0; +} + + +/** + * eap_peer_tls_data_reassemble - Reassemble TLS data + * @data: Data for TLS processing + * @in_data: Next incoming TLS segment + * @need_more_input: Variable for returning whether more input data is needed + * to reassemble this TLS packet + * Returns: Pointer to output data, %NULL on error or when more data is needed + * for the full message (in which case, *need_more_input is also set to 1). + * + * This function reassembles TLS fragments. Caller must not free the returned + * data buffer since an internal pointer to it is maintained. + */ +static const struct wpabuf * eap_peer_tls_data_reassemble( + struct eap_ssl_data *data, const struct wpabuf *in_data, + int *need_more_input) +{ + *need_more_input = 0; + + if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) { + /* Message has fragments */ + int res = eap_peer_tls_reassemble_fragment(data, in_data); + if (res) { + if (res == 1) + *need_more_input = 1; + return NULL; + } + + /* Message is now fully reassembled. */ + } else { + /* No fragments in this message, so just make a copy of it. */ + data->tls_in_left = 0; + data->tls_in = wpabuf_dup(in_data); + if (data->tls_in == NULL) + return NULL; + } + + return data->tls_in; +} + + +/** + * eap_tls_process_input - Process incoming TLS message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @in_data: Message received from the server + * @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 struct wpabuf *in_data, + struct wpabuf **out_data) +{ + const struct wpabuf *msg; + int need_more_input; + struct wpabuf *appl_data; + + msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input); + if (msg == NULL) + return need_more_input ? 1 : -1; + + /* Full TLS message reassembled - continue handshake processing */ + if (data->tls_out) { + /* This should not happen.. */ + wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending " + "tls_out data even though tls_out_len = 0"); + wpabuf_free(data->tls_out); + WPA_ASSERT(data->tls_out == NULL); + } + appl_data = NULL; + data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn, + msg, &appl_data); + + eap_peer_tls_reset_input(data); + + if (appl_data && + tls_connection_established(data->ssl_ctx, data->conn) && + !tls_connection_get_failed(data->ssl_ctx, data->conn)) { + wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data", + appl_data); + *out_data = appl_data; + return 2; + } + + wpabuf_free(appl_data); + + return 0; +} + + +/** + * eap_tls_process_output - Process outgoing TLS message + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * @id: EAP identifier for the response + * @ret: Return value to use on success + * @out_data: Buffer for returning the allocated output buffer + * Returns: ret (0 or 1) on success, -1 on failure + */ +static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, + int peap_version, u8 id, int ret, + struct wpabuf **out_data) +{ + size_t len; + u8 *flags; + int more_fragments, length_included; + + if (data->tls_out == NULL) + return -1; + len = wpabuf_len(data->tls_out) - data->tls_out_pos; + wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " + "%lu bytes)", + (unsigned long) len, + (unsigned long) wpabuf_len(data->tls_out)); + + /* + * Limit outgoing message to the configured maximum size. Fragment + * message if needed. + */ + if (len > data->tls_out_limit) { + more_fragments = 1; + len = data->tls_out_limit; + wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments " + "will follow", (unsigned long) len); + } else + more_fragments = 0; + + length_included = data->tls_out_pos == 0 && + (wpabuf_len(data->tls_out) > data->tls_out_limit || + data->include_tls_length); + if (!length_included && + eap_type == EAP_TYPE_PEAP && peap_version == 0 && + !tls_connection_established(data->eap->ssl_ctx, data->conn)) { + /* + * Windows Server 2008 NPS really wants to have the TLS Message + * length included in phase 0 even for unfragmented frames or + * it will get very confused with Compound MAC calculation and + * Outer TLVs. + */ + length_included = 1; + } + + *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len, + EAP_CODE_RESPONSE, id); + if (*out_data == NULL) + return -1; + + flags = wpabuf_put(*out_data, 1); + *flags = peap_version; + if (more_fragments) + *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; + if (length_included) { + *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; + wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out)); + } + + wpabuf_put_data(*out_data, + wpabuf_head_u8(data->tls_out) + data->tls_out_pos, + len); + data->tls_out_pos += len; + + if (!more_fragments) + eap_peer_tls_reset_output(data); + + return ret; +} + + +/** + * eap_peer_tls_process_helper - Process TLS handshake message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * @id: EAP identifier for the response + * @in_data: Message received from the server + * @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 struct wpabuf *in_data, + struct wpabuf **out_data) +{ + int ret = 0; + + *out_data = NULL; + + if (data->tls_out && wpabuf_len(data->tls_out) > 0 && + wpabuf_len(in_data) > 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, out_data); + if (res) { + /* + * Input processing failed (res = -1) or more data is + * needed (res = 1). + */ + return res; + } + + /* + * The incoming message has been reassembled and processed. The + * response was allocated into data->tls_out buffer. + */ + } + + if (data->tls_out == NULL) { + /* + * No outgoing fragments remaining from the previous message + * and no new message generated. This indicates an error in TLS + * processing. + */ + eap_peer_tls_reset_output(data); + return -1; + } + + if (tls_connection_get_failed(data->ssl_ctx, data->conn)) { + /* TLS processing has failed - return error */ + wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " + "report error (len=%u)", + (unsigned int) wpabuf_len(data->tls_out)); + ret = -1; + /* TODO: clean pin if engine used? */ + if (wpabuf_len(data->tls_out) == 0) { + wpabuf_free(data->tls_out); + data->tls_out = NULL; + return -1; + } + } + + if (wpabuf_len(data->tls_out) == 0) { + /* + * TLS negotiation should now be complete since all other cases + * needing more data should have been caught above based on + * the TLS Message Length field. + */ + wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); + wpabuf_free(data->tls_out); + data->tls_out = NULL; + return 1; + } + + /* Send the pending message (in fragments, if needed). */ + return eap_tls_process_output(data, eap_type, peap_version, id, ret, + out_data); +} + + +/** + * eap_peer_tls_build_ack - Build a TLS ACK frame + * @id: EAP identifier for the response + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * Returns: Pointer to the allocated ACK frame or %NULL on failure + */ +struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, + int peap_version) +{ + struct wpabuf *resp; + + resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)", + (int) eap_type, id, peap_version); + wpabuf_put_u8(resp, peap_version); /* Flags */ + return resp; +} + + +/** + * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * Returns: 0 on success, -1 on failure + */ +int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) +{ + eap_peer_tls_reset_input(data); + eap_peer_tls_reset_output(data); + return tls_connection_shutdown(data->ssl_ctx, data->conn); +} + + +/** + * eap_peer_tls_status - Get TLS status + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + */ +int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, + char *buf, size_t buflen, int verbose) +{ + char version[20], name[128]; + int len = 0, ret; + + if (tls_get_version(data->ssl_ctx, data->conn, version, + sizeof(version)) < 0) + version[0] = '\0'; + if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) < 0) + name[0] = '\0'; + + ret = os_snprintf(buf + len, buflen - len, + "eap_tls_version=%s\n" + "EAP TLS cipher=%s\n" + "tls_session_reused=%d\n", + version, name, + tls_connection_resumed(data->ssl_ctx, data->conn)); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + return len; +} + + +/** + * eap_peer_tls_process_init - Initial validation/processing of EAP requests + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @ret: Return values from EAP request validation and processing + * @reqData: EAP request to be processed (eapReqData) + * @len: Buffer for returning length of the remaining payload + * @flags: Buffer for returning TLS flags + * Returns: Pointer to payload after TLS flags and length or %NULL on failure + * + * This function validates the EAP header and processes the optional TLS + * Message Length field. If this is the first fragment of a TLS message, the + * TLS reassembly code is initialized to receive the indicated number of bytes. + * + * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this + * function as the first step in processing received messages. They will need + * to process the flags (apart from Message Length Included) that are returned + * through the flags pointer and the message payload that will be returned (and + * the length is returned through the len pointer). Return values (ret) are set + * for continuation of EAP method processing. The caller is responsible for + * setting these to indicate completion (either success or failure) based on + * the authentication result. + */ +const u8 * eap_peer_tls_process_init(struct eap_sm *sm, + struct eap_ssl_data *data, + EapType eap_type, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + size_t *len, u8 *flags) +{ + const u8 *pos; + size_t left; + unsigned int tls_msg_len; + + if (tls_get_errors(data->ssl_ctx)) { + wpa_printf(MSG_INFO, "SSL: TLS errors detected"); + ret->ignore = TRUE; + return NULL; + } + + if (eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, reqData, + &left); + else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW, + EAP_VENDOR_WFA_UNAUTH_TLS, reqData, + &left); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, + &left); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + if (left == 0) { + wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags " + "octet included"); + if (!sm->workaround) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags " + "indicates ACK frame"); + *flags = 0; + } else { + *flags = *pos++; + left--; + } + wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) wpabuf_len(reqData), + *flags); + if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "SSL: Short frame with TLS " + "length"); + ret->ignore = TRUE; + return NULL; + } + tls_msg_len = WPA_GET_BE32(pos); + wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d", + tls_msg_len); + if (data->tls_in_left == 0) { + data->tls_in_total = tls_msg_len; + data->tls_in_left = tls_msg_len; + wpabuf_free(data->tls_in); + data->tls_in = NULL; + } + pos += 4; + left -= 4; + + if (left > tls_msg_len) { + wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d " + "bytes) smaller than this fragment (%d " + "bytes)", (int) tls_msg_len, (int) left); + ret->ignore = TRUE; + return NULL; + } + } + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + *len = left; + return pos; +} + + +/** + * eap_peer_tls_reset_input - Reset input buffers + * @data: Data for TLS processing + * + * This function frees any allocated memory for input buffers and resets input + * state. + */ +void eap_peer_tls_reset_input(struct eap_ssl_data *data) +{ + data->tls_in_left = data->tls_in_total = 0; + wpabuf_free(data->tls_in); + data->tls_in = NULL; +} + + +/** + * eap_peer_tls_reset_output - Reset output buffers + * @data: Data for TLS processing + * + * This function frees any allocated memory for output buffers and resets + * output state. + */ +void eap_peer_tls_reset_output(struct eap_ssl_data *data) +{ + data->tls_out_pos = 0; + wpabuf_free(data->tls_out); + data->tls_out = NULL; +} + + +/** + * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @in_data: Message received from the server + * @in_decrypted: Buffer for returning a pointer to the decrypted message + * Returns: 0 on success, 1 if more input data is needed, or -1 on failure + */ +int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, + const struct wpabuf *in_data, + struct wpabuf **in_decrypted) +{ + const struct wpabuf *msg; + int need_more_input; + + msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input); + if (msg == NULL) + return need_more_input ? 1 : -1; + + *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg); + eap_peer_tls_reset_input(data); + if (*in_decrypted == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data"); + return -1; + } + return 0; +} + + +/** + * eap_peer_tls_encrypt - Encrypt phase 2 TLS message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * @id: EAP identifier for the response + * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments + * @out_data: Buffer for returning a pointer to the encrypted response message + * Returns: 0 on success, -1 on failure + */ +int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, u8 id, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + if (in_data) { + eap_peer_tls_reset_output(data); + data->tls_out = tls_connection_encrypt(data->ssl_ctx, + data->conn, in_data); + if (data->tls_out == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 " + "data (in_len=%lu)", + (unsigned long) wpabuf_len(in_data)); + eap_peer_tls_reset_output(data); + return -1; + } + } + + return eap_tls_process_output(data, eap_type, peap_version, id, 0, + out_data); +} + + +/** + * eap_peer_select_phase2_methods - Select phase 2 EAP method + * @config: Pointer to the network configuration + * @prefix: 'phase2' configuration prefix, e.g., "auth=" + * @types: Buffer for returning allocated list of allowed EAP methods + * @num_types: Buffer for returning number of allocated EAP methods + * Returns: 0 on success, -1 on failure + * + * This function is used to parse EAP method list and select allowed methods + * for Phase2 authentication. + */ +int eap_peer_select_phase2_methods(struct eap_peer_config *config, + const char *prefix, + struct eap_method_type **types, + size_t *num_types) +{ + char *start, *pos, *buf; + struct eap_method_type *methods = NULL, *_methods; + u32 method; + size_t num_methods = 0, prefix_len; + + if (config == NULL || config->phase2 == NULL) + goto get_defaults; + + start = buf = os_strdup(config->phase2); + if (buf == NULL) + return -1; + + prefix_len = os_strlen(prefix); + + while (start && *start != '\0') { + int vendor; + pos = os_strstr(start, prefix); + if (pos == NULL) + break; + if (start != pos && *(pos - 1) != ' ') { + start = pos + prefix_len; + continue; + } + + start = pos + prefix_len; + pos = os_strchr(start, ' '); + if (pos) + *pos++ = '\0'; + method = eap_get_phase2_type(start, &vendor); + if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP " + "method '%s'", start); + } else { + num_methods++; + _methods = os_realloc_array(methods, num_methods, + sizeof(*methods)); + if (_methods == NULL) { + os_free(methods); + os_free(buf); + return -1; + } + methods = _methods; + methods[num_methods - 1].vendor = vendor; + methods[num_methods - 1].method = method; + } + + start = pos; + } + + os_free(buf); + +get_defaults: + if (methods == NULL) + methods = eap_get_phase2_types(config, &num_methods); + + if (methods == NULL) { + wpa_printf(MSG_ERROR, "TLS: No Phase2 EAP methods available"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types", + (u8 *) methods, + num_methods * sizeof(struct eap_method_type)); + + *types = methods; + *num_types = num_methods; + + return 0; +} + + +/** + * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2 + * @types: Buffer for returning allocated list of allowed EAP methods + * @num_types: Buffer for returning number of allocated EAP methods + * @hdr: EAP-Request header (and the following EAP type octet) + * @resp: Buffer for returning the EAP-Nak message + * Returns: 0 on success, -1 on failure + */ +int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types, + struct eap_hdr *hdr, struct wpabuf **resp) +{ + u8 *pos = (u8 *) (hdr + 1); + size_t i; + + /* TODO: add support for expanded Nak */ + wpa_printf(MSG_DEBUG, "TLS: Phase 2 Request: Nak type=%d", *pos); + wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types", + (u8 *) types, num_types * sizeof(struct eap_method_type)); + *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types, + EAP_CODE_RESPONSE, hdr->identifier); + if (*resp == NULL) + return -1; + + for (i = 0; i < num_types; i++) { + if (types[i].vendor == EAP_VENDOR_IETF && + types[i].method < 256) + wpabuf_put_u8(*resp, types[i].method); + } + + eap_update_len(*resp); + + return 0; +} diff --git a/freebsd/contrib/wpa/src/eap_peer/eap_tls_common.h b/freebsd/contrib/wpa/src/eap_peer/eap_tls_common.h new file mode 100644 index 00000000..acd2b783 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_tls_common.h @@ -0,0 +1,132 @@ +/* + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2009, 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TLS_COMMON_H +#define EAP_TLS_COMMON_H + +/** + * struct eap_ssl_data - TLS data for EAP methods + */ +struct eap_ssl_data { + /** + * conn - TLS connection context data from tls_connection_init() + */ + struct tls_connection *conn; + + /** + * tls_out - TLS message to be sent out in fragments + */ + struct wpabuf *tls_out; + + /** + * tls_out_pos - The current position in the outgoing TLS message + */ + size_t tls_out_pos; + + /** + * tls_out_limit - Maximum fragment size for outgoing TLS messages + */ + size_t tls_out_limit; + + /** + * tls_in - Received TLS message buffer for re-assembly + */ + struct wpabuf *tls_in; + + /** + * tls_in_left - Number of remaining bytes in the incoming TLS message + */ + size_t tls_in_left; + + /** + * tls_in_total - Total number of bytes in the incoming TLS message + */ + size_t tls_in_total; + + /** + * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel) + */ + int phase2; + + /** + * include_tls_length - Whether the TLS length field is included even + * if the TLS data is not fragmented + */ + int include_tls_length; + + /** + * eap - EAP state machine allocated with eap_peer_sm_init() + */ + struct eap_sm *eap; + + /** + * ssl_ctx - TLS library context to use for the connection + */ + void *ssl_ctx; + + /** + * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + */ + u8 eap_type; +}; + + +/* EAP TLS Flags */ +#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TLS_FLAGS_START 0x20 +#define EAP_TLS_VERSION_MASK 0x07 + + /* could be up to 128 bytes, but only the first 64 bytes are used */ +#define EAP_TLS_KEY_LEN 64 + +/* dummy type used as a flag for UNAUTH-TLS */ +#define EAP_UNAUTH_TLS_TYPE 255 +#define EAP_WFA_UNAUTH_TLS_TYPE 254 + + +int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + struct eap_peer_config *config, u8 eap_type); +void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); +u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + const char *label, size_t len); +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len); +int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, + u8 id, const struct wpabuf *in_data, + 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/freebsd/contrib/wpa/src/eap_peer/eap_ttls.c b/freebsd/contrib/wpa/src/eap_peer/eap_ttls.c new file mode 100644 index 00000000..55788774 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/eap_ttls.c @@ -0,0 +1,1723 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAP peer method: EAP-TTLS (RFC 5281) + * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/ms_funcs.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" +#include "eap_common/chap.h" +#include "eap_common/eap_ttls.h" +#include "mschapv2.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" + + +#define EAP_TTLS_VERSION 0 + + +static void eap_ttls_deinit(struct eap_sm *sm, void *priv); + + +struct eap_ttls_data { + struct eap_ssl_data ssl; + + int ttls_version; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + int phase2_start; + + enum phase2_types { + EAP_TTLS_PHASE2_EAP, + EAP_TTLS_PHASE2_MSCHAPV2, + EAP_TTLS_PHASE2_MSCHAP, + EAP_TTLS_PHASE2_PAP, + EAP_TTLS_PHASE2_CHAP + } phase2_type; + struct eap_method_type phase2_eap_type; + struct eap_method_type *phase2_eap_types; + size_t num_phase2_eap_types; + + u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; + int auth_response_valid; + u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */ + u8 ident; + int resuming; /* starting a resumed session */ + int reauth; /* reauthentication */ + u8 *key_data; + u8 *session_id; + size_t id_len; + + struct wpabuf *pending_phase2_req; + +#ifdef EAP_TNC + int ready_for_tnc; + int tnc_started; +#endif /* EAP_TNC */ +}; + + +static void * eap_ttls_init(struct eap_sm *sm) +{ + struct eap_ttls_data *data; + struct eap_peer_config *config = eap_get_config(sm); + char *selected; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->ttls_version = EAP_TTLS_VERSION; + selected = "EAP"; + data->phase2_type = EAP_TTLS_PHASE2_EAP; + + if (config && config->phase2) { + if (os_strstr(config->phase2, "autheap=")) { + selected = "EAP"; + data->phase2_type = EAP_TTLS_PHASE2_EAP; + } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) { + selected = "MSCHAPV2"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; + } else if (os_strstr(config->phase2, "auth=MSCHAP")) { + selected = "MSCHAP"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; + } else if (os_strstr(config->phase2, "auth=PAP")) { + selected = "PAP"; + data->phase2_type = EAP_TTLS_PHASE2_PAP; + } else if (os_strstr(config->phase2, "auth=CHAP")) { + selected = "CHAP"; + data->phase2_type = EAP_TTLS_PHASE2_CHAP; + } + } + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); + + if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { + if (eap_peer_select_phase2_methods(config, "autheap=", + &data->phase2_eap_types, + &data->num_phase2_eap_types) + < 0) { + eap_ttls_deinit(sm, data); + return NULL; + } + + data->phase2_eap_type.vendor = EAP_VENDOR_IETF; + data->phase2_eap_type.method = EAP_TYPE_NONE; + } + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); + eap_ttls_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm, + struct eap_ttls_data *data) +{ + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->deinit(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + } +} + + +static void eap_ttls_free_key(struct eap_ttls_data *data) +{ + if (data->key_data) { + bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + data->key_data = NULL; + } +} + + +static void eap_ttls_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + if (data == NULL) + return; + eap_ttls_phase2_eap_deinit(sm, data); + os_free(data->phase2_eap_types); + eap_peer_tls_ssl_deinit(sm, &data->ssl); + eap_ttls_free_key(data); + os_free(data->session_id); + wpabuf_free(data->pending_phase2_req); + os_free(data); +} + + +static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, + int mandatory, size_t len) +{ + struct ttls_avp_vendor *avp; + u8 flags; + size_t hdrlen; + + avp = (struct ttls_avp_vendor *) avphdr; + flags = mandatory ? AVP_FLAGS_MANDATORY : 0; + if (vendor_id) { + flags |= AVP_FLAGS_VENDOR; + hdrlen = sizeof(*avp); + avp->vendor_id = host_to_be32(vendor_id); + } else { + hdrlen = sizeof(struct ttls_avp); + } + + avp->avp_code = host_to_be32(avp_code); + avp->avp_length = host_to_be32(((u32) flags << 24) | + (u32) (hdrlen + len)); + + return avphdr + hdrlen; +} + + +static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code, + u32 vendor_id, int mandatory, + const u8 *data, size_t len) +{ + u8 *pos; + pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len); + os_memcpy(pos, data, len); + pos += len; + AVP_PAD(start, pos); + return pos; +} + + +static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, + int mandatory) +{ + struct wpabuf *msg; + u8 *avp, *pos; + + msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4); + if (msg == NULL) { + wpabuf_free(*resp); + *resp = NULL; + return -1; + } + + avp = wpabuf_mhead(msg); + pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp)); + os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp)); + pos += wpabuf_len(*resp); + AVP_PAD(avp, pos); + wpabuf_free(*resp); + wpabuf_put(msg, pos - avp); + *resp = msg; + return 0; +} + + +static int eap_ttls_v0_derive_key(struct eap_sm *sm, + struct eap_ttls_data *data) +{ + eap_ttls_free_key(data); + data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN + + EAP_EMSK_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); + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived EMSK", + data->key_data + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); + + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_TTLS, + &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id"); + } + + return 0; +} + + +#ifndef CONFIG_FIPS +static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, + struct eap_ttls_data *data, size_t len) +{ + return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); +} +#endif /* CONFIG_FIPS */ + + +static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data, + u8 method) +{ + size_t i; + for (i = 0; i < data->num_phase2_eap_types; i++) { + if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF || + data->phase2_eap_types[i].method != method) + continue; + + data->phase2_eap_type.vendor = + data->phase2_eap_types[i].vendor; + data->phase2_eap_type.method = + data->phase2_eap_types[i].method; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " + "Phase 2 EAP vendor %d method %d", + data->phase2_eap_type.vendor, + data->phase2_eap_type.method); + break; + } +} + + +static int eap_ttls_phase2_eap_process(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, size_t len, + struct wpabuf **resp) +{ + struct wpabuf msg; + struct eap_method_ret iret; + + os_memset(&iret, 0, sizeof(iret)); + wpabuf_set(&msg, hdr, len); + *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, + &msg); + if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC || + iret.decision == DECISION_FAIL)) { + ret->methodState = iret.methodState; + ret->decision = iret.decision; + } + + return 0; +} + + +static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, size_t len, + u8 method, struct wpabuf **resp) +{ +#ifdef EAP_TNC + if (data->tnc_started && data->phase2_method && + data->phase2_priv && method == EAP_TYPE_TNC && + data->phase2_eap_type.method == EAP_TYPE_TNC) + return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, + resp); + + if (data->ready_for_tnc && !data->tnc_started && + method == EAP_TYPE_TNC) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " + "EAP method"); + data->tnc_started = 1; + } + + if (data->tnc_started) { + if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF || + data->phase2_eap_type.method == EAP_TYPE_TNC) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP " + "type %d for TNC", method); + return -1; + } + + data->phase2_eap_type.vendor = EAP_VENDOR_IETF; + data->phase2_eap_type.method = method; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " + "Phase 2 EAP vendor %d method %d (TNC)", + data->phase2_eap_type.vendor, + data->phase2_eap_type.method); + + if (data->phase2_type == EAP_TTLS_PHASE2_EAP) + eap_ttls_phase2_eap_deinit(sm, data); + } +#endif /* EAP_TNC */ + + if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF && + data->phase2_eap_type.method == EAP_TYPE_NONE) + eap_ttls_phase2_select_eap_method(data, method); + + if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE) + { + if (eap_peer_tls_phase2_nak(data->phase2_eap_types, + data->num_phase2_eap_types, + hdr, resp)) + return -1; + return 0; + } + + if (data->phase2_priv == NULL) { + data->phase2_method = eap_peer_get_eap_method( + EAP_VENDOR_IETF, method); + if (data->phase2_method) { + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + } + } + if (data->phase2_priv == NULL || data->phase2_method == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize " + "Phase 2 EAP method %d", method); + return -1; + } + + return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp); +} + + +static int eap_ttls_phase2_request_eap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, + struct wpabuf **resp) +{ + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_peer_config *config = eap_get_config(sm); + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-TTLS: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos); + switch (*pos) { + case EAP_TYPE_IDENTITY: + *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); + break; + default: + if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len, + *pos, resp) < 0) + return -1; + break; + } + + if (*resp == NULL && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp)) { + return 0; + } + + if (*resp == NULL) + return -1; + + wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response", + *resp); + return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1); +} + + +static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct wpabuf **resp) +{ +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPV2 not supported in FIPS build"); + return -1; +#else /* CONFIG_FIPS */ +#ifdef EAP_MSCHAPv2 + struct wpabuf *msg; + u8 *buf, *pos, *challenge, *peer_challenge; + const u8 *identity, *password; + size_t identity_len, password_len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (identity == NULL || password == NULL) + return -1; + + msg = wpabuf_alloc(identity_len + 1000); + if (msg == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/MSCHAPV2: Failed to allocate memory"); + return -1; + } + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* MS-CHAP-Challenge */ + challenge = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); + if (challenge == NULL) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " + "implicit challenge"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + + /* MS-CHAP2-Response */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + EAP_TTLS_MSCHAPV2_RESPONSE_LEN); + data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; + *pos++ = data->ident; + *pos++ = 0; /* Flags */ + if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) { + os_free(challenge); + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get " + "random data for peer challenge"); + return -1; + } + peer_challenge = pos; + pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; + os_memset(pos, 0, 8); /* Reserved, must be zero */ + pos += 8; + if (mschapv2_derive_response(identity, identity_len, password, + password_len, pwhash, challenge, + peer_challenge, pos, data->auth_response, + data->master_key)) { + os_free(challenge); + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " + "response"); + return -1; + } + data->auth_response_valid = 1; + + pos += 24; + os_free(challenge); + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + return 0; +#else /* EAP_MSCHAPv2 */ + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); + return -1; +#endif /* EAP_MSCHAPv2 */ +#endif /* CONFIG_FIPS */ +} + + +static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct wpabuf **resp) +{ +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAP not supported in FIPS build"); + return -1; +#else /* CONFIG_FIPS */ + struct wpabuf *msg; + u8 *buf, *pos, *challenge; + const u8 *identity, *password; + size_t identity_len, password_len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (identity == NULL || password == NULL) + return -1; + + msg = wpabuf_alloc(identity_len + 1000); + if (msg == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/MSCHAP: Failed to allocate memory"); + return -1; + } + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* MS-CHAP-Challenge */ + challenge = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); + if (challenge == NULL) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " + "implicit challenge"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); + + /* MS-CHAP-Response */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + EAP_TTLS_MSCHAP_RESPONSE_LEN); + data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN]; + *pos++ = data->ident; + *pos++ = 1; /* Flags: Use NT style passwords */ + os_memset(pos, 0, 24); /* LM-Response */ + pos += 24; + if (pwhash) { + challenge_response(challenge, password, pos); /* NT-Response */ + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash", + password, 16); + } else { + nt_challenge_response(challenge, password, password_len, + pos); /* NT-Response */ + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password", + password, password_len); + } + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge", + challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24); + pos += 24; + os_free(challenge); + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + /* EAP-TTLS/MSCHAP does not provide tunneled success + * notification, so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + + return 0; +#endif /* CONFIG_FIPS */ +} + + +static int eap_ttls_phase2_request_pap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct wpabuf **resp) +{ + struct wpabuf *msg; + u8 *buf, *pos; + size_t pad; + const u8 *identity, *password; + size_t identity_len, password_len; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password(sm, &password_len); + if (identity == NULL || password == NULL) + return -1; + + msg = wpabuf_alloc(identity_len + password_len + 100); + if (msg == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/PAP: Failed to allocate memory"); + return -1; + } + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts + * the data, so no separate encryption is used in the AVP itself. + * However, the password is padded to obfuscate its length. */ + pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15; + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1, + password_len + pad); + os_memcpy(pos, password, password_len); + pos += password_len; + os_memset(pos, 0, pad); + pos += pad; + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + /* EAP-TTLS/PAP does not provide tunneled success notification, + * so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + + return 0; +} + + +static int eap_ttls_phase2_request_chap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct wpabuf **resp) +{ +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "EAP-TTLS: CHAP not supported in FIPS build"); + return -1; +#else /* CONFIG_FIPS */ + struct wpabuf *msg; + u8 *buf, *pos, *challenge; + const u8 *identity, *password; + size_t identity_len, password_len; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password(sm, &password_len); + if (identity == NULL || password == NULL) + return -1; + + msg = wpabuf_alloc(identity_len + 1000); + if (msg == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/CHAP: Failed to allocate memory"); + return -1; + } + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* CHAP-Challenge */ + challenge = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); + if (challenge == NULL) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " + "implicit challenge"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1, + challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); + + /* CHAP-Password */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1, + 1 + EAP_TTLS_CHAP_PASSWORD_LEN); + data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN]; + *pos++ = data->ident; + + /* MD5(Ident + Password + Challenge) */ + chap_md5(data->ident, password, password_len, challenge, + EAP_TTLS_CHAP_CHALLENGE_LEN, pos); + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username", + identity, identity_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password", + password, password_len); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge", + challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password", + pos, EAP_TTLS_CHAP_PASSWORD_LEN); + pos += EAP_TTLS_CHAP_PASSWORD_LEN; + os_free(challenge); + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + /* EAP-TTLS/CHAP does not provide tunneled success + * notification, so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + + return 0; +#endif /* CONFIG_FIPS */ +} + + +static int eap_ttls_phase2_request(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, + struct wpabuf **resp) +{ + int res = 0; + size_t len; + enum phase2_types phase2_type = data->phase2_type; + +#ifdef EAP_TNC + if (data->tnc_started) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC"); + phase2_type = EAP_TTLS_PHASE2_EAP; + } +#endif /* EAP_TNC */ + + if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 || + phase2_type == EAP_TTLS_PHASE2_MSCHAP || + phase2_type == EAP_TTLS_PHASE2_PAP || + phase2_type == EAP_TTLS_PHASE2_CHAP) { + if (eap_get_config_identity(sm, &len) == NULL) { + wpa_printf(MSG_INFO, + "EAP-TTLS: Identity not configured"); + eap_sm_request_identity(sm); + if (eap_get_config_password(sm, &len) == NULL) + eap_sm_request_password(sm); + return 0; + } + + if (eap_get_config_password(sm, &len) == NULL) { + wpa_printf(MSG_INFO, + "EAP-TTLS: Password not configured"); + eap_sm_request_password(sm); + return 0; + } + } + + switch (phase2_type) { + case EAP_TTLS_PHASE2_EAP: + res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp); + break; + case EAP_TTLS_PHASE2_MSCHAPV2: + res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp); + break; + case EAP_TTLS_PHASE2_MSCHAP: + res = eap_ttls_phase2_request_mschap(sm, data, ret, resp); + break; + case EAP_TTLS_PHASE2_PAP: + res = eap_ttls_phase2_request_pap(sm, data, ret, resp); + break; + case EAP_TTLS_PHASE2_CHAP: + res = eap_ttls_phase2_request_chap(sm, data, ret, resp); + break; + default: + wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown"); + res = -1; + break; + } + + if (res < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return res; +} + + +struct ttls_parse_avp { + u8 *mschapv2; + u8 *eapdata; + size_t eap_len; + int mschapv2_error; +}; + + +static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen, + struct ttls_parse_avp *parse) +{ + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); + if (parse->eapdata == NULL) { + parse->eapdata = os_malloc(dlen); + if (parse->eapdata == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " + "memory for Phase 2 EAP data"); + return -1; + } + os_memcpy(parse->eapdata, dpos, dlen); + parse->eap_len = dlen; + } else { + u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen); + if (neweap == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " + "memory for Phase 2 EAP data"); + return -1; + } + os_memcpy(neweap + parse->eap_len, dpos, dlen); + parse->eapdata = neweap; + parse->eap_len += dlen; + } + + return 0; +} + + +static int eap_ttls_parse_avp(u8 *pos, size_t left, + struct ttls_parse_avp *parse) +{ + struct ttls_avp *avp; + u32 avp_code, avp_length, vendor_id = 0; + u8 avp_flags, *dpos; + size_t dlen; + + avp = (struct ttls_avp *) pos; + avp_code = be_to_host32(avp->avp_code); + avp_length = be_to_host32(avp->avp_length); + avp_flags = (avp_length >> 24) & 0xff; + avp_length &= 0xffffff; + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " + "length=%d", (int) avp_code, avp_flags, + (int) avp_length); + + if (avp_length > left) { + wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " + "(len=%d, left=%lu) - dropped", + (int) avp_length, (unsigned long) left); + return -1; + } + + if (avp_length < sizeof(*avp)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d", + avp_length); + return -1; + } + + dpos = (u8 *) (avp + 1); + dlen = avp_length - sizeof(*avp); + if (avp_flags & AVP_FLAGS_VENDOR) { + if (dlen < 4) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP " + "underflow"); + return -1; + } + vendor_id = WPA_GET_BE32(dpos); + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", + (int) vendor_id); + dpos += 4; + dlen -= 4; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); + + if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { + if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0) + return -1; + } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) { + /* This is an optional message that can be displayed to + * the user. */ + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message", + dpos, dlen); + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success", + dpos, dlen); + if (dlen != 43) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected " + "MS-CHAP2-Success length " + "(len=%lu, expected 43)", + (unsigned long) dlen); + return -1; + } + parse->mschapv2 = dpos; + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP_ERROR) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error", + dpos, dlen); + parse->mschapv2_error = 1; + } else if (avp_flags & AVP_FLAGS_MANDATORY) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP " + "code %d vendor_id %d - dropped", + (int) avp_code, (int) vendor_id); + return -1; + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP " + "code %d vendor_id %d", + (int) avp_code, (int) vendor_id); + } + + return avp_length; +} + + +static int eap_ttls_parse_avps(struct wpabuf *in_decrypted, + struct ttls_parse_avp *parse) +{ + u8 *pos; + size_t left, pad; + int avp_length; + + pos = wpabuf_mhead(in_decrypted); + left = wpabuf_len(in_decrypted); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left); + if (left < sizeof(struct ttls_avp)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame" + " len=%lu expected %lu or more - dropped", + (unsigned long) left, + (unsigned long) sizeof(struct ttls_avp)); + return -1; + } + + /* Parse AVPs */ + os_memset(parse, 0, sizeof(*parse)); + + while (left > 0) { + avp_length = eap_ttls_parse_avp(pos, left, parse); + if (avp_length < 0) + return -1; + + pad = (4 - (avp_length & 3)) & 3; + pos += avp_length + pad; + if (left < avp_length + pad) + left = 0; + else + left -= avp_length + pad; + } + + return 0; +} + + +static u8 * eap_ttls_fake_identity_request(void) +{ + struct eap_hdr *hdr; + u8 *buf; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of " + "Phase 2 - use fake EAP-Request Identity"); + buf = os_malloc(sizeof(*hdr) + 1); + if (buf == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate " + "memory for fake EAP-Identity Request"); + return NULL; + } + + hdr = (struct eap_hdr *) buf; + hdr->code = EAP_CODE_REQUEST; + hdr->identifier = 0; + hdr->length = host_to_be16(sizeof(*hdr) + 1); + buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY; + + return buf; +} + + +static int eap_ttls_encrypt_response(struct eap_sm *sm, + struct eap_ttls_data *data, + struct wpabuf *resp, u8 identifier, + struct wpabuf **out_data) +{ + if (resp == NULL) + return 0; + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data", + resp); + if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, identifier, + resp, out_data)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 " + "frame"); + wpabuf_free(resp); + return -1; + } + wpabuf_free(resp); + + return 0; +} + + +static int eap_ttls_process_phase2_eap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct ttls_parse_avp *parse, + struct wpabuf **resp) +{ + struct eap_hdr *hdr; + size_t len; + + if (parse->eapdata == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the " + "packet - dropped"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP", + parse->eapdata, parse->eap_len); + hdr = (struct eap_hdr *) parse->eapdata; + + if (parse->eap_len < sizeof(*hdr)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP " + "frame (len=%lu, expected %lu or more) - dropped", + (unsigned long) parse->eap_len, + (unsigned long) sizeof(*hdr)); + return -1; + } + len = be_to_host16(hdr->length); + if (len > parse->eap_len) { + wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 " + "EAP frame (EAP hdr len=%lu, EAP data len in " + "AVP=%lu)", + (unsigned long) len, + (unsigned long) parse->eap_len); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d " + "identifier=%d length=%lu", + hdr->code, hdr->identifier, (unsigned long) len); + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " + "processing failed"); + return -1; + } + break; + default: + wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + return -1; + } + + return 0; +} + + +static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct ttls_parse_avp *parse) +{ +#ifdef EAP_MSCHAPv2 + if (parse->mschapv2_error) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " + "MS-CHAP-Error - failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + /* Reply with empty data to ACK error */ + return 1; + } + + if (parse->mschapv2 == NULL) { +#ifdef EAP_TNC + if (data->phase2_success && parse->eapdata) { + /* + * Allow EAP-TNC to be started after successfully + * completed MSCHAPV2. + */ + return 1; + } +#endif /* EAP_TNC */ + wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP " + "received for Phase2 MSCHAPV2"); + return -1; + } + if (parse->mschapv2[0] != data->ident) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 " + "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)", + parse->mschapv2[0], data->ident); + return -1; + } + if (!data->auth_response_valid || + mschapv2_verify_auth_response(data->auth_response, + parse->mschapv2 + 1, 42)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator " + "response in Phase 2 MSCHAPV2 success request"); + return -1; + } + + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 " + "authentication succeeded"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + data->phase2_success = 1; + + /* + * Reply with empty data; authentication server will reply + * with EAP-Success after this. + */ + return 1; +#else /* EAP_MSCHAPv2 */ + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); + return -1; +#endif /* EAP_MSCHAPv2 */ +} + + +#ifdef EAP_TNC +static int eap_ttls_process_tnc_start(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct ttls_parse_avp *parse, + struct wpabuf **resp) +{ + /* TNC uses inner EAP method after non-EAP TTLS phase 2. */ + if (parse->eapdata == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " + "unexpected tunneled data (no EAP)"); + return -1; + } + + if (!data->ready_for_tnc) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " + "EAP after non-EAP, but not ready for TNC"); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " + "non-EAP method"); + data->tnc_started = 1; + + if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0) + return -1; + + return 0; +} +#endif /* EAP_TNC */ + + +static int eap_ttls_process_decrypted(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + u8 identifier, + struct ttls_parse_avp *parse, + struct wpabuf *in_decrypted, + struct wpabuf **out_data) +{ + struct wpabuf *resp = NULL; + struct eap_peer_config *config = eap_get_config(sm); + int res; + enum phase2_types phase2_type = data->phase2_type; + +#ifdef EAP_TNC + if (data->tnc_started) + phase2_type = EAP_TTLS_PHASE2_EAP; +#endif /* EAP_TNC */ + + switch (phase2_type) { + case EAP_TTLS_PHASE2_EAP: + if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) < + 0) + return -1; + break; + case EAP_TTLS_PHASE2_MSCHAPV2: + res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse); +#ifdef EAP_TNC + if (res == 1 && parse->eapdata && data->phase2_success) { + /* + * TNC may be required as the next + * authentication method within the tunnel. + */ + ret->methodState = METHOD_MAY_CONT; + data->ready_for_tnc = 1; + if (eap_ttls_process_tnc_start(sm, data, ret, parse, + &resp) == 0) + break; + } +#endif /* EAP_TNC */ + return res; + case EAP_TTLS_PHASE2_MSCHAP: + case EAP_TTLS_PHASE2_PAP: + case EAP_TTLS_PHASE2_CHAP: +#ifdef EAP_TNC + if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) < + 0) + return -1; + break; +#else /* EAP_TNC */ + /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled + * requests to the supplicant */ + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected " + "tunneled data"); + return -1; +#endif /* EAP_TNC */ + } + + if (resp) { + if (eap_ttls_encrypt_response(sm, data, resp, identifier, + out_data) < 0) + return -1; + } else if (config->pending_req_identity || + config->pending_req_password || + config->pending_req_otp || + config->pending_req_new_password) { + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_dup(in_decrypted); + } + + return 0; +} + + +static int eap_ttls_implicit_identity_request(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + u8 identifier, + struct wpabuf **out_data) +{ + int retval = 0; + struct eap_hdr *hdr; + struct wpabuf *resp; + + hdr = (struct eap_hdr *) eap_ttls_fake_identity_request(); + if (hdr == NULL) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + + resp = NULL; + if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " + "processing failed"); + retval = -1; + } else { + struct eap_peer_config *config = eap_get_config(sm); + if (resp == NULL && + (config->pending_req_identity || + config->pending_req_password || + config->pending_req_otp || + config->pending_req_new_password)) { + /* + * Use empty buffer to force implicit request + * processing when EAP request is re-processed after + * user input. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_alloc(0); + } + + retval = eap_ttls_encrypt_response(sm, data, resp, identifier, + out_data); + } + + os_free(hdr); + + if (retval < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return retval; +} + + +static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data, + struct eap_method_ret *ret, u8 identifier, + struct wpabuf **out_data) +{ + data->phase2_start = 0; + + /* + * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only + * if TLS part was indeed resuming a previous session. Most + * Authentication Servers terminate EAP-TTLS before reaching this + * point, but some do not. Make wpa_supplicant stop phase 2 here, if + * needed. + */ + if (data->reauth && + tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - " + "skip phase 2"); + *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS, + data->ttls_version); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + data->phase2_success = 1; + return 0; + } + + return eap_ttls_implicit_identity_request(sm, data, ret, identifier, + out_data); +} + + +static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, + struct eap_method_ret *ret, u8 identifier, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + struct wpabuf *in_decrypted = NULL; + int retval = 0; + struct ttls_parse_avp parse; + + os_memset(&parse, 0, sizeof(parse)); + + wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" + " Phase 2", + in_data ? (unsigned long) wpabuf_len(in_data) : 0); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - " + "skip decryption and use old data"); + /* Clear TLS reassembly state. */ + eap_peer_tls_reset_input(&data->ssl); + + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + if (wpabuf_len(in_decrypted) == 0) { + wpabuf_free(in_decrypted); + return eap_ttls_implicit_identity_request( + sm, data, ret, identifier, out_data); + } + goto continue_req; + } + + if ((in_data == NULL || wpabuf_len(in_data) == 0) && + data->phase2_start) { + return eap_ttls_phase2_start(sm, data, ret, identifier, + out_data); + } + + if (in_data == NULL || wpabuf_len(in_data) == 0) { + /* Received TLS ACK - requesting more fragments */ + return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, + identifier, NULL, out_data); + } + + retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); + if (retval) + goto done; + +continue_req: + data->phase2_start = 0; + + if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) { + retval = -1; + goto done; + } + + retval = eap_ttls_process_decrypted(sm, data, ret, identifier, + &parse, in_decrypted, out_data); + +done: + wpabuf_free(in_decrypted); + os_free(parse.eapdata); + + if (retval < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return retval; +} + + +static int eap_ttls_process_handshake(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + u8 identifier, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + int res; + + res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, identifier, + in_data, out_data); + if (res < 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS processing failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " + "Phase 2"); + if (data->resuming) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may " + "skip Phase 2"); + ret->decision = DECISION_COND_SUCC; + ret->methodState = METHOD_MAY_CONT; + } + data->phase2_start = 1; + eap_ttls_v0_derive_key(sm, data); + + if (*out_data == NULL || wpabuf_len(*out_data) == 0) { + if (eap_ttls_decrypt(sm, data, ret, identifier, + NULL, out_data)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: " + "failed to process early " + "start for Phase 2"); + } + res = 0; + } + data->resuming = 0; + } + + if (res == 2) { + /* + * Application data included in the handshake message. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = *out_data; + *out_data = NULL; + res = eap_ttls_decrypt(sm, data, ret, identifier, in_data, + out_data); + } + + return res; +} + + +static void eap_ttls_check_auth_status(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret) +{ + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + if (ret->decision == DECISION_UNCOND_SUCC || + ret->decision == DECISION_COND_SUCC) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " + "completed successfully"); + data->phase2_success = 1; +#ifdef EAP_TNC + if (!data->ready_for_tnc && !data->tnc_started) { + /* + * TNC may be required as the next + * authentication method within the tunnel. + */ + ret->methodState = METHOD_MAY_CONT; + data->ready_for_tnc = 1; + } +#endif /* EAP_TNC */ + } + } else if (ret->methodState == METHOD_MAY_CONT && + (ret->decision == DECISION_UNCOND_SUCC || + ret->decision == DECISION_COND_SUCC)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " + "completed successfully (MAY_CONT)"); + data->phase2_success = 1; + } +} + + +static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + size_t left; + int res; + u8 flags, id; + struct wpabuf *resp; + const u8 *pos; + struct eap_ttls_data *data = priv; + struct wpabuf msg; + + pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret, + reqData, &left, &flags); + if (pos == NULL) + return NULL; + id = eap_get_id(reqData); + + if (flags & EAP_TLS_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own " + "ver=%d)", flags & EAP_TLS_VERSION_MASK, + data->ttls_version); + + /* RFC 5281, Ch. 9.2: + * "This packet MAY contain additional information in the form + * of AVPs, which may provide useful hints to the client" + * For now, ignore any potential extra data. + */ + left = 0; + } + + wpabuf_set(&msg, pos, left); + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp); + } else { + res = eap_ttls_process_handshake(sm, data, ret, id, + &msg, &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; + eap_ttls_free_key(data); + os_free(data->session_id); + data->session_id = NULL; + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + if (data->phase2_priv && data->phase2_method && + data->phase2_method->init_for_reauth) + data->phase2_method->init_for_reauth(sm, data->phase2_priv); + data->phase2_start = 0; + data->phase2_success = 0; + data->resuming = 1; + data->reauth = 1; + return priv; +} + + +static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_ttls_data *data = priv; + int len, ret; + + len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); + ret = os_snprintf(buf + len, buflen - len, + "EAP-TTLSv%d Phase2 method=", + data->ttls_version); + if (os_snprintf_error(buflen - len, ret)) + 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 (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + return len; +} + + +static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + return data->key_data != NULL && data->phase2_success; +} + + +static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *key; + + if (data->key_data == NULL || !data->phase2_success) + return NULL; + + key = os_malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *id; + + if (data->session_id == NULL || !data->phase2_success) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + +static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_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_ttls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); + if (eap == NULL) + return -1; + + eap->init = eap_ttls_init; + eap->deinit = eap_ttls_deinit; + eap->process = eap_ttls_process; + eap->isKeyAvailable = eap_ttls_isKeyAvailable; + eap->getKey = eap_ttls_getKey; + eap->getSessionId = eap_ttls_get_session_id; + eap->get_status = eap_ttls_get_status; + eap->has_reauth_data = eap_ttls_has_reauth_data; + eap->deinit_for_reauth = eap_ttls_deinit_for_reauth; + eap->init_for_reauth = eap_ttls_init_for_reauth; + eap->get_emsk = eap_ttls_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/freebsd/contrib/wpa/src/eap_peer/mschapv2.c b/freebsd/contrib/wpa/src/eap_peer/mschapv2.c new file mode 100644 index 00000000..ba5d763c --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/mschapv2.c @@ -0,0 +1,126 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * MSCHAPV2 (RFC 2759) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/ms_funcs.h" +#include "mschapv2.h" + +const u8 * mschapv2_remove_domain(const u8 *username, size_t *len) +{ + size_t i; + + /* + * MSCHAPv2 does not include optional domain name in the + * challenge-response calculation, so remove domain prefix + * (if present). + */ + + for (i = 0; i < *len; i++) { + if (username[i] == '\\') { + *len -= i + 1; + return username + i + 1; + } + } + + return username; +} + + +int mschapv2_derive_response(const u8 *identity, size_t identity_len, + const u8 *password, size_t password_len, + int pwhash, + const u8 *auth_challenge, + const u8 *peer_challenge, + u8 *nt_response, u8 *auth_response, + u8 *master_key) +{ + const u8 *username; + size_t username_len; + u8 password_hash[16], password_hash_hash[16]; + + wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity", + identity, identity_len); + username_len = identity_len; + username = mschapv2_remove_domain(identity, &username_len); + wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username", + username, username_len); + + wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge", + auth_challenge, MSCHAPV2_CHAL_LEN); + wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge", + peer_challenge, MSCHAPV2_CHAL_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username", + username, username_len); + /* Authenticator response is not really needed yet, but calculate it + * here so that challenges need not be saved. */ + if (pwhash) { + wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash", + password, password_len); + if (generate_nt_response_pwhash(auth_challenge, peer_challenge, + username, username_len, + password, nt_response) || + generate_authenticator_response_pwhash( + password, peer_challenge, auth_challenge, + username, username_len, nt_response, + auth_response)) + return -1; + } else { + wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password", + password, password_len); + if (generate_nt_response(auth_challenge, peer_challenge, + username, username_len, + password, password_len, + nt_response) || + generate_authenticator_response(password, password_len, + peer_challenge, + auth_challenge, + username, username_len, + nt_response, + auth_response)) + return -1; + } + wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response", + nt_response, MSCHAPV2_NT_RESPONSE_LEN); + wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response", + auth_response, MSCHAPV2_AUTH_RESPONSE_LEN); + + /* Generate master_key here since we have the needed data available. */ + if (pwhash) { + if (hash_nt_password_hash(password, password_hash_hash)) + return -1; + } else { + if (nt_password_hash(password, password_len, password_hash) || + hash_nt_password_hash(password_hash, password_hash_hash)) + return -1; + } + if (get_master_key(password_hash_hash, nt_response, master_key)) + return -1; + wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key", + master_key, MSCHAPV2_MASTER_KEY_LEN); + + return 0; +} + + +int mschapv2_verify_auth_response(const u8 *auth_response, + const u8 *buf, size_t buf_len) +{ + u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN]; + if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN || + buf[0] != 'S' || buf[1] != '=' || + hexstr2bin((char *) (buf + 2), recv_response, + MSCHAPV2_AUTH_RESPONSE_LEN) || + os_memcmp_const(auth_response, recv_response, + MSCHAPV2_AUTH_RESPONSE_LEN) != 0) + return -1; + return 0; +} diff --git a/freebsd/contrib/wpa/src/eap_peer/mschapv2.h b/freebsd/contrib/wpa/src/eap_peer/mschapv2.h new file mode 100644 index 00000000..edd458b4 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/mschapv2.h @@ -0,0 +1,28 @@ +/* + * MSCHAPV2 (RFC 2759) + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MSCHAPV2_H +#define MSCHAPV2_H + +#define MSCHAPV2_CHAL_LEN 16 +#define MSCHAPV2_NT_RESPONSE_LEN 24 +#define MSCHAPV2_AUTH_RESPONSE_LEN 20 +#define MSCHAPV2_MASTER_KEY_LEN 16 + +const u8 * mschapv2_remove_domain(const u8 *username, size_t *len); +int mschapv2_derive_response(const u8 *username, size_t username_len, + const u8 *password, size_t password_len, + int pwhash, + const u8 *auth_challenge, + const u8 *peer_challenge, + u8 *nt_response, u8 *auth_response, + u8 *master_key); +int mschapv2_verify_auth_response(const u8 *auth_response, + const u8 *buf, size_t buf_len); + +#endif /* MSCHAPV2_H */ diff --git a/freebsd/contrib/wpa/src/eap_peer/tncc.h b/freebsd/contrib/wpa/src/eap_peer/tncc.h new file mode 100644 index 00000000..df2a2870 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_peer/tncc.h @@ -0,0 +1,36 @@ +/* + * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TNCC_H +#define TNCC_H + +struct tncc_data; + +struct tncc_data * tncc_init(void); +void tncc_deinit(struct tncc_data *tncc); +void tncc_init_connection(struct tncc_data *tncc); +size_t tncc_total_send_len(struct tncc_data *tncc); +u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos); +char * tncc_if_tnccs_start(struct tncc_data *tncc); +char * tncc_if_tnccs_end(void); + +enum tncc_process_res { + TNCCS_PROCESS_ERROR = -1, + TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0, + TNCCS_RECOMMENDATION_ERROR, + TNCCS_RECOMMENDATION_ALLOW, + TNCCS_RECOMMENDATION_NONE, + TNCCS_RECOMMENDATION_ISOLATE +}; + +enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc, + const u8 *msg, size_t len); + +struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len); + +#endif /* TNCC_H */ diff --git a/freebsd/contrib/wpa/src/eap_server/eap_methods.h b/freebsd/contrib/wpa/src/eap_server/eap_methods.h new file mode 100644 index 00000000..0baa3279 --- /dev/null +++ b/freebsd/contrib/wpa/src/eap_server/eap_methods.h @@ -0,0 +1,51 @@ +/* + * EAP server method registration + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_SERVER_METHODS_H +#define EAP_SERVER_METHODS_H + +#include "eap_common/eap_defs.h" + +const struct eap_method * eap_server_get_eap_method(int vendor, + EapType method); +struct eap_method * eap_server_method_alloc(int version, int vendor, + EapType method, const char *name); +void eap_server_method_free(struct eap_method *method); +int eap_server_method_register(struct eap_method *method); + +EapType eap_server_get_type(const char *name, int *vendor); +void eap_server_unregister_methods(void); +const char * eap_server_get_name(int vendor, EapType type); + +/* EAP server method registration calls for statically linked in methods */ +int eap_server_identity_register(void); +int eap_server_md5_register(void); +int eap_server_tls_register(void); +int eap_server_unauth_tls_register(void); +int eap_server_wfa_unauth_tls_register(void); +int eap_server_mschapv2_register(void); +int eap_server_peap_register(void); +int eap_server_tlv_register(void); +int eap_server_gtc_register(void); +int eap_server_ttls_register(void); +int eap_server_sim_register(void); +int eap_server_aka_register(void); +int eap_server_aka_prime_register(void); +int eap_server_pax_register(void); +int eap_server_psk_register(void); +int eap_server_sake_register(void); +int eap_server_gpsk_register(void); +int eap_server_vendor_test_register(void); +int eap_server_fast_register(void); +int eap_server_wsc_register(void); +int eap_server_ikev2_register(void); +int eap_server_tnc_register(void); +int eap_server_pwd_register(void); +int eap_server_eke_register(void); + +#endif /* EAP_SERVER_METHODS_H */ diff --git a/freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.c b/freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.c new file mode 100644 index 00000000..e633d7c1 --- /dev/null +++ b/freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.c @@ -0,0 +1,2152 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * EAPOL supplicant state machines + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "state_machine.h" +#include "wpabuf.h" +#include "eloop.h" +#include "crypto/crypto.h" +#include "crypto/md5.h" +#include "common/eapol_common.h" +#include "eap_peer/eap.h" +#include "eap_peer/eap_proxy.h" +#include "eapol_supp_sm.h" + +#define STATE_MACHINE_DATA struct eapol_sm +#define STATE_MACHINE_DEBUG_PREFIX "EAPOL" + + +/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */ + +/** + * struct eapol_sm - Internal data for EAPOL state machines + */ +struct eapol_sm { + /* Timers */ + unsigned int authWhile; + unsigned int heldWhile; + unsigned int startWhen; + unsigned int idleWhile; /* for EAP state machine */ + int timer_tick_enabled; + + /* Global variables */ + Boolean eapFail; + Boolean eapolEap; + Boolean eapSuccess; + Boolean initialize; + Boolean keyDone; + Boolean keyRun; + PortControl portControl; + Boolean portEnabled; + PortStatus suppPortStatus; /* dot1xSuppControlledPortStatus */ + Boolean portValid; + Boolean suppAbort; + Boolean suppFail; + Boolean suppStart; + Boolean suppSuccess; + Boolean suppTimeout; + + /* Supplicant PAE state machine */ + enum { + SUPP_PAE_UNKNOWN = 0, + SUPP_PAE_DISCONNECTED = 1, + SUPP_PAE_LOGOFF = 2, + SUPP_PAE_CONNECTING = 3, + SUPP_PAE_AUTHENTICATING = 4, + SUPP_PAE_AUTHENTICATED = 5, + /* unused(6) */ + SUPP_PAE_HELD = 7, + SUPP_PAE_RESTART = 8, + SUPP_PAE_S_FORCE_AUTH = 9, + SUPP_PAE_S_FORCE_UNAUTH = 10 + } SUPP_PAE_state; /* dot1xSuppPaeState */ + /* Variables */ + Boolean userLogoff; + Boolean logoffSent; + unsigned int startCount; + Boolean eapRestart; + PortControl sPortMode; + /* Constants */ + unsigned int heldPeriod; /* dot1xSuppHeldPeriod */ + unsigned int startPeriod; /* dot1xSuppStartPeriod */ + unsigned int maxStart; /* dot1xSuppMaxStart */ + + /* Key Receive state machine */ + enum { + KEY_RX_UNKNOWN = 0, + KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE + } KEY_RX_state; + /* Variables */ + Boolean rxKey; + + /* Supplicant Backend state machine */ + enum { + SUPP_BE_UNKNOWN = 0, + SUPP_BE_INITIALIZE = 1, + SUPP_BE_IDLE = 2, + SUPP_BE_REQUEST = 3, + SUPP_BE_RECEIVE = 4, + SUPP_BE_RESPONSE = 5, + SUPP_BE_FAIL = 6, + SUPP_BE_TIMEOUT = 7, + SUPP_BE_SUCCESS = 8 + } SUPP_BE_state; /* dot1xSuppBackendPaeState */ + /* Variables */ + Boolean eapNoResp; + Boolean eapReq; + Boolean eapResp; + /* Constants */ + unsigned int authPeriod; /* dot1xSuppAuthPeriod */ + + /* Statistics */ + unsigned int dot1xSuppEapolFramesRx; + unsigned int dot1xSuppEapolFramesTx; + unsigned int dot1xSuppEapolStartFramesTx; + unsigned int dot1xSuppEapolLogoffFramesTx; + unsigned int dot1xSuppEapolRespFramesTx; + unsigned int dot1xSuppEapolReqIdFramesRx; + unsigned int dot1xSuppEapolReqFramesRx; + unsigned int dot1xSuppInvalidEapolFramesRx; + unsigned int dot1xSuppEapLengthErrorFramesRx; + unsigned int dot1xSuppLastEapolFrameVersion; + unsigned char dot1xSuppLastEapolFrameSource[6]; + + /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */ + Boolean changed; + struct eap_sm *eap; + struct eap_peer_config *config; + Boolean initial_req; + u8 *last_rx_key; + size_t last_rx_key_len; + struct wpabuf *eapReqData; /* for EAP */ + Boolean altAccept; /* for EAP */ + Boolean altReject; /* for EAP */ + Boolean eapTriggerStart; + 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; + + Boolean force_authorized_update; + +#ifdef CONFIG_EAP_PROXY + Boolean use_eap_proxy; + struct eap_proxy_sm *eap_proxy; +#endif /* CONFIG_EAP_PROXY */ +}; + + +static void eapol_sm_txLogoff(struct eapol_sm *sm); +static void eapol_sm_txStart(struct eapol_sm *sm); +static void eapol_sm_processKey(struct eapol_sm *sm); +static void eapol_sm_getSuppRsp(struct eapol_sm *sm); +static void eapol_sm_txSuppRsp(struct eapol_sm *sm); +static void eapol_sm_abortSupp(struct eapol_sm *sm); +static void eapol_sm_abort_cached(struct eapol_sm *sm); +static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx); +static void eapol_sm_set_port_authorized(struct eapol_sm *sm); +static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm); + + +/* Port Timers state machine - implemented as a function that will be called + * once a second as a registered event loop timeout */ +static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_sm *sm = timeout_ctx; + + if (sm->authWhile > 0) { + sm->authWhile--; + if (sm->authWhile == 0) + wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0"); + } + if (sm->heldWhile > 0) { + sm->heldWhile--; + if (sm->heldWhile == 0) + wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0"); + } + if (sm->startWhen > 0) { + sm->startWhen--; + if (sm->startWhen == 0) + wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0"); + } + if (sm->idleWhile > 0) { + sm->idleWhile--; + if (sm->idleWhile == 0) + wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0"); + } + + if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) { + eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, + sm); + } else { + wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick"); + sm->timer_tick_enabled = 0; + } + eapol_sm_step(sm); +} + + +static void eapol_enable_timer_tick(struct eapol_sm *sm) +{ + if (sm->timer_tick_enabled) + return; + wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick"); + sm->timer_tick_enabled = 1; + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); +} + + +SM_STATE(SUPP_PAE, LOGOFF) +{ + SM_ENTRY(SUPP_PAE, LOGOFF); + eapol_sm_txLogoff(sm); + sm->logoffSent = TRUE; + eapol_sm_set_port_unauthorized(sm); +} + + +SM_STATE(SUPP_PAE, DISCONNECTED) +{ + SM_ENTRY(SUPP_PAE, DISCONNECTED); + sm->sPortMode = Auto; + sm->startCount = 0; + sm->eapTriggerStart = FALSE; + sm->logoffSent = FALSE; + eapol_sm_set_port_unauthorized(sm); + sm->suppAbort = TRUE; + + sm->unicast_key_received = FALSE; + sm->broadcast_key_received = FALSE; + + /* + * IEEE Std 802.1X-2004 does not clear heldWhile here, but doing so + * allows the timer tick to be stopped more quickly when the port is + * not enabled. Since this variable is used only within HELD state, + * clearing it on initialization does not change actual state machine + * behavior. + */ + sm->heldWhile = 0; +} + + +SM_STATE(SUPP_PAE, CONNECTING) +{ + int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING || + sm->SUPP_PAE_state == SUPP_PAE_HELD; + SM_ENTRY(SUPP_PAE, CONNECTING); + + if (sm->eapTriggerStart) + send_start = 1; + sm->eapTriggerStart = FALSE; + + 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. + */ + if (sm->conf.wps && !(sm->conf.wps & EAPOL_PEER_IS_WPS20_AP)) { + /* Reduce latency on starting WPS negotiation. */ + wpa_printf(MSG_DEBUG, + "EAPOL: Using shorter startWhen for WPS"); + sm->startWhen = 1; + } else { + sm->startWhen = 2; + } + } + 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); + eapol_sm_set_port_unauthorized(sm); + sm->cb_status = EAPOL_CB_FAILURE; +} + + +SM_STATE(SUPP_PAE, AUTHENTICATED) +{ + SM_ENTRY(SUPP_PAE, AUTHENTICATED); + 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); + eapol_sm_set_port_authorized(sm); + sm->sPortMode = ForceAuthorized; +} + + +SM_STATE(SUPP_PAE, S_FORCE_UNAUTH) +{ + SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH); + 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); + else if (sm->eapTriggerStart) + SM_ENTER(SUPP_PAE, CONNECTING); + break; + case SUPP_PAE_HELD: + if (sm->heldWhile == 0) + SM_ENTER(SUPP_PAE, CONNECTING); + else if (sm->eapolEap) + SM_ENTER(SUPP_PAE, RESTART); + break; + case SUPP_PAE_AUTHENTICATED: + if (sm->eapolEap && sm->portValid) + SM_ENTER(SUPP_PAE, RESTART); + else if (!sm->portValid) + SM_ENTER(SUPP_PAE, DISCONNECTED); + break; + case SUPP_PAE_RESTART: + if (!sm->eapRestart) + SM_ENTER(SUPP_PAE, AUTHENTICATING); + break; + case SUPP_PAE_S_FORCE_AUTH: + break; + case SUPP_PAE_S_FORCE_UNAUTH: + break; + } +} + + +SM_STATE(KEY_RX, NO_KEY_RECEIVE) +{ + SM_ENTRY(KEY_RX, NO_KEY_RECEIVE); +} + + +SM_STATE(KEY_RX, KEY_RECEIVE) +{ + SM_ENTRY(KEY_RX, KEY_RECEIVE); + eapol_sm_processKey(sm); + sm->rxKey = FALSE; +} + + +SM_STEP(KEY_RX) +{ + if (sm->initialize || !sm->portEnabled) + SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); + switch (sm->KEY_RX_state) { + case KEY_RX_UNKNOWN: + break; + case KEY_RX_NO_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + case KEY_RX_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + } +} + + +SM_STATE(SUPP_BE, REQUEST) +{ + SM_ENTRY(SUPP_BE, REQUEST); + sm->authWhile = 0; + sm->eapReq = TRUE; + eapol_sm_getSuppRsp(sm); +} + + +SM_STATE(SUPP_BE, RESPONSE) +{ + SM_ENTRY(SUPP_BE, RESPONSE); + eapol_sm_txSuppRsp(sm); + sm->eapResp = FALSE; +} + + +SM_STATE(SUPP_BE, SUCCESS) +{ + SM_ENTRY(SUPP_BE, SUCCESS); + sm->keyRun = TRUE; + sm->suppSuccess = TRUE; + +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + if (eap_proxy_key_available(sm->eap_proxy)) { + /* New key received - clear IEEE 802.1X EAPOL-Key replay + * counter */ + sm->replay_counter_valid = FALSE; + } + return; + } +#endif /* CONFIG_EAP_PROXY */ + + if (eap_key_available(sm->eap)) { + /* New key received - clear IEEE 802.1X EAPOL-Key replay + * counter */ + sm->replay_counter_valid = FALSE; + } +} + + +SM_STATE(SUPP_BE, FAIL) +{ + SM_ENTRY(SUPP_BE, FAIL); + sm->suppFail = TRUE; +} + + +SM_STATE(SUPP_BE, TIMEOUT) +{ + SM_ENTRY(SUPP_BE, TIMEOUT); + sm->suppTimeout = TRUE; +} + + +SM_STATE(SUPP_BE, IDLE) +{ + SM_ENTRY(SUPP_BE, IDLE); + sm->suppStart = FALSE; + sm->initial_req = TRUE; +} + + +SM_STATE(SUPP_BE, INITIALIZE) +{ + SM_ENTRY(SUPP_BE, INITIALIZE); + eapol_sm_abortSupp(sm); + sm->suppAbort = FALSE; + + /* + * IEEE Std 802.1X-2004 does not clear authWhile here, but doing so + * allows the timer tick to be stopped more quickly when the port is + * not enabled. Since this variable is used only within RECEIVE state, + * clearing it on initialization does not change actual state machine + * behavior. + */ + sm->authWhile = 0; +} + + +SM_STATE(SUPP_BE, RECEIVE) +{ + SM_ENTRY(SUPP_BE, RECEIVE); + sm->authWhile = sm->authPeriod; + eapol_enable_timer_tick(sm); + sm->eapolEap = FALSE; + sm->eapNoResp = FALSE; + sm->initial_req = FALSE; +} + + +SM_STEP(SUPP_BE) +{ + if (sm->initialize || sm->suppAbort) + SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE); + else switch (sm->SUPP_BE_state) { + case SUPP_BE_UNKNOWN: + break; + case SUPP_BE_REQUEST: + /* + * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL + * and SUCCESS based on eapFail and eapSuccess, respectively. + * However, IEEE Std 802.1X-2004 is also specifying that + * eapNoResp should be set in conjunction with eapSuccess and + * eapFail which would mean that more than one of the + * transitions here would be activated at the same time. + * Skipping RESPONSE and/or RECEIVE states in these cases can + * cause problems and the direct transitions to do not seem + * correct. Because of this, the conditions for these + * transitions are verified only after eapNoResp. They are + * unlikely to be used since eapNoResp should always be set if + * either of eapSuccess or eapFail is set. + */ + if (sm->eapResp && sm->eapNoResp) { + wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both " + "eapResp and eapNoResp set?!"); + } + if (sm->eapResp) + SM_ENTER(SUPP_BE, RESPONSE); + else if (sm->eapNoResp) + SM_ENTER(SUPP_BE, RECEIVE); + else if (sm->eapFail) + SM_ENTER(SUPP_BE, FAIL); + else if (sm->eapSuccess) + SM_ENTER(SUPP_BE, SUCCESS); + break; + case SUPP_BE_RESPONSE: + SM_ENTER(SUPP_BE, RECEIVE); + break; + case SUPP_BE_SUCCESS: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_FAIL: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_TIMEOUT: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_IDLE: + if (sm->eapFail && sm->suppStart) + SM_ENTER(SUPP_BE, FAIL); + else if (sm->eapolEap && sm->suppStart) + SM_ENTER(SUPP_BE, REQUEST); + else if (sm->eapSuccess && sm->suppStart) + SM_ENTER(SUPP_BE, SUCCESS); + break; + case SUPP_BE_INITIALIZE: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_RECEIVE: + if (sm->eapolEap) + SM_ENTER(SUPP_BE, REQUEST); + else if (sm->eapFail) + SM_ENTER(SUPP_BE, FAIL); + else if (sm->authWhile == 0) + SM_ENTER(SUPP_BE, TIMEOUT); + else if (sm->eapSuccess) + SM_ENTER(SUPP_BE, SUCCESS); + break; + } +} + + +static void eapol_sm_txLogoff(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: txLogoff"); + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0); + sm->dot1xSuppEapolLogoffFramesTx++; + sm->dot1xSuppEapolFramesTx++; +} + + +static void eapol_sm_txStart(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: txStart"); + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0); + sm->dot1xSuppEapolStartFramesTx++; + sm->dot1xSuppEapolFramesTx++; +} + + +#define IEEE8021X_ENCR_KEY_LEN 32 +#define IEEE8021X_SIGN_KEY_LEN 32 + +struct eap_key_data { + u8 encr_key[IEEE8021X_ENCR_KEY_LEN]; + u8 sign_key[IEEE8021X_SIGN_KEY_LEN]; +}; + + +static void eapol_sm_processKey(struct eapol_sm *sm) +{ +#ifndef CONFIG_FIPS + struct ieee802_1x_hdr *hdr; + struct ieee802_1x_eapol_key *key; + struct eap_key_data keydata; + u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32]; +#ifndef CONFIG_NO_RC4 + u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; +#endif /* CONFIG_NO_RC4 */ + int key_len, res, sign_key_len, encr_key_len; + u16 rx_key_length; + size_t plen; + + wpa_printf(MSG_DEBUG, "EAPOL: processKey"); + if (sm->last_rx_key == NULL) + return; + + if (!sm->conf.accept_802_1x_keys) { + wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key" + " even though this was not accepted - " + "ignoring this packet"); + return; + } + + if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key)) + return; + hdr = (struct ieee802_1x_hdr *) sm->last_rx_key; + key = (struct ieee802_1x_eapol_key *) (hdr + 1); + plen = be_to_host16(hdr->length); + if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) { + wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame"); + return; + } + rx_key_length = WPA_GET_BE16(key->key_length); + wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d " + "EAPOL-Key: type=%d key_length=%d key_index=0x%x", + hdr->version, hdr->type, be_to_host16(hdr->length), + key->type, rx_key_length, key->key_index); + + eapol_sm_notify_lower_layer_success(sm, 1); + sign_key_len = IEEE8021X_SIGN_KEY_LEN; + encr_key_len = IEEE8021X_ENCR_KEY_LEN; + res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata)); + if (res < 0) { + wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for " + "decrypting EAPOL-Key keys"); + return; + } + if (res == 16) { + /* LEAP derives only 16 bytes of keying material. */ + res = eapol_sm_get_key(sm, (u8 *) &keydata, 16); + if (res) { + wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP " + "master key for decrypting EAPOL-Key keys"); + return; + } + sign_key_len = 16; + encr_key_len = 16; + os_memcpy(keydata.sign_key, keydata.encr_key, 16); + } else if (res) { + wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key " + "data for decrypting EAPOL-Key keys (res=%d)", res); + return; + } + + /* The key replay_counter must increase when same master key */ + if (sm->replay_counter_valid && + os_memcmp(sm->last_replay_counter, key->replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN) >= 0) { + wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did " + "not increase - ignoring key"); + wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter", + sm->last_replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN); + wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter", + key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN); + return; + } + + /* Verify key signature (HMAC-MD5) */ + os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN); + os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN); + hmac_md5(keydata.sign_key, sign_key_len, + sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length), + key->key_signature); + if (os_memcmp_const(orig_key_sign, key->key_signature, + IEEE8021X_KEY_SIGN_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in " + "EAPOL-Key packet"); + os_memcpy(key->key_signature, orig_key_sign, + IEEE8021X_KEY_SIGN_LEN); + return; + } + wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified"); + + key_len = plen - sizeof(*key); + if (key_len > 32 || rx_key_length > 32) { + wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d", + key_len ? key_len : rx_key_length); + return; + } + if (key_len == rx_key_length) { +#ifdef CONFIG_NO_RC4 + if (encr_key_len) { + /* otherwise unused */ + } + wpa_printf(MSG_ERROR, "EAPOL: RC4 not supported in the build"); + return; +#else /* CONFIG_NO_RC4 */ + 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); +#endif /* CONFIG_NO_RC4 */ + } else if (key_len == 0) { + /* + * IEEE 802.1X-2004 specifies that least significant Key Length + * octets from MS-MPPE-Send-Key are used as the key if the key + * data is not present. This seems to be meaning the beginning + * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in + * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator. + * Anyway, taking the beginning of the keying material from EAP + * seems to interoperate with Authenticators. + */ + key_len = rx_key_length; + os_memcpy(datakey, keydata.encr_key, key_len); + wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying " + "material data encryption key", + datakey, key_len); + } else { + wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d " + "(key_length=%d)", key_len, rx_key_length); + return; + } + + sm->replay_counter_valid = TRUE; + os_memcpy(sm->last_replay_counter, key->replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN); + + wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d " + "len %d", + key->key_index & IEEE8021X_KEY_INDEX_FLAG ? + "unicast" : "broadcast", + key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len); + + if (sm->ctx->set_wep_key && + sm->ctx->set_wep_key(sm->ctx->ctx, + key->key_index & IEEE8021X_KEY_INDEX_FLAG, + key->key_index & IEEE8021X_KEY_INDEX_MASK, + datakey, key_len) < 0) { + wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the " + " driver."); + } else { + if (key->key_index & IEEE8021X_KEY_INDEX_FLAG) + sm->unicast_key_received = TRUE; + else + sm->broadcast_key_received = TRUE; + + if ((sm->unicast_key_received || + !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) && + (sm->broadcast_key_received || + !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST))) + { + wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key " + "frames received"); + sm->portValid = TRUE; + if (sm->ctx->eapol_done_cb) + sm->ctx->eapol_done_cb(sm->ctx->ctx); + } + } +#endif /* CONFIG_FIPS */ +} + + +static void eapol_sm_getSuppRsp(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp"); + /* EAP layer processing; no special code is needed, since Supplicant + * Backend state machine is waiting for eapNoResp or eapResp to be set + * and these are only set in the EAP state machine when the processing + * has finished. */ +} + + +static void eapol_sm_txSuppRsp(struct eapol_sm *sm) +{ + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp"); + +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + /* Get EAP Response from EAP Proxy */ + resp = eap_proxy_get_eapRespData(sm->eap_proxy); + if (resp == NULL) { + wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP Proxy " + "response data not available"); + return; + } + } else +#endif /* CONFIG_EAP_PROXY */ + + resp = eap_get_eapRespData(sm->eap); + if (resp == NULL) { + wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data " + "not available"); + return; + } + + /* Send EAP-Packet from the EAP layer to the Authenticator */ + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp), + wpabuf_len(resp)); + + /* eapRespData is not used anymore, so free it here */ + wpabuf_free(resp); + + if (sm->initial_req) + sm->dot1xSuppEapolReqIdFramesRx++; + else + sm->dot1xSuppEapolReqFramesRx++; + sm->dot1xSuppEapolRespFramesTx++; + sm->dot1xSuppEapolFramesTx++; +} + + +static void eapol_sm_abortSupp(struct eapol_sm *sm) +{ + /* release system resources that may have been allocated for the + * authentication session */ + os_free(sm->last_rx_key); + sm->last_rx_key = NULL; + wpabuf_free(sm->eapReqData); + sm->eapReqData = NULL; + eap_sm_abort(sm->eap); +} + + +static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx) +{ + eapol_sm_step(timeout_ctx); +} + + +static void eapol_sm_set_port_authorized(struct eapol_sm *sm) +{ + int cb; + + cb = sm->suppPortStatus != Authorized || sm->force_authorized_update; + sm->force_authorized_update = FALSE; + sm->suppPortStatus = Authorized; + if (cb && sm->ctx->port_cb) + sm->ctx->port_cb(sm->ctx->ctx, 1); +} + + +static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm) +{ + int cb; + + cb = sm->suppPortStatus != Unauthorized || sm->force_authorized_update; + sm->force_authorized_update = FALSE; + sm->suppPortStatus = Unauthorized; + if (cb && sm->ctx->port_cb) + sm->ctx->port_cb(sm->ctx->ctx, 0); +} + + +/** + * eapol_sm_step - EAPOL state machine step function + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * This function is called to notify the state machine about changed external + * variables. It will step through the EAPOL state machines in loop to process + * all triggered state changes. + */ +void eapol_sm_step(struct eapol_sm *sm) +{ + int i; + + /* In theory, it should be ok to run this in loop until !changed. + * However, it is better to use a limit on number of iterations to + * allow events (e.g., SIGTERM) to stop the program cleanly if the + * state machine were to generate a busy loop. */ + for (i = 0; i < 100; i++) { + sm->changed = FALSE; + SM_STEP_RUN(SUPP_PAE); + SM_STEP_RUN(KEY_RX); + SM_STEP_RUN(SUPP_BE); +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + /* Drive the EAP proxy state machine */ + if (eap_proxy_sm_step(sm->eap_proxy, sm->eap)) + sm->changed = TRUE; + } else +#endif /* CONFIG_EAP_PROXY */ + if (eap_peer_sm_step(sm->eap)) + sm->changed = TRUE; + if (!sm->changed) + break; + } + + if (sm->changed) { + /* restart EAPOL state machine step from timeout call in order + * to allow other events to be processed. */ + eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); + eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm); + } + + if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) { + enum eapol_supp_result result; + if (sm->cb_status == EAPOL_CB_SUCCESS) + result = EAPOL_SUPP_RESULT_SUCCESS; + else if (eap_peer_was_failure_expected(sm->eap)) + result = EAPOL_SUPP_RESULT_EXPECTED_FAILURE; + else + result = EAPOL_SUPP_RESULT_FAILURE; + sm->cb_status = EAPOL_CB_IN_PROGRESS; + sm->ctx->cb(sm, result, 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 (os_snprintf_error(buflen, len)) + 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 (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) + len += eap_proxy_sm_get_status(sm->eap_proxy, + buf + len, buflen - len, + verbose); + else +#endif /* CONFIG_EAP_PROXY */ + len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose); + + return len; +} + + +/** + * eapol_sm_get_mib - Get EAPOL state machine MIBs + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @buf: Buffer for MIB information + * @buflen: Maximum buffer length + * Returns: Number of bytes written to buf. + * + * Query EAPOL state machine for MIB information. This function fills in a + * text area with current MIB information from the EAPOL state machine. If + * the buffer (buf) is not large enough, MIB information will be truncated to + * fit the buffer. + */ +int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) +{ + size_t len; + int ret; + + if (sm == NULL) + return 0; + ret = os_snprintf(buf, buflen, + "dot1xSuppPaeState=%d\n" + "dot1xSuppHeldPeriod=%u\n" + "dot1xSuppAuthPeriod=%u\n" + "dot1xSuppStartPeriod=%u\n" + "dot1xSuppMaxStart=%u\n" + "dot1xSuppSuppControlledPortStatus=%s\n" + "dot1xSuppBackendPaeState=%d\n", + sm->SUPP_PAE_state, + sm->heldPeriod, + sm->authPeriod, + sm->startPeriod, + sm->maxStart, + sm->suppPortStatus == Authorized ? + "Authorized" : "Unauthorized", + sm->SUPP_BE_state); + + if (os_snprintf_error(buflen, ret)) + 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 (os_snprintf_error(buflen - len, ret)) + 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.wps && sm->conf.workaround && + plen < len - sizeof(*hdr) && + hdr->type == IEEE802_1X_TYPE_EAP_PACKET && + len - sizeof(*hdr) > sizeof(struct eap_hdr)) { + const struct eap_hdr *ehdr = + (const struct eap_hdr *) (hdr + 1); + u16 elen; + + elen = be_to_host16(ehdr->length); + if (elen > plen && elen <= len - sizeof(*hdr)) { + /* + * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS + * packets with too short EAPOL header length field + * (14 octets). This is fixed in firmware Ver.1.49. + * As a workaround, fix the EAPOL header based on the + * correct length in the EAP packet. + */ + wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL " + "payload length based on EAP header: " + "%d -> %d", (int) plen, elen); + plen = elen; + } + } +#endif /* CONFIG_WPS */ + data_len = plen + sizeof(*hdr); + + switch (hdr->type) { + case IEEE802_1X_TYPE_EAP_PACKET: + if (sm->conf.workaround) { + /* + * An AP has been reported to send out EAP message with + * undocumented code 10 at some point near the + * completion of EAP authentication. This can result in + * issues with the unexpected EAP message triggering + * restart of EAPOL authentication. Avoid this by + * skipping the message without advancing the state + * machine. + */ + const struct eap_hdr *ehdr = + (const struct eap_hdr *) (hdr + 1); + if (plen >= sizeof(*ehdr) && ehdr->code == 10) { + wpa_printf(MSG_DEBUG, "EAPOL: Ignore EAP packet with unknown code 10"); + break; + } + } + + if (sm->cached_pmk) { + /* Trying to use PMKSA caching, but Authenticator did + * not seem to have a matching entry. Need to restart + * EAPOL state machines. + */ + eapol_sm_abort_cached(sm); + } + wpabuf_free(sm->eapReqData); + sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen); + if (sm->eapReqData) { + wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet " + "frame"); + sm->eapolEap = TRUE; +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + eap_proxy_packet_update( + sm->eap_proxy, + wpabuf_mhead_u8(sm->eapReqData), + wpabuf_len(sm->eapReqData)); + wpa_printf(MSG_DEBUG, "EAPOL: eap_proxy " + "EAP Req updated"); + } +#endif /* CONFIG_EAP_PROXY */ + eapol_sm_step(sm); + } + break; + case IEEE802_1X_TYPE_EAPOL_KEY: + if (plen < sizeof(*key)) { + wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key " + "frame received"); + break; + } + key = (const struct ieee802_1x_eapol_key *) (hdr + 1); + if (key->type == EAPOL_KEY_TYPE_WPA || + key->type == EAPOL_KEY_TYPE_RSN) { + /* WPA Supplicant takes care of this frame. */ + wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key " + "frame in EAPOL state machines"); + res = 0; + break; + } + if (key->type != EAPOL_KEY_TYPE_RC4) { + wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown " + "EAPOL-Key type %d", key->type); + break; + } + os_free(sm->last_rx_key); + sm->last_rx_key = os_malloc(data_len); + if (sm->last_rx_key) { + wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key " + "frame"); + os_memcpy(sm->last_rx_key, buf, data_len); + sm->last_rx_key_len = data_len; + sm->rxKey = TRUE; + eapol_sm_step(sm); + } + break; +#ifdef CONFIG_MACSEC + case IEEE802_1X_TYPE_EAPOL_MKA: + wpa_printf(MSG_EXCESSIVE, + "EAPOL type %d will be handled by MKA", + hdr->type); + break; +#endif /* CONFIG_MACSEC */ + 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); + if (sm->portEnabled != enabled) + sm->force_authorized_update = TRUE; + sm->portEnabled = enabled; + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_portValid - Notification about portValid change + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @valid: New portValid value + * + * Notify EAPOL state machine about new portValid value. + */ +void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "portValid=%d", valid); + sm->portValid = valid; + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_eap_success - Notification of external EAP success trigger + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @success: %TRUE = set success, %FALSE = clear success + * + * Notify the EAPOL state machine that external event has forced EAP state to + * success (success = %TRUE). This can be cleared by setting success = %FALSE. + * + * This function is called to update EAP state when WPA-PSK key handshake has + * been completed successfully since WPA-PSK does not use EAP state machine. + */ +void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "EAP success=%d", success); + sm->eapSuccess = success; + sm->altAccept = success; + if (success) + eap_notify_success(sm->eap); + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @fail: %TRUE = set failure, %FALSE = clear failure + * + * Notify EAPOL state machine that external event has forced EAP state to + * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE. + */ +void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "EAP fail=%d", fail); + sm->eapFail = fail; + sm->altReject = fail; + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_config - Notification of EAPOL configuration change + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @config: Pointer to current network EAP configuration + * @conf: Pointer to EAPOL configuration data + * + * Notify EAPOL state machine that configuration has changed. config will be + * stored as a backpointer to network configuration. This can be %NULL to clear + * the stored pointed. conf will be copied to local EAPOL/EAP configuration + * data. If conf is %NULL, this part of the configuration change will be + * skipped. + */ +void eapol_sm_notify_config(struct eapol_sm *sm, + struct eap_peer_config *config, + const struct eapol_config *conf) +{ + if (sm == NULL) + return; + + sm->config = config; +#ifdef CONFIG_EAP_PROXY + sm->use_eap_proxy = eap_proxy_notify_config(sm->eap_proxy, config) > 0; +#endif /* CONFIG_EAP_PROXY */ + + if (conf == NULL) + return; + + sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys; + sm->conf.required_keys = conf->required_keys; + sm->conf.fast_reauth = conf->fast_reauth; + sm->conf.workaround = conf->workaround; + sm->conf.wps = conf->wps; +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + /* Using EAP Proxy, so skip EAP state machine update */ + return; + } +#endif /* CONFIG_EAP_PROXY */ + if (sm->eap) { + eap_set_fast_reauth(sm->eap, conf->fast_reauth); + eap_set_workaround(sm->eap, conf->workaround); + eap_set_force_disabled(sm->eap, conf->eap_disabled); + eap_set_external_sim(sm->eap, conf->external_sim); + } +} + + +/** + * eapol_sm_get_key - Get master session key (MSK) from EAP + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @key: Pointer for key buffer + * @len: Number of bytes to copy to key + * Returns: 0 on success (len of key available), maximum available key len + * (>0) if key is available but it is shorter than len, or -1 on failure. + * + * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key + * is available only after a successful authentication. + */ +int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) +{ + const u8 *eap_key; + size_t eap_len; + +#ifdef CONFIG_EAP_PROXY + if (sm && sm->use_eap_proxy) { + /* Get key from EAP proxy */ + if (sm == NULL || !eap_proxy_key_available(sm->eap_proxy)) { + wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available"); + return -1; + } + eap_key = eap_proxy_get_eapKeyData(sm->eap_proxy, &eap_len); + if (eap_key == NULL) { + wpa_printf(MSG_DEBUG, "EAPOL: Failed to get " + "eapKeyData"); + return -1; + } + goto key_fetched; + } +#endif /* CONFIG_EAP_PROXY */ + if (sm == NULL || !eap_key_available(sm->eap)) { + wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available"); + return -1; + } + eap_key = eap_get_eapKeyData(sm->eap, &eap_len); + if (eap_key == NULL) { + wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData"); + return -1; + } +#ifdef CONFIG_EAP_PROXY +key_fetched: +#endif /* CONFIG_EAP_PROXY */ + if (len > eap_len) { + wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not " + "available (len=%lu)", + (unsigned long) len, (unsigned long) eap_len); + return eap_len; + } + os_memcpy(key, eap_key, len); + wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)", + (unsigned long) len); + return 0; +} + + +/** + * eapol_sm_get_session_id - Get EAP Session-Id + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @len: Pointer to variable that will be set to number of bytes in the session + * Returns: Pointer to the EAP Session-Id or %NULL on failure + * + * The Session-Id is available only after a successful authentication. + */ +const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len) +{ + if (sm == NULL || !eap_key_available(sm->eap)) { + wpa_printf(MSG_DEBUG, "EAPOL: EAP Session-Id not available"); + return NULL; + } + return eap_get_eapSessionId(sm->eap, len); +} + + +/** + * eapol_sm_notify_logoff - Notification of logon/logoff commands + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @logoff: Whether command was logoff + * + * Notify EAPOL state machines that user requested logon/logoff. + */ +void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) +{ + if (sm) { + sm->userLogoff = logoff; + if (!logoff) { + /* If there is a delayed txStart queued, start now. */ + sm->startWhen = 0; + } + eapol_sm_step(sm); + } +} + + +/** + * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machines that PMKSA caching was successful. This is used + * to move EAPOL and EAP state machines into authenticated/successful state. + */ +void eapol_sm_notify_cached(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL"); + sm->eapSuccess = TRUE; + eap_notify_success(sm->eap); + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machines if PMKSA caching is used. + */ +void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA"); + sm->cached_pmk = TRUE; +} + + +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; + 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; + case EAPOL_eapTriggerStart: + return sm->eapTriggerStart; + } + 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; + case EAPOL_eapTriggerStart: + sm->eapTriggerStart = value; + break; + } +} + + +static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return 0; + switch (variable) { + case EAPOL_idleWhile: + return sm->idleWhile; + } + return 0; +} + + +static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable, + unsigned int value) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return; + switch (variable) { + case EAPOL_idleWhile: + sm->idleWhile = value; + if (sm->idleWhile > 0) + eapol_enable_timer_tick(sm); + break; + } +} + + +static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + struct eapol_sm *sm = ctx; + if (sm && sm->ctx && sm->ctx->set_config_blob) + sm->ctx->set_config_blob(sm->ctx->ctx, blob); +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + +static const struct wpa_config_blob * +eapol_sm_get_config_blob(void *ctx, const char *name) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + struct eapol_sm *sm = ctx; + if (sm && sm->ctx && sm->ctx->get_config_blob) + return sm->ctx->get_config_blob(sm->ctx->ctx, name); + else + return NULL; +#else /* CONFIG_NO_CONFIG_BLOBS */ + return NULL; +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + +static void eapol_sm_notify_pending(void *ctx) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return; + if (sm->eapReqData && !sm->eapReq) { + wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP " + "state machine - retrying pending EAP Request"); + sm->eapolEap = TRUE; + sm->eapReq = TRUE; + eapol_sm_step(sm); + } +} + + +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, + const char *txt) +{ + struct eapol_sm *sm = ctx; + wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed"); + if (sm->ctx->eap_param_needed) + sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt); +} +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +#define eapol_sm_eap_param_needed NULL +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + +static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject, + const char *altsubject[], + int num_altsubject, const char *cert_hash, + const struct wpabuf *cert) +{ + struct eapol_sm *sm = ctx; + if (sm->ctx->cert_cb) + sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, altsubject, + num_altsubject, cert_hash, cert); +} + + +static void eapol_sm_notify_status(void *ctx, const char *status, + const char *parameter) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->status_cb) + sm->ctx->status_cb(sm->ctx->ctx, status, parameter); +} + + +#ifdef CONFIG_EAP_PROXY +static void eapol_sm_eap_proxy_cb(void *ctx) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->eap_proxy_cb) + sm->ctx->eap_proxy_cb(sm->ctx->ctx); +} +#endif /* CONFIG_EAP_PROXY */ + + +static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->set_anon_id) + sm->ctx->set_anon_id(sm->ctx->ctx, id, len); +} + + +static const struct eapol_callbacks eapol_cb = +{ + eapol_sm_get_config, + eapol_sm_get_bool, + eapol_sm_set_bool, + eapol_sm_get_int, + eapol_sm_set_int, + eapol_sm_get_eapReqData, + eapol_sm_set_config_blob, + eapol_sm_get_config_blob, + eapol_sm_notify_pending, + eapol_sm_eap_param_needed, + eapol_sm_notify_cert, + eapol_sm_notify_status, +#ifdef CONFIG_EAP_PROXY + eapol_sm_eap_proxy_cb, +#endif /* CONFIG_EAP_PROXY */ + eapol_sm_set_anon_id +}; + + +/** + * eapol_sm_init - Initialize EAPOL state machine + * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer + * and EAPOL state machine will free it in eapol_sm_deinit() + * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure + * + * Allocate and initialize an EAPOL state machine. + */ +struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) +{ + struct eapol_sm *sm; + struct eap_config conf; + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + sm->ctx = ctx; + + sm->portControl = Auto; + + /* Supplicant PAE state machine */ + sm->heldPeriod = 60; + sm->startPeriod = 30; + sm->maxStart = 3; + + /* Supplicant Backend state machine */ + sm->authPeriod = 30; + + os_memset(&conf, 0, sizeof(conf)); + conf.opensc_engine_path = ctx->opensc_engine_path; + conf.pkcs11_engine_path = ctx->pkcs11_engine_path; + conf.pkcs11_module_path = ctx->pkcs11_module_path; + conf.openssl_ciphers = ctx->openssl_ciphers; + conf.wps = ctx->wps; + conf.cert_in_cb = ctx->cert_in_cb; + + sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf); + if (sm->eap == NULL) { + os_free(sm); + return NULL; + } + +#ifdef CONFIG_EAP_PROXY + sm->use_eap_proxy = FALSE; + sm->eap_proxy = eap_proxy_init(sm, &eapol_cb, sm->ctx->msg_ctx); + if (sm->eap_proxy == NULL) { + wpa_printf(MSG_ERROR, "Unable to initialize EAP Proxy"); + } +#endif /* CONFIG_EAP_PROXY */ + + /* Initialize EAPOL state machines */ + sm->force_authorized_update = TRUE; + sm->initialize = TRUE; + eapol_sm_step(sm); + sm->initialize = FALSE; + eapol_sm_step(sm); + + sm->timer_tick_enabled = 1; + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); + + return sm; +} + + +/** + * eapol_sm_deinit - Deinitialize EAPOL state machine + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Deinitialize and free EAPOL state machine. + */ +void eapol_sm_deinit(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eap_peer_sm_deinit(sm->eap); +#ifdef CONFIG_EAP_PROXY + eap_proxy_deinit(sm->eap_proxy); +#endif /* CONFIG_EAP_PROXY */ + os_free(sm->last_rx_key); + wpabuf_free(sm->eapReqData); + os_free(sm->ctx); + os_free(sm); +} + + +void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext) +{ + if (sm && sm->eap) + eap_sm_set_ext_pw_ctx(sm->eap, ext); +} + + +int eapol_sm_failed(struct eapol_sm *sm) +{ + if (sm == NULL) + return 0; + return !sm->eapSuccess && sm->eapFail; +} + + +int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len) +{ +#ifdef CONFIG_EAP_PROXY + if (sm->eap_proxy == NULL) + return -1; + return eap_proxy_get_imsi(sm->eap_proxy, imsi, len); +#else /* CONFIG_EAP_PROXY */ + return -1; +#endif /* CONFIG_EAP_PROXY */ +} + + +void eapol_sm_erp_flush(struct eapol_sm *sm) +{ + if (sm) + eap_peer_erp_free_keys(sm->eap); +} diff --git a/freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.h b/freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.h new file mode 100644 index 00000000..1309ff75 --- /dev/null +++ b/freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.h @@ -0,0 +1,443 @@ +/* + * EAPOL supplicant state machines + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAPOL_SUPP_SM_H +#define EAPOL_SUPP_SM_H + +#include "common/defs.h" + +typedef enum { Unauthorized, Authorized } PortStatus; +typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl; + +/** + * struct eapol_config - Per network configuration for EAPOL state machines + */ +struct eapol_config { + /** + * accept_802_1x_keys - Accept IEEE 802.1X (non-WPA) EAPOL-Key frames + * + * This variable should be set to 1 when using EAPOL state machines + * with non-WPA security policy to generate dynamic WEP keys. When + * using WPA, this should be set to 0 so that WPA state machine can + * process the EAPOL-Key frames. + */ + int accept_802_1x_keys; + +#define EAPOL_REQUIRE_KEY_UNICAST BIT(0) +#define EAPOL_REQUIRE_KEY_BROADCAST BIT(1) + /** + * required_keys - Which EAPOL-Key packets are required + * + * This variable determines which EAPOL-Key packets are required before + * marking connection authenticated. This is a bit field of + * EAPOL_REQUIRE_KEY_UNICAST and EAPOL_REQUIRE_KEY_BROADCAST flags. + */ + int required_keys; + + /** + * fast_reauth - Whether fast EAP reauthentication is enabled + */ + int fast_reauth; + + /** + * workaround - Whether EAP workarounds are enabled + */ + unsigned int workaround; + + /** + * eap_disabled - Whether EAP is disabled + */ + int eap_disabled; + + /** + * external_sim - Use external processing for SIM/USIM operations + */ + int external_sim; + +#define EAPOL_LOCAL_WPS_IN_USE BIT(0) +#define EAPOL_PEER_IS_WPS20_AP BIT(1) + /** + * wps - Whether this connection is used for WPS + */ + int wps; +}; + +struct eapol_sm; +struct wpa_config_blob; + +enum eapol_supp_result { + EAPOL_SUPP_RESULT_FAILURE, + EAPOL_SUPP_RESULT_SUCCESS, + EAPOL_SUPP_RESULT_EXPECTED_FAILURE +}; + +/** + * struct eapol_ctx - Global (for all networks) EAPOL state machine context + */ +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 + * @result: 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, enum eapol_supp_result result, + 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; + + /** + * openssl_ciphers - OpenSSL cipher string + * + * This is an OpenSSL specific configuration option for configuring the + * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the + * default. + */ + const char *openssl_ciphers; + + /** + * wps - WPS context data + * + * This is only used by EAP-WSC and can be left %NULL if not available. + */ + struct wps_context *wps; + + /** + * eap_param_needed - Notify that EAP parameter is needed + * @ctx: Callback context (ctx) + * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY) + * @txt: User readable text describing the required parameter + */ + void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field, + const char *txt); + + /** + * port_cb - Set port authorized/unauthorized callback (optional) + * @ctx: Callback context (ctx) + * @authorized: Whether the supplicant port is now in authorized state + */ + void (*port_cb)(void *ctx, int authorized); + + /** + * cert_cb - Notification of a peer certificate + * @ctx: Callback context (ctx) + * @depth: Depth in certificate chain (0 = server) + * @subject: Subject of the peer certificate + * @altsubject: Select fields from AltSubject of the peer certificate + * @num_altsubject: Number of altsubject values + * @cert_hash: SHA-256 hash of the certificate + * @cert: Peer certificate + */ + void (*cert_cb)(void *ctx, int depth, const char *subject, + const char *altsubject[], int num_altsubject, + const char *cert_hash, const struct wpabuf *cert); + + /** + * cert_in_cb - Include server certificates in callback + */ + int cert_in_cb; + + /** + * status_cb - Notification of a change in EAP status + * @ctx: Callback context (ctx) + * @status: Step in the process of EAP authentication + * @parameter: Step-specific parameter, e.g., EAP method name + */ + void (*status_cb)(void *ctx, const char *status, + const char *parameter); + +#ifdef CONFIG_EAP_PROXY + /** + * eap_proxy_cb - Callback signifying any updates from eap_proxy + * @ctx: eapol_ctx from eap_peer_sm_init() call + */ + void (*eap_proxy_cb)(void *ctx); +#endif /* CONFIG_EAP_PROXY */ + + /** + * set_anon_id - Set or add anonymous identity + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) + * @len: Length of anonymous identity in octets + */ + void (*set_anon_id)(void *ctx, const u8 *id, size_t len); +}; + + +struct eap_peer_config; +struct ext_password_data; + +#ifdef IEEE8021X_EAPOL +struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx); +void eapol_sm_deinit(struct eapol_sm *sm); +void eapol_sm_step(struct eapol_sm *sm); +int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, + int verbose); +int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen); +void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, + int startPeriod, int maxStart); +int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, + size_t len); +void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm); +void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled); +void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid); +void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success); +void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail); +void eapol_sm_notify_config(struct eapol_sm *sm, + struct eap_peer_config *config, + const struct eapol_config *conf); +int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len); +const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, 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); +void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx); +void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl); +void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm); +void eapol_sm_notify_ctrl_response(struct eapol_sm *sm); +void eapol_sm_request_reauth(struct eapol_sm *sm); +void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm); +void eapol_sm_invalidate_cached_session(struct eapol_sm *sm); +const char * eapol_sm_get_method_name(struct eapol_sm *sm); +void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext); +int eapol_sm_failed(struct eapol_sm *sm); +void eapol_sm_erp_flush(struct eapol_sm *sm); +int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len); +#else /* IEEE8021X_EAPOL */ +static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) +{ + free(ctx); + return (struct eapol_sm *) 1; +} +static inline void eapol_sm_deinit(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_step(struct eapol_sm *sm) +{ +} +static inline int eapol_sm_get_status(struct eapol_sm *sm, char *buf, + size_t buflen, int verbose) +{ + return 0; +} +static inline int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, + size_t buflen) +{ + return 0; +} +static inline void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, + int authPeriod, int startPeriod, + int maxStart) +{ +} +static inline int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, + const u8 *buf, size_t len) +{ + return 0; +} +static inline void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_portEnabled(struct eapol_sm *sm, + Boolean enabled) +{ +} +static inline void eapol_sm_notify_portValid(struct eapol_sm *sm, + Boolean valid) +{ +} +static inline void eapol_sm_notify_eap_success(struct eapol_sm *sm, + Boolean success) +{ +} +static inline void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) +{ +} +static inline void eapol_sm_notify_config(struct eapol_sm *sm, + struct eap_peer_config *config, + struct eapol_config *conf) +{ +} +static inline int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) +{ + return -1; +} +static inline const u8 * +eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len) +{ + return NULL; +} +static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) +{ +} +static inline void eapol_sm_notify_cached(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm) +{ +} +#define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0) +static inline void eapol_sm_notify_portControl(struct eapol_sm *sm, + PortControl portControl) +{ +} +static inline void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_request_reauth(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, + int in_eapol_sm) +{ +} +static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm) +{ +} +static inline const char * eapol_sm_get_method_name(struct eapol_sm *sm) +{ + return NULL; +} +static inline void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext) +{ +} +static inline int eapol_sm_failed(struct eapol_sm *sm) +{ + return 0; +} +static inline void eapol_sm_erp_flush(struct eapol_sm *sm) +{ +} +#endif /* IEEE8021X_EAPOL */ + +#endif /* EAPOL_SUPP_SM_H */ diff --git a/freebsd/contrib/wpa/src/fst/fst.h b/freebsd/contrib/wpa/src/fst/fst.h new file mode 100644 index 00000000..0c0e435b --- /dev/null +++ b/freebsd/contrib/wpa/src/fst/fst.h @@ -0,0 +1,296 @@ +/* + * FST module - interface definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_H +#define FST_H + +#ifdef CONFIG_FST + +#include "common/defs.h" +#include "fst/fst_ctrl_iface.h" + +/* FST module hostap integration API */ + +#define US_IN_MS 1000 +#define LLT_UNIT_US 32 /* See 10.32.2.2 Transitioning between states */ + +#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US) +#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS) + +#define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1) +#define FST_MAX_PRIO_VALUE ((u8) -1) +#define FST_MAX_GROUP_ID_LEN IFNAMSIZ + +#define FST_DEFAULT_LLT_CFG_VALUE 50 + +struct hostapd_hw_modes; +struct ieee80211_mgmt; +struct fst_iface; +struct fst_group; +struct fst_session; +struct fst_get_peer_ctx; +struct fst_ctrl_handle; + +struct fst_wpa_obj { + void *ctx; + + /** + * get_bssid - Get BSSID of the interface + * @ctx: User context %ctx + * Returns: BSSID for success, %NULL for failure. + * + * NOTE: For AP it returns the own BSSID, while for STA - the BSSID of + * the associated AP. + */ + const u8 * (*get_bssid)(void *ctx); + + /** + * get_channel_info - Get current channel info + * @ctx: User context %ctx + * @hw_mode: OUT, current HW mode + * @channel: OUT, current channel + */ + void (*get_channel_info)(void *ctx, enum hostapd_hw_mode *hw_mode, + u8 *channel); + + /** + * get_hw_modes - Get hardware modes + * @ctx: User context %ctx + * @modes: OUT, pointer on array of hw modes + * + * Returns: Number of hw modes available. + */ + int (*get_hw_modes)(void *ctx, struct hostapd_hw_modes **modes); + + /** + * set_ies - Set interface's MB IE + * @ctx: User context %ctx + * @fst_ies: MB IE buffer (owned by FST module) + */ + void (*set_ies)(void *ctx, const struct wpabuf *fst_ies); + + /** + * send_action - Send FST Action frame via the interface + * @ctx: User context %ctx + * @addr: Address of the destination STA + * @data: Action frame buffer + * Returns: 0 for success, negative error code for failure. + */ + int (*send_action)(void *ctx, const u8 *addr, struct wpabuf *data); + + /** + * get_mb_ie - Get last MB IE received from STA + * @ctx: User context %ctx + * @addr: Address of the STA + * Returns: MB IE buffer, %NULL if no MB IE received from the STA + */ + const struct wpabuf * (*get_mb_ie)(void *ctx, const u8 *addr); + + /** + * update_mb_ie - Update last MB IE received from STA + * @ctx: User context %ctx + * @addr: Address of the STA + * @buf: Buffer that contains the MB IEs data + * @size: Size of data in %buf + */ + void (*update_mb_ie)(void *ctx, const u8 *addr, + const u8 *buf, size_t size); + + /** + * get_peer_first - Get MAC address of the 1st connected STA + * @ctx: User context %ctx + * @get_ctx: Context to be used for %get_peer_next call + * @mb_only: %TRUE if only multi-band capable peer should be reported + * Returns: Address of the 1st connected STA, %NULL if no STAs connected + */ + const u8 * (*get_peer_first)(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only); + /** + * get_peer_next - Get MAC address of the next connected STA + * @ctx: User context %ctx + * @get_ctx: Context received from %get_peer_first or previous + * %get_peer_next call + * @mb_only: %TRUE if only multi-band capable peer should be reported + * Returns: Address of the next connected STA, %NULL if no more STAs + * connected + */ + const u8 * (*get_peer_next)(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only); +}; + +/** + * fst_global_init - Global FST module initiator + * Returns: 0 for success, negative error code for failure. + * Note: The purpose of this function is to allocate and initiate global + * FST module data structures (linked lists, static data etc.) + * This function should be called prior to the 1st %fst_attach call. + */ +int fst_global_init(void); + +/** + * fst_global_deinit - Global FST module de-initiator + * Note: The purpose of this function is to deallocate and de-initiate global + * FST module data structures (linked lists, static data etc.) + */ +void fst_global_deinit(void); + +/** + * struct fst_ctrl - Notification interface for FST module + */ +struct fst_ctrl { + /** + * init - Initialize the notification interface + * Returns: 0 for success, negative error code for failure. + */ + int (*init)(void); + + /** + * deinit - Deinitialize the notification interface + */ + void (*deinit)(void); + + /** + * on_group_created - Notify about FST group creation + * Returns: 0 for success, negative error code for failure. + */ + int (*on_group_created)(struct fst_group *g); + + /** + * on_group_deleted - Notify about FST group deletion + */ + void (*on_group_deleted)(struct fst_group *g); + + /** + * on_iface_added - Notify about interface addition + * Returns: 0 for success, negative error code for failure. + */ + int (*on_iface_added)(struct fst_iface *i); + + /** + * on_iface_removed - Notify about interface removal + */ + void (*on_iface_removed)(struct fst_iface *i); + + /** + * on_session_added - Notify about FST session addition + * Returns: 0 for success, negative error code for failure. + */ + int (*on_session_added)(struct fst_session *s); + + /** + * on_session_removed - Notify about FST session removal + */ + void (*on_session_removed)(struct fst_session *s); + + /** + * on_event - Notify about FST event + * @event_type: Event type + * @i: Interface object that relates to the event or NULL + * @g: Group object that relates to the event or NULL + * @extra - Event specific data (see fst_ctrl_iface.h for more info) + */ + void (*on_event)(enum fst_event_type event_type, struct fst_iface *i, + struct fst_session *s, + const union fst_event_extra *extra); +}; + +struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl); +void fst_global_del_ctrl(struct fst_ctrl_handle *h); + +/** + * NOTE: These values have to be read from configuration file + */ +struct fst_iface_cfg { + char group_id[FST_MAX_GROUP_ID_LEN + 1]; + u8 priority; + u32 llt; +}; + +/** + * fst_attach - Attach interface to an FST group according to configuration read + * @ifname: Interface name + * @own_addr: Own interface MAC address + * @iface_obj: Callbacks to be used by FST module to communicate with + * hostapd/wpa_supplicant + * @cfg: FST-related interface configuration read from the configuration file + * Returns: FST interface object for success, %NULL for failure. + */ +struct fst_iface * fst_attach(const char *ifname, + const u8 *own_addr, + const struct fst_wpa_obj *iface_obj, + const struct fst_iface_cfg *cfg); + +/** + * fst_detach - Detach an interface + * @iface: FST interface object + */ +void fst_detach(struct fst_iface *iface); + +/* FST module inputs */ +/** + * fst_rx_action - FST Action frames handler + * @iface: FST interface object + * @mgmt: Action frame arrived + * @len: Action frame length + */ +void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt, + size_t len); + +/** + * fst_notify_peer_connected - FST STA connect handler + * @iface: FST interface object + * @addr: Address of the connected STA + */ +void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr); + +/** + * fst_notify_peer_disconnected - FST STA disconnect handler + * @iface: FST interface object + * @addr: Address of the disconnected STA + */ +void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr); + +/* FST module auxiliary routines */ + +/** + * fst_are_ifaces_aggregated - Determines whether 2 interfaces belong to the + * same FST group + * @iface1: 1st FST interface object + * @iface1: 2nd FST interface object + * + * Returns: %TRUE if the interfaces belong to the same FST group, + * %FALSE otherwise + */ +Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1, + struct fst_iface *iface2); + +#else /* CONFIG_FST */ + +static inline int fst_global_init(void) +{ + return 0; +} + +static inline int fst_global_start(void) +{ + return 0; +} + +static inline void fst_global_stop(void) +{ +} + +static inline void fst_global_deinit(void) +{ +} + +#endif /* CONFIG_FST */ + +#endif /* FST_H */ diff --git a/freebsd/contrib/wpa/src/fst/fst_ctrl_aux.h b/freebsd/contrib/wpa/src/fst/fst_ctrl_aux.h new file mode 100644 index 00000000..e2133f50 --- /dev/null +++ b/freebsd/contrib/wpa/src/fst/fst_ctrl_aux.h @@ -0,0 +1,91 @@ +/* + * FST module - miscellaneous definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_CTRL_AUX_H +#define FST_CTRL_AUX_H + +#include "common/defs.h" + +/* FST module control interface API */ +#define FST_INVALID_SESSION_ID ((u32) -1) +#define FST_MAX_GROUP_ID_SIZE 32 +#define FST_MAX_INTERFACE_SIZE 32 + +enum fst_session_state { + FST_SESSION_STATE_INITIAL, + FST_SESSION_STATE_SETUP_COMPLETION, + FST_SESSION_STATE_TRANSITION_DONE, + FST_SESSION_STATE_TRANSITION_CONFIRMED, + FST_SESSION_STATE_LAST +}; + +enum fst_event_type { + EVENT_FST_IFACE_STATE_CHANGED, /* An interface has been either attached + * to or detached from an FST group */ + EVENT_FST_ESTABLISHED, /* FST Session has been established */ + EVENT_FST_SETUP, /* FST Session request received */ + EVENT_FST_SESSION_STATE_CHANGED,/* FST Session state has been changed */ + EVENT_PEER_STATE_CHANGED /* FST related generic event occurred, + * see struct fst_hostap_event_data for + * more info */ +}; + +enum fst_initiator { + FST_INITIATOR_UNDEFINED, + FST_INITIATOR_LOCAL, + FST_INITIATOR_REMOTE, +}; + +union fst_event_extra { + struct fst_event_extra_iface_state { + Boolean attached; + char ifname[FST_MAX_INTERFACE_SIZE]; + char group_id[FST_MAX_GROUP_ID_SIZE]; + } iface_state; /* for EVENT_FST_IFACE_STATE_CHANGED */ + struct fst_event_extra_peer_state { + Boolean connected; + char ifname[FST_MAX_INTERFACE_SIZE]; + u8 addr[ETH_ALEN]; + } peer_state; /* for EVENT_PEER_STATE_CHANGED */ + struct fst_event_extra_session_state { + enum fst_session_state old_state; + enum fst_session_state new_state; + union fst_session_state_switch_extra { + struct { + enum fst_reason { + REASON_TEARDOWN, + REASON_SETUP, + REASON_SWITCH, + REASON_STT, + REASON_REJECT, + REASON_ERROR_PARAMS, + REASON_RESET, + REASON_DETACH_IFACE, + } reason; + u8 reject_code; /* REASON_REJECT */ + /* REASON_SWITCH, + * REASON_TEARDOWN, + * REASON_REJECT + */ + enum fst_initiator initiator; + } to_initial; + } extra; + } session_state; /* for EVENT_FST_SESSION_STATE_CHANGED */ +}; + +/* helpers - prints enum in string form */ +#define FST_NAME_UNKNOWN "UNKNOWN" + +const char * fst_get_str_name(unsigned index, const char *names[], + size_t names_size); + +const char * fst_session_event_type_name(enum fst_event_type); +const char * fst_reason_name(enum fst_reason reason); +const char * fst_session_state_name(enum fst_session_state state); + +#endif /* FST_CTRL_AUX_H */ diff --git a/freebsd/contrib/wpa/src/fst/fst_ctrl_iface.h b/freebsd/contrib/wpa/src/fst/fst_ctrl_iface.h new file mode 100644 index 00000000..4d0cd9fc --- /dev/null +++ b/freebsd/contrib/wpa/src/fst/fst_ctrl_iface.h @@ -0,0 +1,45 @@ +/* + * FST module - internal Control interface definitions + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FST_CTRL_IFACE_H +#define FST_CTRL_IFACE_H + +#include "fst/fst_ctrl_aux.h" + +#ifdef CONFIG_FST + +/* receiver */ +int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen); + +int fst_ctrl_iface_receive(const char *txtaddr, char *buf, size_t buflen); + +extern const struct fst_ctrl *fst_ctrl_cli; + +#else /* CONFIG_FST */ + +static inline int +fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen) +{ + return 0; +} + +#endif /* CONFIG_FST */ + +int fst_read_next_int_param(const char *params, Boolean *valid, char **endp); +int fst_read_next_text_param(const char *params, char *buf, size_t buflen, + char **endp); +int fst_read_peer_addr(const char *mac, u8 *peer_addr); + +struct fst_iface_cfg; + +int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size, + struct fst_iface_cfg *cfg); +int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size); +int fst_iface_detach(const char *ifname); + +#endif /* CTRL_IFACE_FST_H */ diff --git a/freebsd/contrib/wpa/src/l2_packet/l2_packet.h b/freebsd/contrib/wpa/src/l2_packet/l2_packet.h new file mode 100644 index 00000000..2a452458 --- /dev/null +++ b/freebsd/contrib/wpa/src/l2_packet/l2_packet.h @@ -0,0 +1,154 @@ +/* + * WPA Supplicant - Layer2 packet interface definition + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This file defines an interface for layer 2 (link layer) packet sending and + * receiving. l2_packet_linux.c is one implementation for such a layer 2 + * implementation using Linux packet sockets and l2_packet_pcap.c another one + * using libpcap and libdnet. When porting %wpa_supplicant to other operating + * systems, a new l2_packet implementation may need to be added. + */ + +#ifndef L2_PACKET_H +#define L2_PACKET_H + +/** + * struct l2_packet_data - Internal l2_packet data structure + * + * This structure is used by the l2_packet implementation to store its private + * data. Other files use a pointer to this data when calling the l2_packet + * functions, but the contents of this structure should not be used directly + * outside l2_packet implementation. + */ +struct l2_packet_data; + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct l2_ethhdr { + u8 h_dest[ETH_ALEN]; + u8 h_source[ETH_ALEN]; + be16 h_proto; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +enum l2_packet_filter_type { + L2_PACKET_FILTER_DHCP, + L2_PACKET_FILTER_NDISC, +}; + +/** + * 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_init_bridge - Like l2_packet_init() but with bridge workaround + * + * This version of l2_packet_init() can be used to enable a workaround for Linux + * packet socket in case of a station interface in a bridge. + */ +struct l2_packet_data * l2_packet_init_bridge( + const char *br_ifname, 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); + +/** + * l2_packet_set_packet_filter - Set socket filter for l2_packet + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @type: enum l2_packet_filter_type, type of filter + * Returns: 0 on success, -1 on failure + * + * This function is used to set the socket filter for l2_packet socket. + * + */ +int l2_packet_set_packet_filter(struct l2_packet_data *l2, + enum l2_packet_filter_type type); + +#endif /* L2_PACKET_H */ diff --git a/freebsd/contrib/wpa/src/l2_packet/l2_packet_freebsd.c b/freebsd/contrib/wpa/src/l2_packet/l2_packet_freebsd.c new file mode 100644 index 00000000..555abc5e --- /dev/null +++ b/freebsd/contrib/wpa/src/l2_packet/l2_packet_freebsd.c @@ -0,0 +1,331 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * 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 software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#if defined(__APPLE__) || defined(__GLIBC__) +#include <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; +} + + +struct l2_packet_data * l2_packet_init_bridge( + const char *br_ifname, 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) +{ + return l2_packet_init(br_ifname, own_addr, protocol, rx_callback, + rx_callback_ctx, l2_hdr); +} + + +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) +{ +} + + +int l2_packet_set_packet_filter(struct l2_packet_data *l2, + enum l2_packet_filter_type type) +{ + return -1; +} diff --git a/freebsd/contrib/wpa/src/p2p/p2p.h b/freebsd/contrib/wpa/src/p2p/p2p.h new file mode 100644 index 00000000..b4060be4 --- /dev/null +++ b/freebsd/contrib/wpa/src/p2p/p2p.h @@ -0,0 +1,2343 @@ +/* + * Wi-Fi Direct - P2P module + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef P2P_H +#define P2P_H + +#include "common/ieee802_11_defs.h" +#include "wps/wps.h" + +/* P2P ASP Setup Capability */ +#define P2PS_SETUP_NONE 0 +#define P2PS_SETUP_NEW BIT(0) +#define P2PS_SETUP_CLIENT BIT(1) +#define P2PS_SETUP_GROUP_OWNER BIT(2) + +#define P2PS_WILD_HASH_STR "org.wi-fi.wfds" +#define P2PS_HASH_LEN 6 +#define P2P_MAX_QUERY_HASH 6 +#define P2PS_FEATURE_CAPAB_CPT_MAX 2 + +/** + * P2P_MAX_PREF_CHANNELS - Maximum number of preferred channels + */ +#define P2P_MAX_PREF_CHANNELS 100 + +/** + * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes + */ +#define P2P_MAX_REG_CLASSES 10 + +/** + * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class + */ +#define P2P_MAX_REG_CLASS_CHANNELS 20 + +/** + * struct p2p_channels - List of supported channels + */ +struct p2p_channels { + /** + * struct p2p_reg_class - Supported regulatory class + */ + struct p2p_reg_class { + /** + * reg_class - Regulatory class (IEEE 802.11-2007, Annex J) + */ + u8 reg_class; + + /** + * channel - Supported channels + */ + u8 channel[P2P_MAX_REG_CLASS_CHANNELS]; + + /** + * channels - Number of channel entries in use + */ + size_t channels; + } reg_class[P2P_MAX_REG_CLASSES]; + + /** + * reg_classes - Number of reg_class entries in use + */ + size_t reg_classes; +}; + +enum p2p_wps_method { + WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC, + WPS_P2PS +}; + +/** + * struct p2p_go_neg_results - P2P Group Owner Negotiation results + */ +struct p2p_go_neg_results { + /** + * status - Negotiation result (Status Code) + * + * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate + * failed negotiation. + */ + int status; + + /** + * role_go - Whether local end is Group Owner + */ + int role_go; + + /** + * freq - Frequency of the group operational channel in MHz + */ + int freq; + + int ht40; + + int vht; + + /** + * ssid - SSID of the group + */ + u8 ssid[SSID_MAX_LEN]; + + /** + * ssid_len - Length of SSID in octets + */ + size_t ssid_len; + + /** + * psk - WPA pre-shared key (256 bits) (GO only) + */ + u8 psk[32]; + + /** + * psk_set - Whether PSK field is configured (GO only) + */ + int psk_set; + + /** + * passphrase - WPA2-Personal passphrase for the group (GO only) + */ + char passphrase[64]; + + /** + * peer_device_addr - P2P Device Address of the peer + */ + u8 peer_device_addr[ETH_ALEN]; + + /** + * peer_interface_addr - P2P Interface Address of the peer + */ + u8 peer_interface_addr[ETH_ALEN]; + + /** + * wps_method - WPS method to be used during provisioning + */ + enum p2p_wps_method wps_method; + +#define P2P_MAX_CHANNELS 50 + + /** + * freq_list - Zero-terminated list of possible operational channels + */ + int freq_list[P2P_MAX_CHANNELS]; + + /** + * persistent_group - Whether the group should be made persistent + * 0 = not persistent + * 1 = persistent group without persistent reconnect + * 2 = persistent group with persistent reconnect + */ + int persistent_group; + + /** + * peer_config_timeout - Peer configuration timeout (in 10 msec units) + */ + unsigned int peer_config_timeout; +}; + +struct p2ps_provision { + /** + * pd_seeker - P2PS provision discovery seeker role + */ + unsigned int pd_seeker:1; + + /** + * status - Remote returned provisioning status code + */ + int status; + + /** + * adv_id - P2PS Advertisement ID + */ + u32 adv_id; + + /** + * session_id - P2PS Session ID + */ + u32 session_id; + + /** + * method - WPS Method (to be) used to establish session + */ + u16 method; + + /** + * conncap - Connection Capabilities negotiated between P2P peers + */ + u8 conncap; + + /** + * role - Info about the roles to be used for this connection + */ + u8 role; + + /** + * session_mac - MAC address of the peer that started the session + */ + u8 session_mac[ETH_ALEN]; + + /** + * adv_mac - MAC address of the peer advertised the service + */ + u8 adv_mac[ETH_ALEN]; + + /** + * cpt_mask - Supported Coordination Protocol Transport mask + * + * A bitwise mask of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_priority. + */ + u8 cpt_mask; + + /** + * cpt_priority - Coordination Protocol Transport priority list + * + * Priorities of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_mask. + * The CPT priority list is 0 terminated. + */ + u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; + + /** + * info - Vendor defined extra Provisioning information + */ + char info[0]; +}; + +struct p2ps_advertisement { + struct p2ps_advertisement *next; + + /** + * svc_info - Pointer to (internal) Service defined information + */ + char *svc_info; + + /** + * id - P2PS Advertisement ID + */ + u32 id; + + /** + * config_methods - WPS Methods which are allowed for this service + */ + u16 config_methods; + + /** + * state - Current state of the service: 0 - Out Of Service, 1-255 Vendor defined + */ + u8 state; + + /** + * auto_accept - Automatically Accept provisioning request if possible. + */ + u8 auto_accept; + + /** + * hash - 6 octet Service Name has to match against incoming Probe Requests + */ + u8 hash[P2PS_HASH_LEN]; + + /** + * cpt_mask - supported Coordination Protocol Transport mask + * + * A bitwise mask of supported ASP Coordination Protocol Transports. + * This property is set together and corresponds with cpt_priority. + */ + u8 cpt_mask; + + /** + * cpt_priority - Coordination Protocol Transport priority list + * + * Priorities of supported ASP Coordinatin Protocol Transports. + * This property is set together and corresponds with cpt_mask. + * The CPT priority list is 0 terminated. + */ + u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; + + /** + * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage + */ + char svc_name[0]; +}; + + +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[WPS_DEV_NAME_MAX_LEN + 1]; + + /** + * manufacturer - Manufacturer (0..64 octets encoded in UTF-8) + */ + char manufacturer[WPS_MANUFACTURER_MAX_LEN + 1]; + + /** + * model_name - Model Name (0..32 octets encoded in UTF-8) + */ + char model_name[WPS_MODEL_NAME_MAX_LEN + 1]; + + /** + * model_number - Model Number (0..32 octets encoded in UTF-8) + */ + char model_number[WPS_MODEL_NUMBER_MAX_LEN + 1]; + + /** + * serial_number - Serial Number (0..32 octets encoded in UTF-8) + */ + char serial_number[WPS_SERIAL_NUMBER_MAX_LEN + 1]; + + /** + * level - Signal level + */ + int level; + + /** + * config_methods - WPS Configuration Methods + */ + u16 config_methods; + + /** + * dev_capab - Device Capabilities + */ + u8 dev_capab; + + /** + * group_capab - Group Capabilities + */ + u8 group_capab; + + /** + * wps_sec_dev_type_list - WPS secondary device type list + * + * This list includes from 0 to 16 Secondary Device Types as indicated + * by wps_sec_dev_type_list_len (8 * number of types). + */ + u8 wps_sec_dev_type_list[WPS_SEC_DEV_TYPE_MAX_LEN]; + + /** + * wps_sec_dev_type_list_len - Length of secondary device type list + */ + size_t wps_sec_dev_type_list_len; + + struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + + /** + * wfd_subelems - Wi-Fi Display subelements from WFD IE(s) + */ + struct wpabuf *wfd_subelems; + + /** + * vendor_elems - Unrecognized vendor elements + * + * This buffer includes any other vendor element than P2P, WPS, and WFD + * IE(s) from the frame that was used to discover the peer. + */ + struct wpabuf *vendor_elems; + + /** + * p2ps_instance - P2PS Application Service Info + */ + struct wpabuf *p2ps_instance; +}; + +enum p2p_prov_disc_status { + P2P_PROV_DISC_SUCCESS, + P2P_PROV_DISC_TIMEOUT, + P2P_PROV_DISC_REJECTED, + P2P_PROV_DISC_TIMEOUT_JOIN, + P2P_PROV_DISC_INFO_UNAVAILABLE, +}; + +struct p2p_channel { + u8 op_class; + u8 chan; +}; + +/** + * struct p2p_config - P2P configuration + * + * This configuration is provided to the P2P module during initialization with + * p2p_init(). + */ +struct p2p_config { + /** + * country - Country code to use in P2P operations + */ + char country[3]; + + /** + * reg_class - Regulatory class for own listen channel + */ + u8 reg_class; + + /** + * channel - Own listen channel + */ + u8 channel; + + /** + * channel_forced - the listen channel was forced by configuration + * or by control interface and cannot be overridden + */ + u8 channel_forced; + + /** + * Regulatory class for own operational channel + */ + u8 op_reg_class; + + /** + * op_channel - Own operational channel + */ + u8 op_channel; + + /** + * cfg_op_channel - Whether op_channel is hardcoded in configuration + */ + u8 cfg_op_channel; + + /** + * channels - Own supported regulatory classes and channels + * + * List of supposerted channels per regulatory class. The regulatory + * classes are defined in IEEE Std 802.11-2007 Annex J and the + * numbering of the clases depends on the configured country code. + */ + struct p2p_channels channels; + + /** + * cli_channels - Additional client channels + * + * This list of channels (if any) will be used when advertising local + * channels during GO Negotiation or Invitation for the cases where the + * local end may become the client. This may allow the peer to become a + * GO on additional channels if it supports these options. The main use + * case for this is to include passive-scan channels on devices that may + * not know their current location and have configured most channels to + * not allow initiation of radition (i.e., another device needs to take + * master responsibilities). + */ + struct p2p_channels cli_channels; + + /** + * num_pref_chan - Number of pref_chan entries + */ + unsigned int num_pref_chan; + + /** + * pref_chan - Preferred channels for GO Negotiation + */ + struct p2p_channel *pref_chan; + + /** + * pri_dev_type - Primary Device Type (see WPS) + */ + u8 pri_dev_type[8]; + + /** + * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types + */ +#define P2P_SEC_DEVICE_TYPES 5 + + /** + * sec_dev_type - Optional secondary device types + */ + u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8]; + + /** + * num_sec_dev_types - Number of sec_dev_type entries + */ + size_t num_sec_dev_types; + + /** + * dev_addr - P2P Device Address + */ + u8 dev_addr[ETH_ALEN]; + + /** + * dev_name - Device Name + */ + char *dev_name; + + char *manufacturer; + char *model_name; + char *model_number; + char *serial_number; + + u8 uuid[16]; + u16 config_methods; + + /** + * concurrent_operations - Whether concurrent operations are supported + */ + int concurrent_operations; + + /** + * max_peers - Maximum number of discovered peers to remember + * + * If more peers are discovered, older entries will be removed to make + * room for the new ones. + */ + size_t max_peers; + + /** + * p2p_intra_bss - Intra BSS communication is supported + */ + int p2p_intra_bss; + + /** + * ssid_postfix - Postfix data to add to the SSID + * + * This data will be added to the end of the SSID after the + * DIRECT-<random two octets> prefix. + */ + u8 ssid_postfix[SSID_MAX_LEN - 9]; + + /** + * ssid_postfix_len - Length of the ssid_postfix data + */ + size_t ssid_postfix_len; + + /** + * max_listen - Maximum listen duration in ms + */ + unsigned int max_listen; + + /** + * passphrase_len - Passphrase length (8..63) + * + * This parameter controls the length of the random passphrase that is + * generated at the GO. + */ + unsigned int passphrase_len; + + /** + * cb_ctx - Context to use with callback functions + */ + void *cb_ctx; + + /** + * debug_print - Debug print + * @ctx: Callback context from cb_ctx + * @level: Debug verbosity level (MSG_*) + * @msg: Debug message + */ + void (*debug_print)(void *ctx, int level, const char *msg); + + + /* Callbacks to request lower layer driver operations */ + + /** + * p2p_scan - Request a P2P scan/search + * @ctx: Callback context from cb_ctx + * @type: Scan type + * @freq: Specific frequency (MHz) to scan or 0 for no restriction + * @num_req_dev_types: Number of requested device types + * @req_dev_types: Array containing requested device types + * @dev_id: Device ID to search for or %NULL to find all devices + * @pw_id: Device Password ID + * Returns: 0 on success, -1 on failure + * + * This callback function is used to request a P2P scan or search + * operation to be completed. Type type argument specifies which type + * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the + * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL + * indicates that all channels are to be scanned. @P2P_SCAN_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, const u8 *dev_id, u16 pw_id); + + /** + * send_probe_resp - Transmit a Probe Response frame + * @ctx: Callback context from cb_ctx + * @buf: Probe Response frame (including the header and body) + * @freq: Forced frequency (in MHz) to use or 0. + * 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, unless otherwise specified, or to be + * dropped if the driver is not listening to Probe Request frames + * anymore. + * + * 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, + unsigned int freq); + + /** + * send_action - Transmit an Action frame + * @ctx: Callback context from cb_ctx + * @freq: Frequency in MHz for the channel on which to transmit + * @dst: Destination MAC address (Address 1) + * @src: Source MAC address (Address 2) + * @bssid: BSSID (Address 3) + * @buf: Frame body (starting from Category field) + * @len: Length of buf in octets + * @wait_time: How many msec to wait for a response frame + * Returns: 0 on success, -1 on failure + * + * The Action frame may not be transmitted immediately and the status + * of the transmission must be reported by calling + * p2p_send_action_cb() once the frame has either been transmitted or + * it has been dropped due to excessive retries or other failure to + * transmit. + */ + int (*send_action)(void *ctx, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time); + + /** + * send_action_done - Notify that Action frame sequence was completed + * @ctx: Callback context from cb_ctx + * + * This function is called when the Action frame sequence that was + * started with send_action() has been completed, i.e., when there is + * no need to wait for a response from the destination peer anymore. + */ + void (*send_action_done)(void *ctx); + + /** + * start_listen - Start Listen state + * @ctx: Callback context from cb_ctx + * @freq: Frequency of the listen channel in MHz + * @duration: Duration for the Listen state in milliseconds + * @probe_resp_ie: IE(s) to be added to Probe Response frames + * Returns: 0 on success, -1 on failure + * + * This Listen state may not start immediately since the driver may + * have other pending operations to complete first. Once the Listen + * state has started, p2p_listen_cb() must be called to notify the P2P + * module. Once the Listen state is stopped, p2p_listen_end() must be + * called to notify the P2P module that the driver is not in the Listen + * state anymore. + * + * If the send_probe_resp() is not used for generating the response, + * the IEs from probe_resp_ie need to be added to the end of the Probe + * Response frame body. If send_probe_resp() is used, the probe_resp_ie + * information can be ignored. + */ + int (*start_listen)(void *ctx, unsigned int freq, + unsigned int duration, + const struct wpabuf *probe_resp_ie); + /** + * stop_listen - Stop Listen state + * @ctx: Callback context from cb_ctx + * + * This callback can be used to stop a Listen state operation that was + * previously requested with start_listen(). + */ + void (*stop_listen)(void *ctx); + + /** + * get_noa - Get current Notice of Absence attribute payload + * @ctx: Callback context from cb_ctx + * @interface_addr: P2P Interface Address of the GO + * @buf: Buffer for returning NoA + * @buf_len: Buffer length in octets + * Returns: Number of octets used in buf, 0 to indicate no NoA is being + * advertized, or -1 on failure + * + * This function is used to fetch the current Notice of Absence + * attribute value from GO. + */ + int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf, + size_t buf_len); + + /* Callbacks to notify events to upper layer management entity */ + + /** + * dev_found - Notification of a found P2P Device + * @ctx: Callback context from cb_ctx + * @addr: Source address of the message triggering this notification + * @info: P2P peer information + * @new_device: Inform if the peer is newly found + * + * This callback is used to notify that a new P2P Device has been + * found. This may happen, e.g., during Search state based on scan + * results or during Listen state based on receive Probe Request and + * Group Owner Negotiation Request. + */ + void (*dev_found)(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, + int new_device); + + /** + * dev_lost - Notification of a lost P2P Device + * @ctx: Callback context from cb_ctx + * @dev_addr: P2P Device Address of the lost P2P Device + * + * This callback is used to notify that a P2P Device has been deleted. + */ + void (*dev_lost)(void *ctx, const u8 *dev_addr); + + /** + * find_stopped - Notification of a p2p_find operation stopping + * @ctx: Callback context from cb_ctx + */ + void (*find_stopped)(void *ctx); + + /** + * go_neg_req_rx - Notification of a receive GO Negotiation Request + * @ctx: Callback context from cb_ctx + * @src: Source address of the message triggering this notification + * @dev_passwd_id: WPS Device Password ID + * @go_intent: Peer's GO Intent + * + * 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, + u8 go_intent); + + /** + * go_neg_completed - Notification of GO Negotiation results + * @ctx: Callback context from cb_ctx + * @res: GO Negotiation results + * + * This callback is used to notify that Group Owner Negotiation has + * been completed. Non-zero struct p2p_go_neg_results::status indicates + * failed negotiation. In case of success, this function is responsible + * for creating a new group interface (or using the existing interface + * depending on driver features), setting up the group interface in + * proper mode based on struct p2p_go_neg_results::role_go and + * initializing WPS provisioning either as a Registrar (if GO) or as an + * Enrollee. Successful WPS provisioning must be indicated by calling + * p2p_wps_success_cb(). The callee is responsible for timing out group + * formation if WPS provisioning cannot be completed successfully + * within 15 seconds. + */ + void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res); + + /** + * sd_request - Callback on Service Discovery Request + * @ctx: Callback context from cb_ctx + * @freq: Frequency (in MHz) of the channel + * @sa: Source address of the request + * @dialog_token: Dialog token + * @update_indic: Service Update Indicator from the source of request + * @tlvs: P2P Service Request TLV(s) + * @tlvs_len: Length of tlvs buffer in octets + * + * This callback is used to indicate reception of a service discovery + * request. Response to the query must be indicated by calling + * p2p_sd_response() with the context information from the arguments to + * this callback function. + * + * This callback handler can be set to %NULL to indicate that service + * discovery is not supported. + */ + void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len); + + /** + * sd_response - Callback on Service Discovery Response + * @ctx: Callback context from cb_ctx + * @sa: Source address of the request + * @update_indic: Service Update Indicator from the source of response + * @tlvs: P2P Service Response TLV(s) + * @tlvs_len: Length of tlvs buffer in octets + * + * This callback is used to indicate reception of a service discovery + * response. This callback handler can be set to %NULL if no service + * discovery requests are used. The information provided with this call + * is replies to the queries scheduled with p2p_sd_request(). + */ + void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len); + + /** + * prov_disc_req - Callback on Provisiong Discovery Request + * @ctx: Callback context from cb_ctx + * @peer: Source address of the request + * @config_methods: Requested WPS Config Method + * @dev_addr: P2P Device Address of the found P2P Device + * @pri_dev_type: Primary Device Type + * @dev_name: Device Name + * @supp_config_methods: Supported configuration Methods + * @dev_capab: Device Capabilities + * @group_capab: Group Capabilities + * @group_id: P2P Group ID (or %NULL if not included) + * @group_id_len: Length of P2P Group ID + * + * This callback is used to indicate reception of a Provision Discovery + * Request frame that the P2P module accepted. + */ + void (*prov_disc_req)(void *ctx, const u8 *peer, u16 config_methods, + const u8 *dev_addr, const u8 *pri_dev_type, + const char *dev_name, u16 supp_config_methods, + u8 dev_capab, u8 group_capab, + const u8 *group_id, size_t group_id_len); + + /** + * prov_disc_resp - Callback on Provisiong Discovery Response + * @ctx: Callback context from cb_ctx + * @peer: Source address of the response + * @config_methods: Value from p2p_prov_disc_req() or 0 on failure + * + * This callback is used to indicate reception of a Provision Discovery + * Response frame for a pending request scheduled with + * p2p_prov_disc_req(). This callback handler can be set to %NULL if + * provision discovery is not used. + */ + void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods); + + /** + * prov_disc_fail - Callback on Provision Discovery failure + * @ctx: Callback context from cb_ctx + * @peer: Source address of the response + * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS + * @adv_id: If non-zero, then the adv_id of the PD Request + * @adv_mac: P2P Device Address of the advertizer + * @deferred_session_resp: Deferred session response sent by advertizer + * + * This callback is used to indicate either a failure or no response + * to an earlier provision discovery request. + * + * This callback handler can be set to %NULL if provision discovery + * is not used or failures do not need to be indicated. + */ + void (*prov_disc_fail)(void *ctx, const u8 *peer, + enum p2p_prov_disc_status status, + u32 adv_id, const u8 *adv_mac, + const char *deferred_session_resp); + + /** + * invitation_process - Optional callback for processing Invitations + * @ctx: Callback context from cb_ctx + * @sa: Source address of the Invitation Request + * @bssid: P2P Group BSSID from the request or %NULL if not included + * @go_dev_addr: GO Device Address from P2P Group ID + * @ssid: SSID from P2P Group ID + * @ssid_len: Length of ssid buffer in octets + * @go: Variable for returning whether the local end is GO in the group + * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO) + * @force_freq: Variable for returning forced frequency for the group + * @persistent_group: Whether this is an invitation to reinvoke a + * persistent group (instead of invitation to join an active + * group) + * @channels: Available operating channels for the group + * @dev_pw_id: Device Password ID for NFC static handover or -1 if not + * used + * Returns: Status code (P2P_SC_*) + * + * This optional callback can be used to implement persistent reconnect + * by allowing automatic restarting of persistent groups without user + * interaction. If this callback is not implemented (i.e., is %NULL), + * the received Invitation Request frames are replied with + * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the + * invitation_result() callback. + * + * If the requested parameters are acceptable and the group is known, + * %P2P_SC_SUCCESS may be returned. If the requested group is unknown, + * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED + * can be returned if there is not enough data to provide immediate + * response, i.e., if some sort of user interaction is needed. The + * invitation_received() callback will be called in that case + * immediately after this call. + */ + u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid, + const u8 *go_dev_addr, const u8 *ssid, + size_t ssid_len, int *go, u8 *group_bssid, + int *force_freq, int persistent_group, + const struct p2p_channels *channels, + int dev_pw_id); + + /** + * invitation_received - Callback on Invitation Request RX + * @ctx: Callback context from cb_ctx + * @sa: Source address of the Invitation Request + * @bssid: P2P Group BSSID or %NULL if not received + * @ssid: SSID of the group + * @ssid_len: Length of ssid in octets + * @go_dev_addr: GO Device Address + * @status: Response Status + * @op_freq: Operational frequency for the group + * + * This callback is used to indicate sending of an Invitation Response + * for a received Invitation Request. If status == 0 (success), the + * upper layer code is responsible for starting the group. status == 1 + * indicates need to get user authorization for the group. Other status + * values indicate that the invitation request was rejected. + */ + void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid, + const u8 *ssid, size_t ssid_len, + const u8 *go_dev_addr, u8 status, + int op_freq); + + /** + * invitation_result - Callback on Invitation result + * @ctx: Callback context from cb_ctx + * @status: Negotiation result (Status Code) + * @bssid: P2P Group BSSID or %NULL if not received + * @channels: Available operating channels for the group + * @addr: Peer address + * @freq: Frequency (in MHz) indicated during invitation or 0 + * @peer_oper_freq: Operating frequency (in MHz) advertized by the peer + * during invitation or 0 + * + * This callback is used to indicate result of an Invitation procedure + * started with a call to p2p_invite(). The indicated status code is + * the value received from the peer in Invitation Response with 0 + * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a + * local failure in transmitting the Invitation Request. + */ + void (*invitation_result)(void *ctx, int status, const u8 *bssid, + const struct p2p_channels *channels, + const u8 *addr, int freq, int peer_oper_freq); + + /** + * go_connected - Check whether we are connected to a GO + * @ctx: Callback context from cb_ctx + * @dev_addr: P2P Device Address of a GO + * Returns: 1 if we are connected as a P2P client to the specified GO + * or 0 if not. + */ + int (*go_connected)(void *ctx, const u8 *dev_addr); + + /** + * presence_resp - Callback on Presence Response + * @ctx: Callback context from cb_ctx + * @src: Source address (GO's P2P Interface Address) + * @status: Result of the request (P2P_SC_*) + * @noa: Returned NoA value + * @noa_len: Length of the NoA buffer in octets + */ + void (*presence_resp)(void *ctx, const u8 *src, u8 status, + const u8 *noa, size_t noa_len); + + /** + * is_concurrent_session_active - Check whether concurrent session is + * active on other virtual interfaces + * @ctx: Callback context from cb_ctx + * Returns: 1 if concurrent session is active on other virtual interface + * or 0 if not. + */ + int (*is_concurrent_session_active)(void *ctx); + + /** + * is_p2p_in_progress - Check whether P2P operation is in progress + * @ctx: Callback context from cb_ctx + * Returns: 1 if P2P operation (e.g., group formation) is in progress + * or 0 if not. + */ + int (*is_p2p_in_progress)(void *ctx); + + /** + * Determine if we have a persistent group we share with remote peer + * and allocate interface for this group if needed + * @ctx: Callback context from cb_ctx + * @addr: Peer device address to search for + * @ssid: Persistent group SSID or %NULL if any + * @ssid_len: Length of @ssid + * @go_dev_addr: Buffer for returning GO P2P Device Address + * @ret_ssid: Buffer for returning group SSID + * @ret_ssid_len: Buffer for returning length of @ssid + * @intended_iface_addr: Buffer for returning intended iface address + * Returns: 1 if a matching persistent group was found, 0 otherwise + */ + int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid, + size_t ssid_len, u8 *go_dev_addr, + u8 *ret_ssid, size_t *ret_ssid_len, + u8 *intended_iface_addr); + + /** + * Get information about a possible local GO role + * @ctx: Callback context from cb_ctx + * @intended_addr: Buffer for returning intended GO interface address + * @ssid: Buffer for returning group SSID + * @ssid_len: Buffer for returning length of @ssid + * @group_iface: Buffer for returning whether a separate group interface + * would be used + * Returns: 1 if GO info found, 0 otherwise + * + * This is used to compose New Group settings (SSID, and intended + * address) during P2PS provisioning if results of provisioning *might* + * result in our being an autonomous GO. + */ + int (*get_go_info)(void *ctx, u8 *intended_addr, + u8 *ssid, size_t *ssid_len, int *group_iface); + + /** + * remove_stale_groups - Remove stale P2PS groups + * + * Because P2PS stages *potential* GOs, and remote devices can remove + * credentials unilaterally, we need to make sure we don't let stale + * unusable groups build up. + */ + int (*remove_stale_groups)(void *ctx, const u8 *peer, const u8 *go, + const u8 *ssid, size_t ssid_len); + + /** + * p2ps_prov_complete - P2PS provisioning complete + * + * When P2PS provisioning completes (successfully or not) we must + * transmit all of the results to the upper layers. + */ + void (*p2ps_prov_complete)(void *ctx, u8 status, const u8 *dev, + const u8 *adv_mac, const u8 *ses_mac, + const u8 *grp_mac, u32 adv_id, u32 ses_id, + u8 conncap, int passwd_id, + const u8 *persist_ssid, + size_t persist_ssid_size, int response_done, + int prov_start, const char *session_info, + const u8 *feat_cap, size_t feat_cap_len); + + /** + * prov_disc_resp_cb - Callback for indicating completion of PD Response + * @ctx: Callback context from cb_ctx + * Returns: 1 if operation was started, 0 otherwise + * + * This callback can be used to perform any pending actions after + * provisioning. It is mainly used for P2PS pending group creation. + */ + int (*prov_disc_resp_cb)(void *ctx); + + /** + * p2ps_group_capability - Determine group capability + * + * This function can be used to determine group capability based on + * information from P2PS PD exchange and the current state of ongoing + * groups and driver capabilities. + * + * P2PS_SETUP_* bitmap is used as the parameters and return value. + */ + u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role); + + /** + * get_pref_freq_list - Get preferred frequency list for an interface + * @ctx: Callback context from cb_ctx + * @go: Whether the use if for GO role + * @len: Length of freq_list in entries (both IN and OUT) + * @freq_list: Buffer for returning the preferred frequencies (MHz) + * Returns: 0 on success, -1 on failure + * + * This function can be used to query the preferred frequency list from + * the driver specific to a particular interface type. + */ + int (*get_pref_freq_list)(void *ctx, int go, + unsigned int *len, unsigned int *freq_list); +}; + + +/* P2P module initialization/deinitialization */ + +/** + * p2p_init - Initialize P2P module + * @cfg: P2P module configuration + * Returns: Pointer to private data or %NULL on failure + * + * This function is used to initialize global P2P module context (one per + * device). The P2P module will keep a copy of the configuration data, so the + * caller does not need to maintain this structure. However, the callback + * functions and the context parameters to them must be kept available until + * the P2P module is deinitialized with p2p_deinit(). + */ +struct p2p_data * p2p_init(const struct p2p_config *cfg); + +/** + * p2p_deinit - Deinitialize P2P module + * @p2p: P2P module context from p2p_init() + */ +void p2p_deinit(struct p2p_data *p2p); + +/** + * p2p_flush - Flush P2P module state + * @p2p: P2P module context from p2p_init() + * + * This command removes the P2P module state like peer device entries. + */ +void p2p_flush(struct p2p_data *p2p); + +/** + * p2p_unauthorize - Unauthorize the specified peer device + * @p2p: P2P module context from p2p_init() + * @addr: P2P peer entry to be unauthorized + * Returns: 0 on success, -1 on failure + * + * This command removes any connection authorization from the specified P2P + * peer device address. This can be used, e.g., to cancel effect of a previous + * p2p_authorize() or p2p_connect() call that has not yet resulted in completed + * GO Negotiation. + */ +int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_set_dev_name - Set device name + * @p2p: P2P module context from p2p_init() + * Returns: 0 on success, -1 on failure + * + * This function can be used to update the P2P module configuration with + * information that was not available at the time of the p2p_init() call. + */ +int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name); + +int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer); +int p2p_set_model_name(struct p2p_data *p2p, const char *model_name); +int p2p_set_model_number(struct p2p_data *p2p, const char *model_number); +int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number); + +void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods); +void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid); + +/** + * p2p_set_pri_dev_type - Set primary device type + * @p2p: P2P module context from p2p_init() + * Returns: 0 on success, -1 on failure + * + * This function can be used to update the P2P module configuration with + * information that was not available at the time of the p2p_init() call. + */ +int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type); + +/** + * p2p_set_sec_dev_types - Set secondary device types + * @p2p: P2P module context from p2p_init() + * Returns: 0 on success, -1 on failure + * + * This function can be used to update the P2P module configuration with + * information that was not available at the time of the p2p_init() call. + */ +int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8], + size_t num_dev_types); + +int p2p_set_country(struct p2p_data *p2p, const char *country); + + +/* Commands from upper layer management entity */ + +enum p2p_discovery_type { + P2P_FIND_START_WITH_FULL, + P2P_FIND_ONLY_SOCIAL, + P2P_FIND_PROGRESSIVE +}; + +/** + * p2p_find - Start P2P Find (Device Discovery) + * @p2p: P2P module context from p2p_init() + * @timeout: Timeout for find operation in seconds or 0 for no timeout + * @type: Device Discovery type + * @num_req_dev_types: Number of requested device types + * @req_dev_types: Requested device types array, must be an array + * containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no + * requested device types. + * @dev_id: Device ID to search for or %NULL to find all devices + * @search_delay: Extra delay in milliseconds between search iterations + * @seek_count: Number of ASP Service Strings in the seek_string array + * @seek_string: ASP Service Strings to query for in Probe Requests + * @freq: Requested first scan frequency (in MHz) to modify type == + * P2P_FIND_START_WITH_FULL behavior. 0 = Use normal full scan. + * If p2p_find is already in progress, this parameter is ignored and full + * scan will be executed. + * Returns: 0 on success, -1 on failure + */ +int p2p_find(struct p2p_data *p2p, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types, + const u8 *dev_id, unsigned int search_delay, + u8 seek_count, const char **seek_string, int freq); + +/** + * p2p_notify_scan_trigger_status - Indicate scan trigger status + * @p2p: P2P module context from p2p_init() + * @status: 0 on success, -1 on failure + */ +void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status); + +/** + * p2p_stop_find - Stop P2P Find (Device Discovery) + * @p2p: P2P module context from p2p_init() + */ +void p2p_stop_find(struct p2p_data *p2p); + +/** + * p2p_stop_find_for_freq - Stop P2P Find for next oper on specific freq + * @p2p: P2P module context from p2p_init() + * @freq: Frequency in MHz for next operation + * + * This is like p2p_stop_find(), but Listen state is not stopped if we are + * already on the same frequency. + */ +void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq); + +/** + * p2p_listen - Start P2P Listen state for specified duration + * @p2p: P2P module context from p2p_init() + * @timeout: Listen state duration in milliseconds + * Returns: 0 on success, -1 on failure + * + * This function can be used to request the P2P module to keep the device + * discoverable on the listen channel for an extended set of time. At least in + * its current form, this is mainly used for testing purposes and may not be of + * much use for normal P2P operations. + */ +int p2p_listen(struct p2p_data *p2p, unsigned int timeout); + +/** + * p2p_stop_listen - Stop P2P Listen + * @p2p: P2P module context from p2p_init() + */ +void p2p_stop_listen(struct p2p_data *p2p); + +/** + * p2p_connect - Start P2P group formation (GO negotiation) + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @wps_method: WPS method to be used in provisioning + * @go_intent: Local GO intent value (1..15) + * @own_interface_addr: Intended interface address to use with the group + * @force_freq: The only allowed channel frequency in MHz or 0 + * @persistent_group: Whether to create a persistent group (0 = no, 1 = + * persistent group without persistent reconnect, 2 = persistent group with + * persistent reconnect) + * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate + * a new SSID + * @force_ssid_len: Length of $force_ssid buffer + * @pd_before_go_neg: Whether to send Provision Discovery prior to GO + * Negotiation as an interoperability workaround when initiating group + * formation + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if + * force_freq == 0) + * Returns: 0 on success, -1 on failure + */ +int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id); + +/** + * p2p_authorize - Authorize P2P group formation (GO negotiation) + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @wps_method: WPS method to be used in provisioning + * @go_intent: Local GO intent value (1..15) + * @own_interface_addr: Intended interface address to use with the group + * @force_freq: The only allowed channel frequency in MHz or 0 + * @persistent_group: Whether to create a persistent group (0 = no, 1 = + * persistent group without persistent reconnect, 2 = persistent group with + * persistent reconnect) + * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate + * a new SSID + * @force_ssid_len: Length of $force_ssid buffer + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if + * force_freq == 0) + * Returns: 0 on success, -1 on failure + * + * This is like p2p_connect(), but the actual group negotiation is not + * initiated automatically, i.e., the other end is expected to do that. + */ +int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + unsigned int pref_freq, u16 oob_pw_id); + +/** + * p2p_reject - Reject peer device (explicitly block connection attempts) + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * Returns: 0 on success, -1 on failure + */ +int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr); + +/** + * p2p_prov_disc_req - Send Provision Discovery Request + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @p2ps_prov: Provisioning info for P2PS + * @config_methods: WPS Config Methods value (only one bit set) + * @join: Whether this is used by a client joining an active group + * @force_freq: Forced TX frequency for the frame (mainly for the join case) + * @user_initiated_pd: Flag to indicate if initiated by user or not + * Returns: 0 on success, -1 on failure + * + * This function can be used to request a discovered P2P peer to display a PIN + * (config_methods = WPS_CONFIG_DISPLAY) or be prepared to enter a PIN from us + * (config_methods = WPS_CONFIG_KEYPAD). The Provision Discovery Request frame + * is transmitted once immediately and if no response is received, the frame + * will be sent again whenever the target device is discovered during device + * dsicovery (start with a p2p_find() call). Response from the peer is + * indicated with the p2p_config::prov_disc_resp() callback. + */ +int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, + struct p2ps_provision *p2ps_prov, u16 config_methods, + int join, int force_freq, + int user_initiated_pd); + +/** + * p2p_sd_request - Schedule a service discovery query + * @p2p: P2P module context from p2p_init() + * @dst: Destination peer or %NULL to apply for all peers + * @tlvs: P2P Service Query TLV(s) + * Returns: Reference to the query or %NULL on failure + * + * Response to the query is indicated with the p2p_config::sd_response() + * callback. + */ +void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs); + +#ifdef CONFIG_WIFI_DISPLAY +void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs); +#endif /* CONFIG_WIFI_DISPLAY */ + +/** + * p2p_sd_cancel_request - Cancel a pending service discovery query + * @p2p: P2P module context from p2p_init() + * @req: Query reference from p2p_sd_request() + * Returns: 0 if request for cancelled; -1 if not found + */ +int p2p_sd_cancel_request(struct p2p_data *p2p, void *req); + +/** + * p2p_sd_response - Send response to a service discovery query + * @p2p: P2P module context from p2p_init() + * @freq: Frequency from p2p_config::sd_request() callback + * @dst: Destination address from p2p_config::sd_request() callback + * @dialog_token: Dialog token from p2p_config::sd_request() callback + * @resp_tlvs: P2P Service Response TLV(s) + * + * This function is called as a response to the request indicated with + * p2p_config::sd_request() callback. + */ +void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, + u8 dialog_token, const struct wpabuf *resp_tlvs); + +/** + * p2p_sd_service_update - Indicate a change in local services + * @p2p: P2P module context from p2p_init() + * + * This function needs to be called whenever there is a change in availability + * of the local services. This will increment the Service Update Indicator + * value which will be used in SD Request and Response frames. + */ +void p2p_sd_service_update(struct p2p_data *p2p); + + +enum p2p_invite_role { + P2P_INVITE_ROLE_GO, + P2P_INVITE_ROLE_ACTIVE_GO, + P2P_INVITE_ROLE_CLIENT +}; + +/** + * p2p_invite - Invite a P2P Device into a group + * @p2p: P2P module context from p2p_init() + * @peer: Device Address of the peer P2P Device + * @role: Local role in the group + * @bssid: Group BSSID or %NULL if not known + * @ssid: Group SSID + * @ssid_len: Length of ssid in octets + * @force_freq: The only allowed channel frequency in MHz or 0 + * @go_dev_addr: Forced GO Device Address or %NULL if none + * @persistent_group: Whether this is to reinvoke a persistent group + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if + * force_freq == 0) + * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover + * case or -1 if not used + * Returns: 0 on success, -1 on failure + */ +int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + unsigned int force_freq, const u8 *go_dev_addr, + int persistent_group, unsigned int pref_freq, int dev_pw_id); + +/** + * p2p_presence_req - Request GO presence + * @p2p: P2P module context from p2p_init() + * @go_interface_addr: GO P2P Interface Address + * @own_interface_addr: Own P2P Interface Address for this group + * @freq: Group operating frequence (in MHz) + * @duration1: Preferred presence duration in microseconds + * @interval1: Preferred presence interval in microseconds + * @duration2: Acceptable presence duration in microseconds + * @interval2: Acceptable presence interval in microseconds + * Returns: 0 on success, -1 on failure + * + * If both duration and interval values are zero, the parameter pair is not + * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0). + */ +int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, + const u8 *own_interface_addr, unsigned int freq, + u32 duration1, u32 interval1, u32 duration2, + u32 interval2); + +/** + * p2p_ext_listen - Set Extended Listen Timing + * @p2p: P2P module context from p2p_init() + * @freq: Group operating frequence (in MHz) + * @period: Availability period in milliseconds (1-65535; 0 to disable) + * @interval: Availability interval in milliseconds (1-65535; 0 to disable) + * Returns: 0 on success, -1 on failure + * + * This function can be used to enable or disable (period = interval = 0) + * Extended Listen Timing. When enabled, the P2P Device will become + * discoverable (go into Listen State) every @interval milliseconds for at + * least @period milliseconds. + */ +int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, + unsigned int interval); + +/* Event notifications from upper layer management operations */ + +/** + * p2p_wps_success_cb - Report successfully completed WPS provisioning + * @p2p: P2P module context from p2p_init() + * @mac_addr: Peer address + * + * This function is used to report successfully completed WPS provisioning + * during group formation in both GO/Registrar and client/Enrollee roles. + */ +void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr); + +/** + * p2p_group_formation_failed - Report failed WPS provisioning + * @p2p: P2P module context from p2p_init() + * + * This function is used to report failed group formation. This can happen + * either due to failed WPS provisioning or due to 15 second timeout during + * the provisioning phase. + */ +void p2p_group_formation_failed(struct p2p_data *p2p); + +/** + * p2p_get_provisioning_info - Get any stored provisioning info + * @p2p: P2P module context from p2p_init() + * @addr: Peer P2P Device Address + * Returns: WPS provisioning information (WPS config method) or 0 if no + * information is available + * + * This function is used to retrieve stored WPS provisioning info for the given + * peer. + */ +u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_clear_provisioning_info - Clear any stored provisioning info + * @p2p: P2P module context from p2p_init() + * @iface_addr: Peer P2P Device Address + * + * This function is used to clear stored WPS provisioning info for the given + * peer. + */ +void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr); + + +/* Event notifications from lower layer driver operations */ + +/** + * enum p2p_probe_req_status + * + * @P2P_PREQ_MALFORMED: frame was not well-formed + * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored + * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request + * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed + * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P + */ +enum p2p_probe_req_status { + P2P_PREQ_MALFORMED, + P2P_PREQ_NOT_LISTEN, + P2P_PREQ_NOT_P2P, + P2P_PREQ_NOT_PROCESSED, + P2P_PREQ_PROCESSED +}; + +/** + * p2p_probe_req_rx - Report reception of a Probe Request frame + * @p2p: P2P module context from p2p_init() + * @addr: Source MAC address + * @dst: Destination MAC address if available or %NULL + * @bssid: BSSID if available or %NULL + * @ie: Information elements from the Probe Request frame body + * @ie_len: Length of ie buffer in octets + * @rx_freq: Probe Request frame RX frequency + * Returns: value indicating the type and status of the probe request + */ +enum p2p_probe_req_status +p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len, + unsigned int rx_freq); + +/** + * p2p_rx_action - Report received Action frame + * @p2p: P2P module context from p2p_init() + * @da: Destination address of the received Action frame + * @sa: Source address of the received Action frame + * @bssid: Address 3 of the received Action frame + * @category: Category of the received Action frame + * @data: Action frame body after the Category field + * @len: Length of the data buffer in octets + * @freq: Frequency (in MHz) on which the frame was received + */ +void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *bssid, u8 category, + const u8 *data, size_t len, int freq); + +/** + * p2p_scan_res_handler - Indicate a P2P scan results + * @p2p: P2P module context from p2p_init() + * @bssid: BSSID of the scan result + * @freq: Frequency of the channel on which the device was found in MHz + * @rx_time: Time when the result was received + * @level: Signal level (signal strength of the received Beacon/Probe Response + * frame) + * @ies: Pointer to IEs from the scan result + * @ies_len: Length of the ies buffer + * Returns: 0 to continue or 1 to stop scan result indication + * + * This function is called to indicate a scan result entry with P2P IE from a + * scan requested with struct p2p_config::p2p_scan(). This can be called during + * the actual scan process (i.e., whenever a new device is found) or as a + * sequence of calls after the full scan has been completed. The former option + * can result in optimized operations, but may not be supported by all + * driver/firmware designs. The ies buffer need to include at least the P2P IE, + * but it is recommended to include all IEs received from the device. The + * caller does not need to check that the IEs contain a P2P IE before calling + * this function since frames will be filtered internally if needed. + * + * This function will return 1 if it wants to stop scan result iteration (and + * scan in general if it is still in progress). This is used to allow faster + * start of a pending operation, e.g., to start a pending GO negotiation. + */ +int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, + struct os_reltime *rx_time, int level, const u8 *ies, + size_t ies_len); + +/** + * p2p_scan_res_handled - Indicate end of scan results + * @p2p: P2P module context from p2p_init() + * + * This function is called to indicate that all P2P scan results from a scan + * have been reported with zero or more calls to p2p_scan_res_handler(). This + * function must be called as a response to successful + * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler() + * calls stopped iteration. + */ +void p2p_scan_res_handled(struct p2p_data *p2p); + +enum p2p_send_action_result { + P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */, + P2P_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged */, + P2P_SEND_ACTION_FAILED /* Frame was not sent due to a failure */ +}; + +/** + * p2p_send_action_cb - Notify TX status of an Action frame + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * @dst: Destination MAC address (Address 1) + * @src: Source MAC address (Address 2) + * @bssid: BSSID (Address 3) + * @result: Result of the transmission attempt + * + * This function is used to indicate the result of an Action frame transmission + * that was requested with struct p2p_config::send_action() callback. + */ +void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + enum p2p_send_action_result result); + +/** + * p2p_listen_cb - Indicate the start of a requested Listen state + * @p2p: P2P module context from p2p_init() + * @freq: Listen channel frequency in MHz + * @duration: Duration for the Listen state in milliseconds + * + * This function is used to indicate that a Listen state requested with + * struct p2p_config::start_listen() callback has started. + */ +void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, + unsigned int duration); + +/** + * p2p_listen_end - Indicate the end of a requested Listen state + * @p2p: P2P module context from p2p_init() + * @freq: Listen channel frequency in MHz + * Returns: 0 if no operations were started, 1 if an operation was started + * + * This function is used to indicate that a Listen state requested with + * struct p2p_config::start_listen() callback has ended. + */ +int p2p_listen_end(struct p2p_data *p2p, unsigned int freq); + +void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len); + +void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len); + + +/* Per-group P2P state for GO */ + +struct p2p_group; + +/** + * struct p2p_group_config - P2P group configuration + * + * This configuration is provided to the P2P module during initialization of + * the per-group information with p2p_group_init(). + */ +struct p2p_group_config { + /** + * persistent_group - Whether the group is persistent + * 0 = not a persistent group + * 1 = persistent group without persistent reconnect + * 2 = persistent group with persistent reconnect + */ + int persistent_group; + + /** + * interface_addr - P2P Interface Address of the group + */ + u8 interface_addr[ETH_ALEN]; + + /** + * max_clients - Maximum number of clients in the group + */ + unsigned int max_clients; + + /** + * ssid - Group SSID + */ + u8 ssid[SSID_MAX_LEN]; + + /** + * ssid_len - Length of SSID + */ + size_t ssid_len; + + /** + * freq - Operating channel of the group + */ + int freq; + + /** + * cb_ctx - Context to use with callback functions + */ + void *cb_ctx; + + /** + * ie_update - Notification of IE update + * @ctx: Callback context from cb_ctx + * @beacon_ies: P2P IE for Beacon frames or %NULL if no change + * @proberesp_ies: P2P Ie for Probe Response frames + * + * P2P module uses this callback function to notify whenever the P2P IE + * in Beacon or Probe Response frames should be updated based on group + * events. + * + * The callee is responsible for freeing the returned buffer(s) with + * wpabuf_free(). + */ + void (*ie_update)(void *ctx, struct wpabuf *beacon_ies, + struct wpabuf *proberesp_ies); + + /** + * idle_update - Notification of changes in group idle state + * @ctx: Callback context from cb_ctx + * @idle: Whether the group is idle (no associated stations) + */ + void (*idle_update)(void *ctx, int idle); +}; + +/** + * p2p_group_init - Initialize P2P group + * @p2p: P2P module context from p2p_init() + * @config: P2P group configuration (will be freed by p2p_group_deinit()) + * Returns: Pointer to private data or %NULL on failure + * + * This function is used to initialize per-group P2P module context. Currently, + * this is only used to manage GO functionality and P2P clients do not need to + * create an instance of this per-group information. + */ +struct p2p_group * p2p_group_init(struct p2p_data *p2p, + struct p2p_group_config *config); + +/** + * p2p_group_deinit - Deinitialize P2P group + * @group: P2P group context from p2p_group_init() + */ +void p2p_group_deinit(struct p2p_group *group); + +/** + * p2p_group_notif_assoc - Notification of P2P client association with GO + * @group: P2P group context from p2p_group_init() + * @addr: Interface address of the P2P client + * @ie: IEs from the (Re)association Request frame + * @len: Length of the ie buffer in octets + * Returns: 0 on success, -1 on failure + */ +int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, + const u8 *ie, size_t len); + +/** + * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response + * @group: P2P group context from p2p_group_init() + * @status: Status value (P2P_SC_SUCCESS if association succeeded) + * Returns: P2P IE for (Re)association Response or %NULL on failure + * + * The caller is responsible for freeing the returned buffer with + * wpabuf_free(). + */ +struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status); + +/** + * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO + * @group: P2P group context from p2p_group_init() + * @addr: Interface address of the P2P client + */ +void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr); + +/** + * p2p_group_notif_formation_done - Notification of completed group formation + * @group: P2P group context from p2p_group_init() + */ +void p2p_group_notif_formation_done(struct p2p_group *group); + +/** + * p2p_group_notif_noa - Notification of NoA change + * @group: P2P group context from p2p_group_init() + * @noa: Notice of Absence attribute payload, %NULL if none + * @noa_len: Length of noa buffer in octets + * Returns: 0 on success, -1 on failure + * + * Notify the P2P group management about a new NoA contents. This will be + * inserted into the P2P IEs in Beacon and Probe Response frames with rest of + * the group information. + */ +int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa, + size_t noa_len); + +/** + * p2p_group_match_dev_type - Match device types in group with requested type + * @group: P2P group context from p2p_group_init() + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) + * Returns: 1 on match, 0 on mismatch + * + * This function can be used to match the Requested Device Type attribute in + * WPS IE with the device types of a group member for deciding whether a GO + * should reply to a Probe Request frame. Match will be reported if the WPS IE + * is not requested any specific device type. + */ +int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps); + +/** + * p2p_group_match_dev_id - Match P2P Device Address in group with requested device id + */ +int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p); + +/** + * p2p_group_go_discover - Send GO Discoverability Request to a group client + * @group: P2P group context from p2p_group_init() + * Returns: 0 on success (frame scheduled); -1 if client was not found + */ +int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, + const u8 *searching_dev, int rx_freq); + + +/* Generic helper functions */ + +/** + * p2p_ie_text - Build text format description of P2P IE + * @p2p_ie: P2P IE + * @buf: Buffer for returning text + * @end: Pointer to the end of the buf area + * Returns: Number of octets written to the buffer or -1 on failure + * + * This function can be used to parse P2P IE contents into text format + * field=value lines. + */ +int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end); + +/** + * p2p_scan_result_text - Build text format description of P2P IE + * @ies: Information elements from scan results + * @ies_len: ies buffer length in octets + * @buf: Buffer for returning text + * @end: Pointer to the end of the buf area + * Returns: Number of octets written to the buffer or -1 on failure + * + * This function can be used to parse P2P IE contents into text format + * field=value lines. + */ +int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end); + +/** + * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated + * P2P IE + * @p2p_ie: P2P IE + * @dev_addr: Buffer for returning P2P Device Address + * Returns: 0 on success or -1 if P2P Device Address could not be parsed + */ +int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr); + +/** + * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s) + * @ies: Information elements from scan results + * @ies_len: ies buffer length in octets + * @dev_addr: Buffer for returning P2P Device Address + * Returns: 0 on success or -1 if P2P Device Address could not be parsed + */ +int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr); + +/** + * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame + * @p2p: P2P module context from p2p_init() + * @bssid: BSSID + * @buf: Buffer for writing the P2P IE + * @len: Maximum buf length in octets + * @p2p_group: Whether this is for association with a P2P GO + * @p2p_ie: Reassembled P2P IE data from scan results or %NULL if none + * Returns: Number of octets written into buf or -1 on failure + */ +int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, + size_t len, int p2p_group, struct wpabuf *p2p_ie); + +/** + * p2p_scan_ie - Build P2P IE for Probe Request + * @p2p: P2P module context from p2p_init() + * @ies: Buffer for writing P2P IE + * @dev_id: Device ID to search for or %NULL for any + */ +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id); + +/** + * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie + * @p2p: P2P module context from p2p_init() + * Returns: Number of octets that p2p_scan_ie() may add to the buffer + */ +size_t p2p_scan_ie_buf_len(struct p2p_data *p2p); + +/** + * p2p_go_params - Generate random P2P group parameters + * @p2p: P2P module context from p2p_init() + * @params: Buffer for parameters + * Returns: 0 on success, -1 on failure + */ +int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params); + +/** + * p2p_get_group_capab - Get Group Capability from P2P IE data + * @p2p_ie: P2P IE(s) contents + * Returns: Group Capability + */ +u8 p2p_get_group_capab(const struct wpabuf *p2p_ie); + +/** + * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection + * @p2p_ie: P2P IE(s) contents + * Returns: 0 if cross connection is allow, 1 if not + */ +int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie); + +/** + * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data + * @p2p_ie: P2P IE(s) contents + * Returns: Pointer to P2P Device Address or %NULL if not included + */ +const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie); + +/** + * p2p_get_peer_info - Get P2P peer information + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer or %NULL to indicate the first peer + * @next: Whether to select the peer entry following the one indicated by addr + * Returns: Pointer to peer info or %NULL if not found + */ +const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p, + const u8 *addr, int next); + +/** + * p2p_get_peer_info_txt - Get internal P2P peer information in text format + * @info: Pointer to P2P peer info from p2p_get_peer_info() + * @buf: Buffer for returning text + * @buflen: Maximum buffer length + * Returns: Number of octets written to the buffer or -1 on failure + * + * Note: This information is internal to the P2P module and subject to change. + * As such, this should not really be used by external programs for purposes + * other than debugging. + */ +int p2p_get_peer_info_txt(const struct p2p_peer_info *info, + char *buf, size_t buflen); + +/** + * p2p_peer_known - Check whether P2P peer is known + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer + * Returns: 1 if the specified device is in the P2P peer table or 0 if not + */ +int p2p_peer_known(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_set_client_discoverability - Set client discoverability capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether client discoverability will be enabled + * + * This function can be used to disable (and re-enable) client discoverability. + * This capability is enabled by default and should not be disabled in normal + * use cases, i.e., this is mainly for testing purposes. + */ +void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled); + +/** + * p2p_set_managed_oper - Set managed P2P Device operations capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether managed P2P Device operations will be enabled + */ +void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); + +/** + * p2p_config_get_random_social - Return a random social channel + * @p2p: P2P config + * @op_class: Selected operating class + * @op_channel: Selected social channel + * Returns: 0 on success, -1 on failure + * + * This function is used before p2p_init is called. A random social channel + * from supports bands 2.4 GHz (channels 1,6,11) and 60 GHz (channel 2) is + * returned on success. + */ +int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, + u8 *op_channel); + +int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, + u8 forced); + +u8 p2p_get_listen_channel(struct p2p_data *p2p); + +int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len); + +int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, + u8 *iface_addr); +int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, + u8 *dev_addr); + +void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_set_cross_connect - Set cross connection capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether cross connection will be enabled + */ +void p2p_set_cross_connect(struct p2p_data *p2p, int enabled); + +int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr); + +/** + * p2p_set_intra_bss_dist - Set intra BSS distribution + * @p2p: P2P module context from p2p_init() + * @enabled: Whether intra BSS distribution will be enabled + */ +void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled); + +int p2p_channels_includes_freq(const struct p2p_channels *channels, + unsigned int freq); + +int p2p_channels_to_freqs(const struct p2p_channels *channels, + int *freq_list, unsigned int max_len); + +/** + * p2p_supported_freq - Check whether channel is supported for P2P + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq); + +/** + * p2p_supported_freq_go - Check whether channel is supported for P2P GO operation + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq); + +/** + * p2p_supported_freq_cli - Check whether channel is supported for P2P client operation + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq); + +/** + * p2p_get_pref_freq - Get channel from preferred channel list + * @p2p: P2P module context from p2p_init() + * @channels: List of channels + * Returns: Preferred channel + */ +unsigned int p2p_get_pref_freq(struct p2p_data *p2p, + const struct p2p_channels *channels); + +void p2p_update_channel_list(struct p2p_data *p2p, + const struct p2p_channels *chan, + const struct p2p_channels *cli_chan); + +/** + * p2p_set_best_channels - Update best channel information + * @p2p: P2P module context from p2p_init() + * @freq_24: Frequency (MHz) of best channel in 2.4 GHz band + * @freq_5: Frequency (MHz) of best channel in 5 GHz band + * @freq_overall: Frequency (MHz) of best channel overall + */ +void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, + int freq_overall); + +/** + * p2p_set_own_freq_preference - Set own preference for channel + * @p2p: P2P module context from p2p_init() + * @freq: Frequency (MHz) of the preferred channel or 0 if no preference + * + * This function can be used to set a preference on the operating channel based + * on frequencies used on the other virtual interfaces that share the same + * radio. If non-zero, this is used to try to avoid multi-channel concurrency. + */ +void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq); + +const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p); + +/** + * p2p_get_group_num_members - Get number of members in group + * @group: P2P group context from p2p_group_init() + * Returns: Number of members in the group + */ +unsigned int p2p_get_group_num_members(struct p2p_group *group); + +/** + * p2p_client_limit_reached - Check if client limit is reached + * @group: P2P group context from p2p_group_init() + * Returns: 1 if no of clients limit reached + */ +int p2p_client_limit_reached(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 Device Address for each call and %NULL for no more members + */ +const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next); + +/** + * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group + * @group: P2P group context from p2p_group_init() + * @addr: P2P Interface Address of the client + * Returns: P2P Device Address of the client if found or %NULL if no match + * found + */ +const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr); + +/** + * p2p_group_is_client_connected - Check whether a specific client is connected + * @group: P2P group context from p2p_group_init() + * @addr: P2P Device Address of the client + * Returns: 1 if client is connected or 0 if not + */ +int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr); + +/** + * p2p_group_get_config - Get the group configuration + * @group: P2P group context from p2p_group_init() + * Returns: The group configuration pointer + */ +const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group); + +/** + * p2p_loop_on_all_groups - Run the given callback on all groups + * @p2p: P2P module context from p2p_init() + * @group_callback: The callback function pointer + * @user_data: Some user data pointer which can be %NULL + * + * The group_callback function can stop the iteration by returning 0. + */ +void p2p_loop_on_all_groups(struct p2p_data *p2p, + int (*group_callback)(struct p2p_group *group, + void *user_data), + void *user_data); + +/** + * p2p_get_peer_found - Get P2P peer info structure of a found peer + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer or %NULL to indicate the first peer + * @next: Whether to select the peer entry following the one indicated by addr + * Returns: The first P2P peer info available or %NULL if no such peer exists + */ +const struct p2p_peer_info * +p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next); + +/** + * p2p_remove_wps_vendor_extensions - Remove WPS vendor extensions + * @p2p: P2P module context from p2p_init() + */ +void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p); + +/** + * p2p_add_wps_vendor_extension - Add a WPS vendor extension + * @p2p: P2P module context from p2p_init() + * @vendor_ext: The vendor extensions to add + * Returns: 0 on success, -1 on failure + * + * The wpabuf structures in the array are owned by the P2P + * module after this call. + */ +int p2p_add_wps_vendor_extension(struct p2p_data *p2p, + const struct wpabuf *vendor_ext); + +/** + * p2p_set_oper_channel - Set the P2P operating channel + * @p2p: P2P module context from p2p_init() + * @op_reg_class: Operating regulatory class to set + * @op_channel: operating channel to set + * @cfg_op_channel : Whether op_channel is hardcoded in configuration + * Returns: 0 on success, -1 on failure + */ +int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, + int cfg_op_channel); + +/** + * p2p_set_pref_chan - Set P2P preferred channel list + * @p2p: P2P module context from p2p_init() + * @num_pref_chan: Number of entries in pref_chan list + * @pref_chan: Preferred channels or %NULL to remove preferences + * Returns: 0 on success, -1 on failure + */ +int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, + const struct p2p_channel *pref_chan); + +/** + * p2p_set_no_go_freq - Set no GO channel ranges + * @p2p: P2P module context from p2p_init() + * @list: Channel ranges or %NULL to remove restriction + * Returns: 0 on success, -1 on failure + */ +int p2p_set_no_go_freq(struct p2p_data *p2p, + const struct wpa_freq_range_list *list); + +/** + * p2p_in_progress - Check whether a P2P operation is progress + * @p2p: P2P module context from p2p_init() + * Returns: 0 if P2P module is idle, 1 if an operation is in progress but not + * in search state, or 2 if search state operation is in progress + */ +int p2p_in_progress(struct p2p_data *p2p); + +const char * p2p_wps_method_text(enum p2p_wps_method method); + +/** + * p2p_set_config_timeout - Set local config timeouts + * @p2p: P2P module context from p2p_init() + * @go_timeout: Time in 10 ms units it takes to start the GO mode + * @client_timeout: Time in 10 ms units it takes to start the client mode + */ +void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, + u8 client_timeout); + +int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, + const struct wpabuf *elem); +struct wpabuf * wifi_display_encaps(struct wpabuf *subelems); + +/** + * p2p_set_disc_int - Set min/max discoverable interval for p2p_find + * @p2p: P2P module context from p2p_init() + * @min_disc_int: minDiscoverableInterval (in units of 100 TU); default 1 + * @max_disc_int: maxDiscoverableInterval (in units of 100 TU); default 3 + * @max_disc_tu: Maximum number of TUs (1.024 ms) for discoverable interval; or + * -1 not to limit + * Returns: 0 on success, or -1 on failure + * + * This function can be used to configure minDiscoverableInterval and + * maxDiscoverableInterval parameters for the Listen state during device + * discovery (p2p_find). A random number of 100 TU units is picked for each + * Listen state iteration from [min_disc_int,max_disc_int] range. + * + * max_disc_tu can be used to futher limit the discoverable duration. However, + * it should be noted that use of this parameter is not recommended since it + * would not be compliant with the P2P specification. + */ +int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, + int max_disc_tu); + +/** + * p2p_get_state_txt - Get current P2P state for debug purposes + * @p2p: P2P module context from p2p_init() + * Returns: Name of the current P2P module state + * + * It should be noted that the P2P module state names are internal information + * and subject to change at any point, i.e., this information should be used + * mainly for debugging purposes. + */ +const char * p2p_get_state_txt(struct p2p_data *p2p); + +struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len); +struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len); + +struct p2p_nfc_params { + int sel; + const u8 *wsc_attr; + size_t wsc_len; + const u8 *p2p_attr; + size_t p2p_len; + + enum { + NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG, + BOTH_GO, PEER_CLIENT + } next_step; + struct p2p_peer_info *peer; + u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN]; + size_t oob_dev_pw_len; + int go_freq; + u8 go_dev_addr[ETH_ALEN]; + u8 go_ssid[SSID_MAX_LEN]; + size_t go_ssid_len; +}; + +int p2p_process_nfc_connection_handover(struct p2p_data *p2p, + struct p2p_nfc_params *params); + +void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id, + int go_intent, + const u8 *own_interface_addr); + +int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len); + +void p2p_loop_on_known_peers(struct p2p_data *p2p, + void (*peer_callback)(struct p2p_peer_info *peer, + void *user_data), + void *user_data); + +void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem); + +void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr); + +struct p2ps_advertisement * +p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id); +int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id, + const char *adv_str, u8 svc_state, + u16 config_methods, const char *svc_info, + const u8 *cpt_priority); +int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id); +void p2p_service_flush_asp(struct p2p_data *p2p); +struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p); + +/** + * p2p_expire_peers - Periodic cleanup function to expire peers + * @p2p: P2P module context from p2p_init() + * + * This is a cleanup function that the entity calling p2p_init() is + * expected to call periodically to clean up expired peer entries. + */ +void p2p_expire_peers(struct p2p_data *p2p); + +void p2p_set_own_pref_freq_list(struct p2p_data *p2p, + const unsigned int *pref_freq_list, + unsigned int size); + +/** + * p2p_group_get_common_freqs - Get the group common frequencies + * @group: P2P group context from p2p_group_init() + * @common_freqs: On return will hold the group common frequencies + * @num: On return will hold the number of group common frequencies + * Returns: 0 on success, -1 otherwise + */ +int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, + unsigned int *num); + +#endif /* P2P_H */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/peerkey.c b/freebsd/contrib/wpa/src/rsn_supp/peerkey.c new file mode 100644 index 00000000..87263f86 --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/peerkey.c @@ -0,0 +1,1157 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * WPA Supplicant - PeerKey for Direct Link Setup (DLS) + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#ifdef CONFIG_PEERKEY + +#include "common.h" +#include "eloop.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" +#include "wpa.h" +#include "wpa_i.h" +#include "wpa_ie.h" +#include "peerkey.h" + + +static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len) +{ + os_memcpy(pos, ie, ie_len); + return pos + ie_len; +} + + +static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len) +{ + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + data_len; + RSN_SELECTOR_PUT(pos, kde); + pos += RSN_SELECTOR_LEN; + os_memcpy(pos, data, data_len); + pos += data_len; + return pos; +} + + +static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx) +{ +#if 0 + struct wpa_sm *sm = eloop_ctx; + struct wpa_peerkey *peerkey = timeout_ctx; +#endif + /* TODO: time out SMK and any STK that was generated using this SMK */ +} + + +static void wpa_supplicant_peerkey_free(struct wpa_sm *sm, + struct wpa_peerkey *peerkey) +{ + eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); + os_free(peerkey); +} + + +static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, + const u8 *peer, + u16 mui, u16 error_type, int ver) +{ + size_t rlen; + struct wpa_eapol_key *err; + struct wpa_eapol_key_192 *err192; + 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; + err192 = (struct wpa_eapol_key_192 *) err; + + 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, sm->ptk.kck_len, ver, dst, + ETH_P_EAPOL, rbuf, rlen, err192->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; + struct wpa_eapol_key_192 *reply192; + 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; + reply192 = (struct wpa_eapol_key_192 *) reply; + + 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, sm->ptk.kck_len, ver, src_addr, + ETH_P_EAPOL, rbuf, rlen, reply192->key_mic); + + return 0; +} + + +static int wpa_supplicant_process_smk_m2( + struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, size_t extra_len, int ver) +{ + struct wpa_peerkey *peerkey; + struct wpa_eapol_ie_parse kde; + struct wpa_ie_data ie; + int cipher; + struct rsn_ie_hdr *hdr; + u8 *pos; + + wpa_printf(MSG_DEBUG, "RSN: Received SMK M2"); + + if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { + wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for " + "the current network"); + return -1; + } + + if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < + 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2"); + return -1; + } + + if (kde.rsn_ie == NULL || kde.mac_addr == NULL || + kde.mac_addr_len < ETH_ALEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " + "SMK M2"); + return -1; + } + + wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR, + MAC2STR(kde.mac_addr)); + + if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) { + wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK " + "M2"); + return -1; + } + + if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2"); + return -1; + } + + cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher & + sm->allowed_pairwise_cipher, 0); + if (cipher < 0) { + wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2"); + wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr, + STK_MUI_SMK, STK_ERR_CPHR_NS, + ver); + return -1; + } + wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", + wpa_cipher_txt(cipher)); + + /* TODO: find existing entry and if found, use that instead of adding + * a new one; how to handle the case where both ends initiate at the + * same time? */ + peerkey = os_zalloc(sizeof(*peerkey)); + if (peerkey == NULL) + return -1; + os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN); + os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); + os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); + peerkey->rsnie_i_len = kde.rsn_ie_len; + peerkey->cipher = cipher; + peerkey->akmp = ie.key_mgmt; + + if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to get random data for PNonce"); + wpa_supplicant_peerkey_free(sm, peerkey); + return -1; + } + + hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + /* Group Suite can be anything for SMK RSN IE; receiver will just + * ignore it. */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + /* Include only the selected cipher in pairwise cipher suite */ + WPA_PUT_LE16(pos, 1); + pos += 2; + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher)); + pos += RSN_SELECTOR_LEN; + + hdr->len = (pos - peerkey->rsnie_p) - 2; + peerkey->rsnie_p_len = pos - peerkey->rsnie_p; + wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", + peerkey->rsnie_p, peerkey->rsnie_p_len); + + wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey); + + peerkey->next = sm->peerkey; + sm->peerkey = peerkey; + + return 0; +} + + +/** + * rsn_smkid - Derive SMK identifier + * @smk: Station master key (32 bytes) + * @pnonce: Peer Nonce + * @mac_p: Peer MAC address + * @inonce: Initiator Nonce + * @mac_i: Initiator MAC address + * @akmp: Negotiated AKM + * + * 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 akmp) +{ + 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 (wpa_key_mgmt_sha256(akmp)) + hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash); + else +#endif /* CONFIG_IEEE80211W */ + hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash); + os_memcpy(smkid, hash, PMKID_LEN); +} + + +static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey) +{ + size_t mlen; + struct wpa_eapol_key *msg; + u8 *mbuf; + size_t kde_len; + u16 key_info, ver; + + kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; + + mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*msg) + kde_len, &mlen, + (void *) &msg); + if (mbuf == NULL) + return; + + msg->type = EAPOL_KEY_TYPE_RSN; + + if (peerkey->cipher != WPA_CIPHER_TKIP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK; + WPA_PUT_BE16(msg->key_info, key_info); + + if (peerkey->cipher != WPA_CIPHER_TKIP) + WPA_PUT_BE16(msg->key_length, 16); + else + WPA_PUT_BE16(msg->key_length, 32); + + os_memcpy(msg->replay_counter, peerkey->replay_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(msg->key_data_length, kde_len); + wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID, + peerkey->smkid, PMKID_LEN); + + if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSN: Failed to get random data for INonce (STK)"); + os_free(mbuf); + return; + } + wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake", + peerkey->inonce, WPA_NONCE_LEN); + os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); + + wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR, + MAC2STR(peerkey->addr)); + wpa_eapol_key_send(sm, NULL, 0, ver, peerkey->addr, ETH_P_EAPOL, + mbuf, mlen, NULL); +} + + +static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey) +{ + size_t mlen; + struct wpa_eapol_key *msg; + u8 *mbuf, *pos; + size_t kde_len; + u16 key_info, ver; + be32 lifetime; + + kde_len = peerkey->rsnie_i_len + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + + mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*msg) + kde_len, &mlen, + (void *) &msg); + if (mbuf == NULL) + return; + + msg->type = EAPOL_KEY_TYPE_RSN; + + if (peerkey->cipher != WPA_CIPHER_TKIP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK | + WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; + WPA_PUT_BE16(msg->key_info, key_info); + + if (peerkey->cipher != WPA_CIPHER_TKIP) + WPA_PUT_BE16(msg->key_length, 16); + else + WPA_PUT_BE16(msg->key_length, 32); + + os_memcpy(msg->replay_counter, peerkey->replay_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(msg->key_data_length, kde_len); + pos = (u8 *) (msg + 1); + pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); + lifetime = host_to_be32(peerkey->lifetime); + wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime)); + + os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); + + wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR, + MAC2STR(peerkey->addr)); + wpa_eapol_key_send(sm, peerkey->stk.kck, peerkey->stk.kck_len, ver, + peerkey->addr, ETH_P_EAPOL, mbuf, mlen, + msg->key_mic); +} + + +static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey, + struct wpa_eapol_ie_parse *kde) +{ + wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")", + MAC2STR(kde->mac_addr)); + + if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0) + { + wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not " + "match with the one used in SMK M3"); + return -1; + } + + if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not " + "match with the one received in SMK M2"); + return -1; + } + + return 0; +} + + +static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + int ver, + struct wpa_peerkey *peerkey, + struct wpa_eapol_ie_parse *kde) +{ + int cipher; + struct wpa_ie_data ie; + + wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")", + MAC2STR(kde->mac_addr)); + if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN || + wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) { + wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5"); + /* TODO: abort negotiation */ + return -1; + } + + if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does " + "not match with INonce used in SMK M1"); + return -1; + } + + if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0) + { + wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not " + "match with the one used in SMK M1"); + return -1; + } + + os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len); + peerkey->rsnie_p_len = kde->rsn_ie_len; + os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN); + + cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher & + sm->allowed_pairwise_cipher, 0); + if (cipher < 0) { + wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected " + "unacceptable cipher", MAC2STR(kde->mac_addr)); + wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr, + STK_MUI_SMK, STK_ERR_CPHR_NS, + ver); + /* TODO: abort negotiation */ + return -1; + } + wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", + wpa_cipher_txt(cipher)); + peerkey->cipher = cipher; + + return 0; +} + + +static int wpa_supplicant_process_smk_m45( + struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, size_t extra_len, int ver) +{ + struct wpa_peerkey *peerkey; + struct wpa_eapol_ie_parse kde; + u32 lifetime; + + 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 eloop time */ + peerkey->lifetime = 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->akmp); + 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->akmp); + } + 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, const u8 *key_data, + size_t key_data_len) +{ + struct wpa_eapol_ie_parse ie; + size_t 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 */ + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len); + if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0 || + ie.pmkid == NULL) { + wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4"); + return; + } + if (os_memcmp_const(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, + stk, peerkey->akmp, peerkey->cipher); + /* Supplicant: swap tx/rx Mic keys */ + os_memcpy(buf, &stk->tk[16], 8); + os_memcpy(&stk->tk[16], &stk->tk[24], 8); + os_memcpy(&stk->tk[24], 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; + + 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; + + 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, const u8 *key_data, + size_t key_data_len) +{ + struct wpa_eapol_ie_parse kde; + + 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. */ + wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", key_data, key_data_len); + if (wpa_supplicant_parse_ies(key_data, key_data_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_const(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, const u8 *key_data, + size_t key_data_len) +{ + struct wpa_eapol_ie_parse kde; + size_t 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. */ + wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", key_data, key_data_len); + if (wpa_supplicant_parse_ies(key_data, key_data_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), + &peerkey->stk)) + return; + + _key = peerkey->stk.tk; + if (peerkey->cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + os_memcpy(key_buf, _key, 16); + os_memcpy(key_buf + 16, _key + 24, 8); + os_memcpy(key_buf + 24, _key + 16, 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) { + os_memset(key_buf, 0, sizeof(key_buf)); + wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " + "driver."); + return; + } + os_memset(key_buf, 0, sizeof(key_buf)); +} + + +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), peerkey->stk.tk, + 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_192 *key, u16 ver, + const u8 *buf, size_t len) +{ + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; + size_t mic_len = 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, + &peerkey->stk, peerkey->akmp, peerkey->cipher); + peerkey->stk_set = 1; + } + + os_memcpy(mic, key->key_mic, mic_len); + if (peerkey->tstk_set) { + os_memset(key->key_mic, 0, mic_len); + wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len, + sm->key_mgmt, ver, buf, len, key->key_mic); + if (os_memcmp_const(mic, key->key_mic, mic_len) != 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)); + os_memset(&peerkey->tstk, 0, sizeof(peerkey->tstk)); + } + } + + if (!ok && peerkey->stk_set) { + os_memset(key->key_mic, 0, mic_len); + wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len, + sm->key_mgmt, ver, buf, len, key->key_mic); + if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { + wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " + "- dropping packet"); + return -1; + } + ok = 1; + } + + if (!ok) { + wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC " + "- dropping packet"); + return -1; + } + + os_memcpy(peerkey->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + peerkey->replay_counter_set = 1; + return 0; +} + + +/** + * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1) + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @peer: MAC address of the peer STA + * Returns: 0 on success, or -1 on failure + * + * Send an EAPOL-Key Request to the current authenticator to start STK + * handshake with the peer. + */ +int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) +{ + size_t rlen, kde_len; + struct wpa_eapol_key *req; + int key_info, ver; + u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos; + u16 count; + struct rsn_ie_hdr *hdr; + struct wpa_peerkey *peerkey; + struct wpa_ie_data ie; + + if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled) + return -1; + + if (sm->ap_rsn_ie && + wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 && + !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) { + wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK"); + return -1; + } + + if (sm->pairwise_cipher != WPA_CIPHER_TKIP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + if (wpa_sm_get_bssid(sm, bssid) < 0) { + wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " + "SMK M1"); + return -1; + } + + /* TODO: find existing entry and if found, use that instead of adding + * a new one */ + peerkey = os_zalloc(sizeof(*peerkey)); + if (peerkey == NULL) + return -1; + peerkey->initiator = 1; + os_memcpy(peerkey->addr, peer, ETH_ALEN); + peerkey->akmp = sm->key_mgmt; + + /* SMK M1: + * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE)) + */ + + hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + /* Group Suite can be anything for SMK RSN IE; receiver will just + * ignore it. */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + count_pos = pos; + pos += 2; + + count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher); + pos += count * RSN_SELECTOR_LEN; + WPA_PUT_LE16(count_pos, count); + + hdr->len = (pos - peerkey->rsnie_i) - 2; + peerkey->rsnie_i_len = pos - peerkey->rsnie_i; + wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", + peerkey->rsnie_i, peerkey->rsnie_i_len); + + kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*req) + kde_len, &rlen, + (void *) &req); + if (rbuf == NULL) { + wpa_supplicant_peerkey_free(sm, peerkey); + return -1; + } + + req->type = EAPOL_KEY_TYPE_RSN; + key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver; + WPA_PUT_BE16(req->key_info, key_info); + WPA_PUT_BE16(req->key_length, 0); + os_memcpy(req->replay_counter, sm->request_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); + + if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to get random data for INonce"); + os_free(rbuf); + wpa_supplicant_peerkey_free(sm, peerkey); + return -1; + } + os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake", + req->key_nonce, WPA_NONCE_LEN); + + WPA_PUT_BE16(req->key_data_length, (u16) kde_len); + pos = (u8 *) (req + 1); + + /* Initiator RSN IE */ + pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); + /* Peer MAC address KDE */ + wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); + + wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer " + MACSTR ")", MAC2STR(peer)); + wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, 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; + wpa_supplicant_peerkey_free(sm, 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, + const u8 *key_data, size_t key_data_len) +{ + 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, + key_data, key_data_len); + } 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, + key_data, key_data_len); + } 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, + key_data, key_data_len); + } +} + + +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/freebsd/contrib/wpa/src/rsn_supp/peerkey.h b/freebsd/contrib/wpa/src/rsn_supp/peerkey.h new file mode 100644 index 00000000..6ccd948b --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/peerkey.h @@ -0,0 +1,82 @@ +/* + * WPA Supplicant - PeerKey for Direct Link Setup (DLS) + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PEERKEY_H +#define PEERKEY_H + +#define PEERKEY_MAX_IE_LEN 80 +struct wpa_peerkey { + struct wpa_peerkey *next; + int initiator; /* whether this end was initator for SMK handshake */ + u8 addr[ETH_ALEN]; /* other end MAC address */ + u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ + u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */ + u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */ + size_t rsnie_i_len; + u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */ + size_t rsnie_p_len; + u8 smk[PMK_LEN]; + int smk_complete; + u8 smkid[PMKID_LEN]; + u32 lifetime; + int cipher; /* Selected cipher (WPA_CIPHER_*) */ + u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; + int replay_counter_set; + int akmp; + + 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_192 *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, + const u8 *key_data, size_t key_data_len); +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, + const u8 *key_data, size_t key_data_len) +{ +} + +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/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.c b/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.c new file mode 100644 index 00000000..e9bf319a --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.c @@ -0,0 +1,539 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * WPA Supplicant - RSN PMKSA cache + * Copyright (c) 2004-2009, 2011-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa.h" +#include "wpa_i.h" +#include "pmksa_cache.h" + +#ifdef IEEE8021X_EAPOL + +static const int pmksa_cache_max_entries = 32; + +struct rsn_pmksa_cache { + struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */ + int pmksa_count; /* number of entries in PMKSA cache */ + struct wpa_sm *sm; /* TODO: get rid of this reference(?) */ + + void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, + enum pmksa_free_reason reason); + void *ctx; +}; + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); + + +static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) +{ + bin_clear_free(entry, sizeof(*entry)); +} + + +static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry, + enum pmksa_free_reason reason) +{ + wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid); + pmksa->pmksa_count--; + pmksa->free_cb(entry, pmksa->ctx, reason); + _pmksa_cache_free_entry(entry); +} + + +static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct rsn_pmksa_cache *pmksa = eloop_ctx; + struct os_reltime now; + + os_get_reltime(&now); + while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + pmksa->pmksa = entry->next; + wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " + MACSTR, MAC2STR(entry->aa)); + pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE); + } + + pmksa_cache_set_expiration(pmksa); +} + + +static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx) +{ + struct rsn_pmksa_cache *pmksa = eloop_ctx; + pmksa->sm->cur_pmksa = NULL; + eapol_sm_request_reauth(pmksa->sm->eapol); +} + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) +{ + int sec; + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL); + if (pmksa->pmksa == NULL) + return; + os_get_reltime(&now); + sec = pmksa->pmksa->expiration - now.sec; + if (sec < 0) + sec = 0; + eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); + + entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : + pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, 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) + * @kck: Key confirmation key or %NULL if not yet derived + * @kck_len: KCK length in bytes + * @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 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp) +{ + struct rsn_pmksa_cache_entry *entry, *pos, *prev; + struct os_reltime now; + + if (pmk_len > PMK_LEN) + return NULL; + + if (wpa_key_mgmt_suite_b(akmp) && !kck) + return NULL; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + os_memcpy(entry->pmk, pmk, pmk_len); + entry->pmk_len = pmk_len; + if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); + else if (wpa_key_mgmt_suite_b(akmp)) + rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); + else + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, + wpa_key_mgmt_sha256(akmp)); + os_get_reltime(&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_const(pos->pmk, pmk, pmk_len) == 0 && + os_memcmp_const(pos->pmkid, entry->pmkid, + PMKID_LEN) == 0) { + wpa_printf(MSG_DEBUG, "WPA: reusing previous " + "PMKSA entry"); + os_free(entry); + return pos; + } + if (prev == NULL) + pmksa->pmksa = pos->next; + else + prev->next = pos->next; + + /* + * If OKC is used, there may be other PMKSA cache + * entries based on the same PMK. These needs to be + * flushed so that a new entry can be created based on + * the new PMK. Only clear other entries if they have a + * matching PMK and this PMK has been used successfully + * with the current AP, i.e., if opportunistic flag has + * been cleared in wpa_supplicant_key_neg_complete(). + */ + wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " + "the current AP and any PMKSA cache entry " + "that was based on the old PMK"); + if (!pos->opportunistic) + pmksa_cache_flush(pmksa, network_ctx, pos->pmk, + pos->pmk_len); + pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); + break; + } + prev = pos; + pos = pos->next; + } + + if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { + /* Remove the oldest entry to make room for the new entry */ + pos = pmksa->pmksa; + + if (pos == pmksa->sm->cur_pmksa) { + /* + * Never remove the current PMKSA cache entry, since + * it's in use, and removing it triggers a needless + * deauthentication. + */ + pos = pos->next; + pmksa->pmksa->next = pos ? pos->next : NULL; + } else + pmksa->pmksa = pos->next; + + if (pos) { + wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " + "PMKSA cache entry (for " MACSTR ") to " + "make room for new one", + MAC2STR(pos->aa)); + pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE); + } + } + + /* Add the new entry; order by expiration time */ + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (pos->expiration > entry->expiration) + break; + prev = pos; + pos = pos->next; + } + if (prev == NULL) { + entry->next = pmksa->pmksa; + pmksa->pmksa = entry; + pmksa_cache_set_expiration(pmksa); + } else { + entry->next = prev->next; + prev->next = entry; + } + pmksa->pmksa_count++; + wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR + " network_ctx=%p", MAC2STR(entry->aa), network_ctx); + wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid); + + return entry; +} + + +/** + * pmksa_cache_flush - Flush PMKSA cache entries for a specific network + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @network_ctx: Network configuration context or %NULL to flush all entries + * @pmk: PMK to match for or %NYLL to match all PMKs + * @pmk_len: PMK length + */ +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len) +{ + struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; + int removed = 0; + + entry = pmksa->pmksa; + while (entry) { + if ((entry->network_ctx == network_ctx || + network_ctx == NULL) && + (pmk == NULL || + (pmk_len == entry->pmk_len && + os_memcmp(pmk, entry->pmk, pmk_len) == 0))) { + wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " + "for " MACSTR, MAC2STR(entry->aa)); + if (prev) + prev->next = entry->next; + else + pmksa->pmksa = entry->next; + tmp = entry; + entry = entry->next; + pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE); + removed++; + } else { + prev = entry; + entry = entry->next; + } + } + if (removed) + pmksa_cache_set_expiration(pmksa); +} + + +/** + * pmksa_cache_deinit - Free all entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + */ +void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ + struct rsn_pmksa_cache_entry *entry, *prev; + + if (pmksa == NULL) + return; + + entry = pmksa->pmksa; + pmksa->pmksa = NULL; + while (entry) { + prev = entry; + entry = entry->next; + os_free(prev); + } + pmksa_cache_set_expiration(pmksa); + os_free(pmksa); +} + + +/** + * pmksa_cache_get - Fetch a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @aa: Authenticator address or %NULL to match any + * @pmkid: PMKID or %NULL to match any + * @network_ctx: Network context or %NULL to match any + * Returns: Pointer to PMKSA cache entry or %NULL if no match was found + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *aa, const u8 *pmkid, + const void *network_ctx) +{ + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + while (entry) { + if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && + (pmkid == NULL || + os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && + (network_ctx == NULL || network_ctx == entry->network_ctx)) + return entry; + entry = entry->next; + } + return NULL; +} + + +static struct rsn_pmksa_cache_entry * +pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa) +{ + struct rsn_pmksa_cache_entry *new_entry; + + new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, + NULL, 0, + aa, pmksa->sm->own_addr, + old_entry->network_ctx, old_entry->akmp); + if (new_entry == NULL) + return NULL; + + /* TODO: reorder entries based on expiration time? */ + new_entry->expiration = old_entry->expiration; + new_entry->opportunistic = 1; + + return new_entry; +} + + +/** + * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @network_ctx: Network configuration context + * @aa: Authenticator address for the new AP + * Returns: Pointer to a new PMKSA cache entry or %NULL if not available + * + * Try to create a new PMKSA cache entry opportunistically by guessing that the + * new AP is sharing the same PMK as another AP that has the same SSID and has + * already an entry in PMKSA cache. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *aa) +{ + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + + wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); + if (network_ctx == NULL) + return NULL; + while (entry) { + if (entry->network_ctx == network_ctx) { + entry = pmksa_cache_clone_entry(pmksa, entry, aa); + if (entry) { + wpa_printf(MSG_DEBUG, "RSN: added " + "opportunistic PMKSA cache entry " + "for " MACSTR, MAC2STR(aa)); + } + return entry; + } + entry = entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_get_current - Get the current used PMKSA entry + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: Pointer to the current PMKSA cache entry or %NULL if not available + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm) +{ + if (sm == NULL) + return NULL; + return sm->cur_pmksa; +} + + +/** + * pmksa_cache_clear_current - Clear the current PMKSA entry selection + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void pmksa_cache_clear_current(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + sm->cur_pmksa = NULL; +} + + +/** + * pmksa_cache_set_current - Set the current PMKSA entry selection + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @pmkid: PMKID for selecting PMKSA or %NULL if not used + * @bssid: BSSID for PMKSA or %NULL if not used + * @network_ctx: Network configuration context + * @try_opportunistic: Whether to allow opportunistic PMKSA caching + * Returns: 0 if PMKSA was found or -1 if no matching entry was found + */ +int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, void *network_ctx, + int try_opportunistic) +{ + struct rsn_pmksa_cache *pmksa = sm->pmksa; + wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " + "try_opportunistic=%d", network_ctx, try_opportunistic); + if (pmkid) + wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", + pmkid, PMKID_LEN); + if (bssid) + wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, + MAC2STR(bssid)); + + sm->cur_pmksa = NULL; + if (pmkid) + sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, + network_ctx); + if (sm->cur_pmksa == NULL && bssid) + sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, + network_ctx); + if (sm->cur_pmksa == NULL && try_opportunistic && bssid) + sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, + network_ctx, + bssid); + if (sm->cur_pmksa) { + wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID", + sm->cur_pmksa->pmkid, PMKID_LEN); + return 0; + } + wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found"); + return -1; +} + + +/** + * pmksa_cache_list - Dump text list of entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @buf: Buffer for the list + * @len: Length of the buffer + * Returns: number of bytes written to buffer + * + * This function is used to generate a text format representation of the + * current PMKSA cache contents for the ctrl_iface PMKSA command. + */ +int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) +{ + int i, ret; + char *pos = buf; + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + os_get_reltime(&now); + ret = os_snprintf(pos, buf + len - pos, + "Index / AA / PMKID / expiration (in seconds) / " + "opportunistic\n"); + if (os_snprintf_error(buf + len - pos, ret)) + 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 (os_snprintf_error(buf + len - pos, ret)) + 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 (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + entry = entry->next; + } + return pos - buf; +} + + +/** + * pmksa_cache_init - Initialize PMKSA cache + * @free_cb: Callback function to be called when a PMKSA cache entry is freed + * @ctx: Context pointer for free_cb function + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: Pointer to PMKSA cache data or %NULL on failure + */ +struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason), + void *ctx, struct wpa_sm *sm) +{ + struct rsn_pmksa_cache *pmksa; + + pmksa = os_zalloc(sizeof(*pmksa)); + if (pmksa) { + pmksa->free_cb = free_cb; + pmksa->ctx = ctx; + pmksa->sm = sm; + } + + return pmksa; +} + +#endif /* IEEE8021X_EAPOL */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.h b/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.h new file mode 100644 index 00000000..f8e040e0 --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.h @@ -0,0 +1,134 @@ +/* + * wpa_supplicant - WPA2/RSN PMKSA cache functions + * Copyright (c) 2003-2009, 2011-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PMKSA_CACHE_H +#define PMKSA_CACHE_H + +/** + * struct rsn_pmksa_cache_entry - PMKSA cache entry + */ +struct rsn_pmksa_cache_entry { + struct rsn_pmksa_cache_entry *next; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN]; + size_t pmk_len; + os_time_t expiration; + int akmp; /* WPA_KEY_MGMT_* */ + u8 aa[ETH_ALEN]; + + os_time_t reauth_time; + + /** + * network_ctx - Network configuration context + * + * This field is only used to match PMKSA cache entries to a specific + * network configuration (e.g., a specific SSID and security policy). + * This can be a pointer to the configuration entry, but PMKSA caching + * code does not dereference the value and this could be any kind of + * identifier. + */ + void *network_ctx; + int opportunistic; +}; + +struct rsn_pmksa_cache; + +enum pmksa_free_reason { + PMKSA_FREE, + PMKSA_REPLACE, + PMKSA_EXPIRE, +}; + +#ifdef IEEE8021X_EAPOL + +struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason), + void *ctx, struct wpa_sm *sm); +void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); +struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *aa, const u8 *pmkid, + const void *network_ctx); +int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); +struct rsn_pmksa_cache_entry * +pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp); +struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); +void pmksa_cache_clear_current(struct wpa_sm *sm); +int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, void *network_ctx, + int try_opportunistic); +struct rsn_pmksa_cache_entry * +pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, + void *network_ctx, const u8 *aa); +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len); + +#else /* IEEE8021X_EAPOL */ + +static inline struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason), + void *ctx, struct wpa_sm *sm) +{ + return (void *) -1; +} + +static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ +} + +static inline struct rsn_pmksa_cache_entry * +pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, + const void *network_ctx) +{ + return NULL; +} + +static inline struct rsn_pmksa_cache_entry * +pmksa_cache_get_current(struct wpa_sm *sm) +{ + return NULL; +} + +static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, + size_t len) +{ + return -1; +} + +static inline struct rsn_pmksa_cache_entry * +pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp) +{ + return NULL; +} + +static inline void pmksa_cache_clear_current(struct wpa_sm *sm) +{ +} + +static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, + void *network_ctx, + int try_opportunistic) +{ + return -1; +} + +static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, + void *network_ctx, + const u8 *pmk, size_t pmk_len) +{ +} + +#endif /* IEEE8021X_EAPOL */ + +#endif /* PMKSA_CACHE_H */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/preauth.c b/freebsd/contrib/wpa/src/rsn_supp/preauth.c new file mode 100644 index 00000000..f6b3cf5c --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/preauth.c @@ -0,0 +1,543 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * RSN pre-authentication (supplicant) + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa.h" +#include "eloop.h" +#include "l2_packet/l2_packet.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "preauth.h" +#include "pmksa_cache.h" +#include "wpa_i.h" + + +#ifdef IEEE8021X_EAPOL + +#define PMKID_CANDIDATE_PRIO_SCAN 1000 + + +struct rsn_pmksa_candidate { + struct dl_list list; + u8 bssid[ETH_ALEN]; + int priority; +}; + + +/** + * pmksa_candidate_free - Free all entries in PMKSA candidate list + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void pmksa_candidate_free(struct wpa_sm *sm) +{ + struct rsn_pmksa_candidate *entry, *n; + + if (sm == NULL) + return; + + dl_list_for_each_safe(entry, n, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + dl_list_del(&entry->list); + os_free(entry); + } +} + + +static void rsn_preauth_receive(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_sm *sm = ctx; + + wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr)); + wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len); + + if (sm->preauth_eapol == NULL || + is_zero_ether_addr(sm->preauth_bssid) || + os_memcmp(sm->preauth_bssid, src_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_WARNING, "RSN pre-auth frame received from " + "unexpected source " MACSTR " - dropped", + MAC2STR(src_addr)); + return; + } + + eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len); +} + + +static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, + enum eapol_supp_result result, + void *ctx) +{ + struct wpa_sm *sm = ctx; + u8 pmk[PMK_LEN]; + + if (result == EAPOL_SUPP_RESULT_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, + NULL, 0, + 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"); + result = EAPOL_SUPP_RESULT_FAILURE; + } + } + + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " + MACSTR " %s", MAC2STR(sm->preauth_bssid), + result == EAPOL_SUPP_RESULT_SUCCESS ? "completed successfully" : + "failed"); + + rsn_preauth_deinit(sm); + rsn_preauth_candidate_process(sm); +} + + +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; + int ret; + + 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"); + ret = -2; + goto fail; + } + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context."); + ret = -4; + goto fail; + } + 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"); + ret = -3; + goto fail; + } + 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; + +fail: + if (sm->l2_preauth_br) { + l2_packet_deinit(sm->l2_preauth_br); + sm->l2_preauth_br = NULL; + } + l2_packet_deinit(sm->l2_preauth); + sm->l2_preauth = NULL; + return ret; +} + + +/** + * 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 && + sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B && + sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)) { + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable " + "state for new pre-authentication"); + return; /* invalid state for new pre-auth */ + } + + dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + struct rsn_pmksa_cache_entry *p = NULL; + p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL); + if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 && + (p == NULL || p->opportunistic)) { + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA " + "candidate " MACSTR + " selected for pre-authentication", + MAC2STR(candidate->bssid)); + dl_list_del(&candidate->list); + rsn_preauth_init(sm, candidate->bssid, + sm->eap_conf_ctx); + os_free(candidate); + return; + } + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA candidate " + MACSTR " does not need pre-authentication anymore", + MAC2STR(candidate->bssid)); + /* Some drivers (e.g., NDIS) expect to get notified about the + * PMKIDs again, so report the existing data now. */ + if (p) { + wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid); + } + + dl_list_del(&candidate->list); + os_free(candidate); + } + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no more pending PMKSA " + "candidates"); +} + + +/** + * pmksa_candidate_add - Add a new PMKSA candidate + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @bssid: BSSID (authenticator address) of the candidate + * @prio: Priority (the smaller number, the higher priority) + * @preauth: Whether the candidate AP advertises support for pre-authentication + * + * This function is used to add PMKSA candidates for RSN pre-authentication. It + * is called from scan result processing and from driver events for PMKSA + * candidates, i.e., EVENT_PMKID_CANDIDATE events to wpa_supplicant_event(). + */ +void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, + int prio, int preauth) +{ + struct rsn_pmksa_candidate *cand, *pos; + + if (sm->network_ctx && sm->proactive_key_caching) + pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx, + bssid); + + if (!preauth) { + wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without " + "preauth flag"); + return; + } + + /* If BSSID already on candidate list, update the priority of the old + * entry. Do not override priority based on normal scan results. */ + cand = NULL; + dl_list_for_each(pos, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + if (os_memcmp(pos->bssid, bssid, ETH_ALEN) == 0) { + cand = pos; + break; + } + } + + if (cand) { + dl_list_del(&cand->list); + if (prio < PMKID_CANDIDATE_PRIO_SCAN) + cand->priority = prio; + } else { + cand = os_zalloc(sizeof(*cand)); + if (cand == NULL) + return; + os_memcpy(cand->bssid, bssid, ETH_ALEN); + cand->priority = prio; + } + + /* Add candidate to the list; order by increasing priority value. i.e., + * highest priority (smallest value) first. */ + dl_list_for_each(pos, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + if (cand->priority <= pos->priority) { + if (!pos->list.prev) { + /* + * This cannot really happen in pracrice since + * pos was fetched from the list and the prev + * pointer must be set. It looks like clang + * static analyzer gets confused with the + * dl_list_del(&cand->list) call above and ends + * up assuming pos->list.prev could be NULL. + */ + os_free(cand); + return; + } + dl_list_add(pos->list.prev, &cand->list); + cand = NULL; + break; + } + } + if (cand) + dl_list_add_tail(&sm->pmksa_candidates, &cand->list); + + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: added PMKSA cache " + "candidate " MACSTR " prio %d", MAC2STR(bssid), prio); + rsn_preauth_candidate_process(sm); +} + + +/* TODO: schedule periodic scans if current AP supports preauth */ + +/** + * rsn_preauth_scan_results - Start processing scan results for canditates + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: 0 if ready to process results or -1 to skip processing + * + * This functions is used to notify RSN code about start of new scan results + * processing. The actual scan results will be provided by calling + * rsn_preauth_scan_result() for each BSS if this function returned 0. + */ +int rsn_preauth_scan_results(struct wpa_sm *sm) +{ + if (sm->ssid_len == 0) + return -1; + + /* + * TODO: is it ok to free all candidates? What about the entries + * received from EVENT_PMKID_CANDIDATE? + */ + pmksa_candidate_free(sm); + + return 0; +} + + +/** + * rsn_preauth_scan_result - Processing scan result for PMKSA canditates + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * Add all suitable APs (Authenticators) from scan results into PMKSA + * candidate list. + */ +void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, + const u8 *ssid, const u8 *rsn) +{ + struct wpa_ie_data ie; + struct rsn_pmksa_cache_entry *pmksa; + + if (ssid[1] != sm->ssid_len || + os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0) + return; /* Not for the current SSID */ + + if (os_memcmp(bssid, sm->bssid, ETH_ALEN) == 0) + return; /* Ignore current AP */ + + if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) + return; + + pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL); + if (pmksa && (!pmksa->opportunistic || + !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) + return; + + /* Give less priority to candidates found from normal scan results. */ + pmksa_candidate_add(sm, bssid, PMKID_CANDIDATE_PRIO_SCAN, + ie.capabilities & WPA_CAPABILITY_PREAUTH); +} + + +#ifdef CONFIG_CTRL_IFACE +/** + * rsn_preauth_get_status - Get pre-authentication status + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query WPA2 pre-authentication for status information. This function fills in + * a text area with current status information. If the buffer (buf) is not + * large enough, status information will be truncated to fit the buffer. + */ +int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose) +{ + char *pos = buf, *end = buf + buflen; + int res, ret; + + if (sm->preauth_eapol) { + ret = os_snprintf(pos, end - pos, "Pre-authentication " + "EAPOL state machines:\n"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + res = eapol_sm_get_status(sm->preauth_eapol, + pos, end - pos, verbose); + if (res >= 0) + pos += res; + } + + return pos - buf; +} +#endif /* CONFIG_CTRL_IFACE */ + + +/** + * rsn_preauth_in_progress - Verify whether pre-authentication is in progress + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +int rsn_preauth_in_progress(struct wpa_sm *sm) +{ + return sm->preauth_eapol != NULL; +} + +#endif /* IEEE8021X_EAPOL */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/preauth.h b/freebsd/contrib/wpa/src/rsn_supp/preauth.h new file mode 100644 index 00000000..277f0663 --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/preauth.h @@ -0,0 +1,79 @@ +/* + * wpa_supplicant - WPA2/RSN pre-authentication functions + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PREAUTH_H +#define PREAUTH_H + +struct wpa_scan_results; + +#ifdef IEEE8021X_EAPOL + +void pmksa_candidate_free(struct wpa_sm *sm); +int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, + struct eap_peer_config *eap_conf); +void rsn_preauth_deinit(struct wpa_sm *sm); +int rsn_preauth_scan_results(struct wpa_sm *sm); +void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, + const u8 *ssid, const u8 *rsn); +void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, + int prio, int preauth); +void rsn_preauth_candidate_process(struct wpa_sm *sm); +int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose); +int rsn_preauth_in_progress(struct wpa_sm *sm); + +#else /* IEEE8021X_EAPOL */ + +static inline void pmksa_candidate_free(struct wpa_sm *sm) +{ +} + +static inline void rsn_preauth_candidate_process(struct wpa_sm *sm) +{ +} + +static inline int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, + struct eap_peer_config *eap_conf) +{ + return -1; +} + +static inline void rsn_preauth_deinit(struct wpa_sm *sm) +{ +} + +static inline int rsn_preauth_scan_results(struct wpa_sm *sm) +{ + return -1; +} + +static inline void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, + const u8 *ssid, const u8 *rsn) +{ +} + +static inline void pmksa_candidate_add(struct wpa_sm *sm, + const u8 *bssid, + int prio, int preauth) +{ +} + +static inline int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, + size_t buflen, int verbose) +{ + return 0; +} + +static inline int rsn_preauth_in_progress(struct wpa_sm *sm) +{ + return 0; +} + +#endif /* IEEE8021X_EAPOL */ + +#endif /* PREAUTH_H */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa.c b/freebsd/contrib/wpa/src/rsn_supp/wpa.c new file mode 100644 index 00000000..a738c986 --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/wpa.c @@ -0,0 +1,2979 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * WPA Supplicant - WPA state machine and EAPOL-Key processing + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa.h" +#include "eloop.h" +#include "preauth.h" +#include "pmksa_cache.h" +#include "wpa_i.h" +#include "wpa_ie.h" +#include "peerkey.h" + + +/** + * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @kck: Key Confirmation Key (KCK, part of PTK) + * @kck_len: KCK length in octets + * @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, size_t kck_len, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic) +{ + size_t mic_len = wpa_mic_len(sm->key_mgmt); + + 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, kck_len, sm->key_mgmt, ver, msg, msg_len, + key_mic)) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC", + ver, sm->key_mgmt); + goto out; + } + wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len); + wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len); + 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 mic_len, hdrlen, rlen; + struct wpa_eapol_key *reply; + struct wpa_eapol_key_192 *reply192; + int key_info, ver; + u8 bssid[ETH_ALEN], *rbuf, *key_mic; + + if (sm->key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->key_mgmt)) + ver = WPA_KEY_INFO_TYPE_AKM_DEFINED; + else if (wpa_key_mgmt_ft(sm->key_mgmt) || + wpa_key_mgmt_sha256(sm->key_mgmt)) + ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; + else if (sm->pairwise_cipher != WPA_CIPHER_TKIP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + if (wpa_sm_get_bssid(sm, bssid) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "Failed to read BSSID for EAPOL-Key request"); + return; + } + + mic_len = wpa_mic_len(sm->key_mgmt); + hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + hdrlen, &rlen, (void *) &reply); + if (rbuf == NULL) + return; + reply192 = (struct wpa_eapol_key_192 *) reply; + + reply->type = (sm->proto == WPA_PROTO_RSN || + sm->proto == WPA_PROTO_OSEN) ? + 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); + + if (mic_len == 24) + WPA_PUT_BE16(reply192->key_data_length, 0); + else + WPA_PUT_BE16(reply->key_data_length, 0); + if (!(key_info & WPA_KEY_INFO_MIC)) + key_mic = NULL; + else + key_mic = reply192->key_mic; /* same offset in reply */ + + 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, sm->ptk.kck_len, ver, bssid, + ETH_P_EAPOL, rbuf, rlen, key_mic); +} + + +static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm) +{ +#ifdef CONFIG_IEEE80211R + if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { + if (wpa_sm_key_mgmt_set_pmk(sm, sm->xxkey, sm->xxkey_len)) + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cannot set low order 256 bits of MSK for key management offload"); + } else { +#endif /* CONFIG_IEEE80211R */ + if (wpa_sm_key_mgmt_set_pmk(sm, sm->pmk, sm->pmk_len)) + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cannot set PMK for key management offload"); +#ifdef CONFIG_IEEE80211R + } +#endif /* CONFIG_IEEE80211R */ +} + + +static int wpa_supplicant_get_pmk(struct wpa_sm *sm, + const unsigned char *src_addr, + const u8 *pmkid) +{ + int abort_cached = 0; + + if (pmkid && !sm->cur_pmksa) { + /* When using drivers that generate RSN IE, wpa_supplicant may + * not have enough time to get the association information + * event before receiving this 1/4 message, so try to find a + * matching PMKSA cache entry here. */ + sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid, + NULL); + if (sm->cur_pmksa) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: found matching PMKID from PMKSA cache"); + } else { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: no matching PMKID found"); + abort_cached = 1; + } + } + + if (pmkid && sm->cur_pmksa && + os_memcmp_const(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) { + wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN); + wpa_sm_set_pmk_from_pmksa(sm); + wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache", + sm->pmk, sm->pmk_len); + eapol_sm_notify_cached(sm->eapol); +#ifdef CONFIG_IEEE80211R + sm->xxkey_len = 0; +#endif /* CONFIG_IEEE80211R */ + } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) { + int res, pmk_len; + pmk_len = PMK_LEN; + res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); + if (res) { + /* + * EAP-LEAP is an exception from other EAP methods: it + * uses only 16-byte PMK. + */ + res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); + pmk_len = 16; + } else { +#ifdef CONFIG_IEEE80211R + u8 buf[2 * PMK_LEN]; + if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0) + { + os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN); + sm->xxkey_len = PMK_LEN; + os_memset(buf, 0, sizeof(buf)); + } +#endif /* CONFIG_IEEE80211R */ + } + if (res == 0) { + struct rsn_pmksa_cache_entry *sa = NULL; + wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " + "machines", sm->pmk, pmk_len); + sm->pmk_len = pmk_len; + wpa_supplicant_key_mgmt_set_pmk(sm); + if (sm->proto == WPA_PROTO_RSN && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && + !wpa_key_mgmt_ft(sm->key_mgmt)) { + sa = pmksa_cache_add(sm->pmksa, + sm->pmk, pmk_len, + NULL, 0, + src_addr, sm->own_addr, + sm->network_ctx, + sm->key_mgmt); + } + if (!sm->cur_pmksa && pmkid && + pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL)) + { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: the new PMK matches with the " + "PMKID"); + abort_cached = 0; + } else if (sa && !sm->cur_pmksa && pmkid) { + /* + * It looks like the authentication server + * derived mismatching MSK. This should not + * really happen, but bugs happen.. There is not + * much we can do here without knowing what + * exactly caused the server to misbehave. + */ + wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); + return -1; + } + + if (!sm->cur_pmksa) + sm->cur_pmksa = sa; + } else { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to get master session key from " + "EAPOL state machines - key handshake " + "aborted"); + if (sm->cur_pmksa) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cancelled PMKSA caching " + "attempt"); + sm->cur_pmksa = NULL; + abort_cached = 1; + } else if (!abort_cached) { + return -1; + } + } + } + + if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && + !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN) + { + /* 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 mic_len, hdrlen, rlen; + struct wpa_eapol_key *reply; + struct wpa_eapol_key_192 *reply192; + u8 *rbuf, *key_mic; + 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); + + mic_len = wpa_mic_len(sm->key_mgmt); + hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, + NULL, hdrlen + wpa_ie_len, + &rlen, (void *) &reply); + if (rbuf == NULL) { + os_free(rsn_ie_buf); + return -1; + } + reply192 = (struct wpa_eapol_key_192 *) reply; + + reply->type = (sm->proto == WPA_PROTO_RSN || + sm->proto == WPA_PROTO_OSEN) ? + 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 || sm->proto == WPA_PROTO_OSEN) + 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); + + key_mic = reply192->key_mic; /* same offset for reply and reply192 */ + if (mic_len == 24) { + WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len); + os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len); + } else { + 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, ptk->kck_len, ver, dst, ETH_P_EAPOL, + rbuf, rlen, 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) +{ +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) + return wpa_derive_ptk_ft(sm, src_addr, key, ptk); +#endif /* CONFIG_IEEE80211R */ + + return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", + sm->own_addr, sm->bssid, sm->snonce, + key->key_nonce, ptk, sm->key_mgmt, + sm->pairwise_cipher); +} + + +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, const u8 *key_data, + size_t key_data_len) +{ + struct wpa_eapol_ie_parse ie; + struct wpa_ptk *ptk; + int res; + u8 *kde, *kde_buf = NULL; + size_t kde_len; + + if (wpa_sm_get_network_ctx(sm) == NULL) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info " + "found (msg 1 of 4)"); + return; + } + + wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way " + "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + + os_memset(&ie, 0, sizeof(ie)); + + if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { + /* RSN: msg 1/4 should contain PMKID for the selected PMK */ + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", + key_data, key_data_len); + if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) + goto failed; + if (ie.pmkid) { + wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " + "Authenticator", ie.pmkid, PMKID_LEN); + } + } + + res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); + if (res == -2) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to " + "msg 1/4 - requesting full EAP authentication"); + return; + } + if (res) + goto failed; + + if (sm->renew_snonce) { + if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to get random data for SNonce"); + goto failed; + } + sm->renew_snonce = 0; + wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", + sm->snonce, WPA_NONCE_LEN); + } + + /* Calculate PTK which will be stored as a temporary PTK until it has + * been verified when processing message 3/4. */ + ptk = &sm->tptk; + wpa_derive_ptk(sm, src_addr, key, ptk); + if (sm->pairwise_cipher == WPA_CIPHER_TKIP) { + u8 buf[8]; + /* Supplicant: swap tx/rx Mic keys */ + os_memcpy(buf, &ptk->tk[16], 8); + os_memcpy(&ptk->tk[16], &ptk->tk[24], 8); + os_memcpy(&ptk->tk[24], buf, 8); + os_memset(buf, 0, sizeof(buf)); + } + sm->tptk_set = 1; + + kde = sm->assoc_wpa_ie; + kde_len = sm->assoc_wpa_ie_len; + +#ifdef CONFIG_P2P + if (sm->p2p) { + kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1); + if (kde_buf) { + u8 *pos; + wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE " + "into EAPOL-Key 2/4"); + os_memcpy(kde_buf, kde, kde_len); + kde = kde_buf; + pos = kde + kde_len; + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + 1; + RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ); + pos += RSN_SELECTOR_LEN; + *pos++ = 0x01; + kde_len = pos - kde; + } + } +#endif /* CONFIG_P2P */ + + if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, + kde, kde_len, ptk)) + goto failed; + + os_free(kde_buf); + os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); + return; + +failed: + os_free(kde_buf); + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + +static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + rsn_preauth_candidate_process(sm); +} + + +static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, + const u8 *addr, int secure) +{ + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Key negotiation completed with " + MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr), + wpa_cipher_txt(sm->pairwise_cipher), + wpa_cipher_txt(sm->group_cipher)); + wpa_sm_cancel_auth_timeout(sm); + wpa_sm_set_state(sm, WPA_COMPLETED); + + if (secure) { + wpa_sm_mlme_setprotection( + sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX, + MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); + eapol_sm_notify_portValid(sm->eapol, TRUE); + if (wpa_key_mgmt_wpa_psk(sm->key_mgmt)) + eapol_sm_notify_eap_success(sm->eapol, TRUE); + /* + * Start preauthentication after a short wait to avoid a + * possible race condition between the data receive and key + * configuration after the 4-Way Handshake. This increases the + * likelihood of the first preauth EAPOL-Start frame getting to + * the target AP. + */ + eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL); + } + + if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Authenticator accepted " + "opportunistic PMKSA entry - marking it valid"); + sm->cur_pmksa->opportunistic = 0; + } + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) { + /* Prepare for the next transition */ + wpa_ft_prepare_auth_request(sm, NULL); + } +#endif /* CONFIG_IEEE80211R */ +} + + +static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying"); + wpa_sm_key_request(sm, 0, 1); +} + + +static int wpa_supplicant_install_ptk(struct wpa_sm *sm, + const struct wpa_eapol_key *key) +{ + int keylen, rsclen; + enum wpa_alg alg; + const u8 *key_rsc; + u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Installing PTK to the driver"); + + if (sm->pairwise_cipher == WPA_CIPHER_NONE) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher " + "Suite: NONE - do not use pairwise keys"); + return 0; + } + + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported pairwise cipher %d", + sm->pairwise_cipher); + return -1; + } + + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); + + if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { + 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, + sm->ptk.tk, 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; + } + + /* TK is not needed anymore in supplicant */ + os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); + + if (sm->wpa_ptk_rekey) { + eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); + eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk, + sm, NULL); + } + + return 0; +} + + +static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm, + int group_cipher, + int keylen, int maxkeylen, + int *key_rsc_len, + enum wpa_alg *alg) +{ + int klen; + + *alg = wpa_cipher_to_alg(group_cipher); + if (*alg == WPA_ALG_NONE) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported Group Cipher %d", + group_cipher); + return -1; + } + *key_rsc_len = wpa_cipher_rsc_len(group_cipher); + + klen = wpa_cipher_key_len(group_cipher); + if (keylen != klen || maxkeylen < klen) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported %s Group Cipher key length %d (%d)", + wpa_cipher_txt(group_cipher), keylen, maxkeylen); + return -1; + } + return 0; +} + + +struct wpa_gtk_data { + enum wpa_alg alg; + int tx, key_rsc_len, keyidx; + u8 gtk[32]; + int gtk_len; +}; + + +static int wpa_supplicant_install_gtk(struct wpa_sm *sm, + const struct wpa_gtk_data *gd, + const u8 *key_rsc) +{ + const u8 *_gtk = gd->gtk; + u8 gtk_buf[32]; + + wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)", + gd->keyidx, gd->tx, gd->gtk_len); + wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len); + if (sm->group_cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + os_memcpy(gtk_buf, gd->gtk, 16); + os_memcpy(gtk_buf + 16, gd->gtk + 24, 8); + os_memcpy(gtk_buf + 24, gd->gtk + 16, 8); + _gtk = gtk_buf; + } + if (sm->pairwise_cipher == WPA_CIPHER_NONE) { + if (wpa_sm_set_key(sm, gd->alg, NULL, + gd->keyidx, 1, key_rsc, gd->key_rsc_len, + _gtk, gd->gtk_len) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to set GTK to the driver " + "(Group only)"); + os_memset(gtk_buf, 0, sizeof(gtk_buf)); + 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); + os_memset(gtk_buf, 0, sizeof(gtk_buf)); + return -1; + } + os_memset(gtk_buf, 0, sizeof(gtk_buf)); + + return 0; +} + + +static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, + int tx) +{ + if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) { + /* Ignore Tx bit for GTK if a pairwise key is used. One AP + * seemed to set this bit (incorrectly, since Tx is only when + * doing Group Key only APs) and without this workaround, the + * data connection does not work because wpa_supplicant + * configured non-zero keyidx to be used for unicast. */ + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Tx bit set for GTK, but pairwise " + "keys are used - ignore Tx bit"); + return 0; + } + return tx; +} + + +static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + const u8 *gtk, size_t gtk_len, + int key_info) +{ + struct wpa_gtk_data gd; + + /* + * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x + * GTK KDE format: + * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7] + * Reserved [bits 0-7] + * GTK + */ + + os_memset(&gd, 0, sizeof(gd)); + wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake", + gtk, gtk_len); + + if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk)) + return -1; + + gd.keyidx = gtk[0] & 0x3; + gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(gtk[0] & BIT(2))); + gtk += 2; + gtk_len -= 2; + + os_memcpy(gd.gtk, gtk, gtk_len); + gd.gtk_len = gtk_len; + + if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED && + (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gtk_len, gtk_len, + &gd.key_rsc_len, &gd.alg) || + wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Failed to install GTK"); + os_memset(&gd, 0, sizeof(gd)); + return -1; + } + os_memset(&gd, 0, sizeof(gd)); + + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & WPA_KEY_INFO_SECURE); + return 0; +} + + +static int ieee80211w_set_keys(struct wpa_sm *sm, + struct wpa_eapol_ie_parse *ie) +{ +#ifdef CONFIG_IEEE80211W + if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher)) + return 0; + + if (ie->igtk) { + size_t len; + const struct wpa_igtk_kde *igtk; + u16 keyidx; + len = wpa_cipher_key_len(sm->mgmt_group_cipher); + if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len) + 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, 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_cipher_to_alg(sm->mgmt_group_cipher), + broadcast_ether_addr, + keyidx, 0, igtk->pn, sizeof(igtk->pn), + igtk->igtk, len) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to configure IGTK to the driver"); + return -1; + } + } + + return 0; +#else /* CONFIG_IEEE80211W */ + return 0; +#endif /* CONFIG_IEEE80211W */ +} + + +static void wpa_report_ie_mismatch(struct wpa_sm *sm, + const char *reason, const u8 *src_addr, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *rsn_ie, size_t rsn_ie_len) +{ + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")", + reason, MAC2STR(src_addr)); + + if (sm->ap_wpa_ie) { + wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp", + sm->ap_wpa_ie, sm->ap_wpa_ie_len); + } + if (wpa_ie) { + if (!sm->ap_wpa_ie) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No WPA IE in Beacon/ProbeResp"); + } + wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg", + wpa_ie, wpa_ie_len); + } + + if (sm->ap_rsn_ie) { + wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp", + sm->ap_rsn_ie, sm->ap_rsn_ie_len); + } + if (rsn_ie) { + if (!sm->ap_rsn_ie) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No RSN IE in Beacon/ProbeResp"); + } + wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg", + rsn_ie, rsn_ie_len); + } + + wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); +} + + +#ifdef CONFIG_IEEE80211R + +static int ft_validate_mdie(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie, + const u8 *assoc_resp_mdie) +{ + struct rsn_mdie *mdie; + + mdie = (struct rsn_mdie *) (ie->mdie + 2); + if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did " + "not match with the current mobility domain"); + return -1; + } + + if (assoc_resp_mdie && + (assoc_resp_mdie[1] != ie->mdie[1] || + os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch"); + wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4", + ie->mdie, 2 + ie->mdie[1]); + wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response", + assoc_resp_mdie, 2 + assoc_resp_mdie[1]); + return -1; + } + + return 0; +} + + +static int ft_validate_ftie(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie, + const u8 *assoc_resp_ftie) +{ + if (ie->ftie == NULL) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "FT: No FTIE in EAPOL-Key msg 3/4"); + return -1; + } + + if (assoc_resp_ftie == NULL) + return 0; + + if (assoc_resp_ftie[1] != ie->ftie[1] || + os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch"); + wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4", + ie->ftie, 2 + ie->ftie[1]); + wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response", + assoc_resp_ftie, 2 + assoc_resp_ftie[1]); + return -1; + } + + return 0; +} + + +static int ft_validate_rsnie(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie) +{ + struct wpa_ie_data rsn; + + if (!ie->rsn_ie) + return 0; + + /* + * Verify that PMKR1Name from EAPOL-Key message 3/4 + * matches with the value we derived. + */ + if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 || + rsn.num_pmkid != 1 || rsn.pmkid == NULL) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in " + "FT 4-way handshake message 3/4"); + return -1; + } + + if (os_memcmp_const(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 + * @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, + struct wpa_ptk *ptk) +{ + size_t mic_len, hdrlen, rlen; + struct wpa_eapol_key *reply; + struct wpa_eapol_key_192 *reply192; + u8 *rbuf, *key_mic; + + mic_len = wpa_mic_len(sm->key_mgmt); + hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + hdrlen, &rlen, (void *) &reply); + if (rbuf == NULL) + return -1; + reply192 = (struct wpa_eapol_key_192 *) reply; + + reply->type = (sm->proto == WPA_PROTO_RSN || + sm->proto == WPA_PROTO_OSEN) ? + 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 || sm->proto == WPA_PROTO_OSEN) + 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); + + key_mic = reply192->key_mic; /* same offset for reply and reply192 */ + if (mic_len == 24) + WPA_PUT_BE16(reply192->key_data_length, 0); + else + WPA_PUT_BE16(reply->key_data_length, 0); + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); + wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, + rbuf, rlen, key_mic); + + return 0; +} + + +static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + u16 ver, const u8 *key_data, + size_t key_data_len) +{ + u16 key_info, keylen; + 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); + + wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len); + if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) + goto failed; + if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: GTK IE in unencrypted key data"); + goto failed; + } +#ifdef CONFIG_IEEE80211W + if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: IGTK KDE in unencrypted key data"); + goto failed; + } + + if (ie.igtk && + wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) && + ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN + + (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid IGTK KDE length %lu", + (unsigned long) ie.igtk_len); + goto failed; + } +#endif /* CONFIG_IEEE80211W */ + + if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0) + goto failed; + + if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: ANonce from message 1 of 4-Way Handshake " + "differs from 3 of 4-Way Handshake - drop packet (src=" + MACSTR ")", MAC2STR(sm->bssid)); + goto failed; + } + + keylen = WPA_GET_BE16(key->key_length); + if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid %s key length %d (src=" MACSTR + ")", wpa_cipher_txt(sm->pairwise_cipher), keylen, + MAC2STR(sm->bssid)); + goto failed; + } + +#ifdef CONFIG_P2P + if (ie.ip_addr_alloc) { + os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4); + wpa_hexdump(MSG_DEBUG, "P2P: IP address info", + sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr)); + } +#endif /* CONFIG_P2P */ + + if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, + &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 (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) { + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & WPA_KEY_INFO_SECURE); + } else if (ie.gtk && + wpa_supplicant_pairwise_gtk(sm, key, + ie.gtk, ie.gtk_len, key_info) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure GTK"); + goto failed; + } + + if (ieee80211w_set_keys(sm, &ie) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure IGTK"); + goto failed; + } + + if (ie.gtk) + wpa_sm_set_rekey_offload(sm); + + if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) { + struct rsn_pmksa_cache_entry *sa; + + sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, + sm->ptk.kck, sm->ptk.kck_len, + sm->bssid, sm->own_addr, + sm->network_ctx, sm->key_mgmt); + if (!sm->cur_pmksa) + sm->cur_pmksa = sa; + } + + sm->msg_3_of_4_ok = 1; + return; + +failed: + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + +static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, + const u8 *keydata, + size_t keydatalen, + u16 key_info, + struct wpa_gtk_data *gd) +{ + int maxkeylen; + struct wpa_eapol_ie_parse ie; + + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); + if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0) + return -1; + if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: GTK IE in unencrypted key data"); + return -1; + } + if (ie.gtk == NULL) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No GTK IE in Group Key msg 1/2"); + return -1; + } + maxkeylen = gd->gtk_len = ie.gtk_len - 2; + + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gd->gtk_len, maxkeylen, + &gd->key_rsc_len, &gd->alg)) + return -1; + + wpa_hexdump_key(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, + const u8 *key_data, + size_t key_data_len, u16 key_info, + u16 ver, struct wpa_gtk_data *gd) +{ + size_t maxkeylen; + u16 gtk_len; + + gtk_len = WPA_GET_BE16(key->key_length); + maxkeylen = key_data_len; + 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 (gtk_len > maxkeylen || + wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gtk_len, maxkeylen, + &gd->key_rsc_len, &gd->alg)) + return -1; + + gd->gtk_len = gtk_len; + 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 && sm->ptk.kek_len == 16) { +#ifdef CONFIG_NO_RC4 + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 not supported in the build"); + return -1; +#else /* CONFIG_NO_RC4 */ + u8 ek[32]; + if (key_data_len > sizeof(gd->gtk)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 key data too long (%lu)", + (unsigned long) key_data_len); + return -1; + } + os_memcpy(ek, key->key_iv, 16); + os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); + os_memcpy(gd->gtk, key_data, key_data_len); + if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) { + os_memset(ek, 0, sizeof(ek)); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: RC4 failed"); + return -1; + } + os_memset(ek, 0, sizeof(ek)); +#endif /* CONFIG_NO_RC4 */ + } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + if (maxkeylen % 8) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported AES-WRAP len %lu", + (unsigned long) maxkeylen); + 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) key_data_len, + (unsigned long) maxkeylen); + return -1; + } + if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8, + key_data, 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 mic_len, hdrlen, rlen; + struct wpa_eapol_key *reply; + struct wpa_eapol_key_192 *reply192; + u8 *rbuf, *key_mic; + + mic_len = wpa_mic_len(sm->key_mgmt); + hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + hdrlen, &rlen, (void *) &reply); + if (rbuf == NULL) + return -1; + reply192 = (struct wpa_eapol_key_192 *) reply; + + reply->type = (sm->proto == WPA_PROTO_RSN || + sm->proto == WPA_PROTO_OSEN) ? + 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 || sm->proto == WPA_PROTO_OSEN) + 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); + + key_mic = reply192->key_mic; /* same offset for reply and reply192 */ + if (mic_len == 24) + WPA_PUT_BE16(reply192->key_data_length, 0); + else + 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, sm->ptk.kck_len, ver, sm->bssid, + ETH_P_EAPOL, rbuf, rlen, 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, + const u8 *key_data, + size_t key_data_len, u16 ver) +{ + u16 key_info; + int rekey, ret; + struct wpa_gtk_data gd; + + if (!sm->msg_3_of_4_ok) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Group Key Handshake started prior to completion of 4-way handshake"); + goto failed; + } + + 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); + + if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { + ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data, + key_data_len, key_info, + &gd); + } else { + ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data, + key_data_len, + key_info, 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; + os_memset(&gd, 0, sizeof(gd)); + + if (rekey) { + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Group rekeying " + "completed with " MACSTR " [GTK=%s]", + MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); + wpa_sm_cancel_auth_timeout(sm); + wpa_sm_set_state(sm, WPA_COMPLETED); + } else { + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & + WPA_KEY_INFO_SECURE); + } + + wpa_sm_set_rekey_offload(sm); + + return; + +failed: + os_memset(&gd, 0, sizeof(gd)); + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + +static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, + struct wpa_eapol_key_192 *key, + u16 ver, + const u8 *buf, size_t len) +{ + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; + int ok = 0; + size_t mic_len = wpa_mic_len(sm->key_mgmt); + + os_memcpy(mic, key->key_mic, mic_len); + if (sm->tptk_set) { + os_memset(key->key_mic, 0, mic_len); + wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt, + ver, buf, len, key->key_mic); + if (os_memcmp_const(mic, key->key_mic, mic_len) != 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)); + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + } + } + + if (!ok && sm->ptk_set) { + os_memset(key->key_mic, 0, mic_len); + wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt, + ver, buf, len, key->key_mic); + if (os_memcmp_const(mic, key->key_mic, mic_len) != 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, + u8 *key_data, size_t *key_data_len) +{ + wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", + key_data, *key_data_len); + 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 && sm->ptk.kek_len == 16) { +#ifdef CONFIG_NO_RC4 + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 not supported in the build"); + return -1; +#else /* CONFIG_NO_RC4 */ + u8 ek[32]; + os_memcpy(ek, key->key_iv, 16); + os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); + if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) { + os_memset(ek, 0, sizeof(ek)); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: RC4 failed"); + return -1; + } + os_memset(ek, 0, sizeof(ek)); +#endif /* CONFIG_NO_RC4 */ + } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || + sm->key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->key_mgmt)) { + u8 *buf; + if (*key_data_len < 8 || *key_data_len % 8) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported AES-WRAP len %u", + (unsigned int) *key_data_len); + return -1; + } + *key_data_len -= 8; /* AES-WRAP adds 8 bytes */ + buf = os_malloc(*key_data_len); + 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, sm->ptk.kek_len, *key_data_len / 8, + key_data, 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_data, buf, *key_data_len); + os_free(buf); + WPA_PUT_BE16(key->key_data_length, *key_data_len); + } 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", + key_data, *key_data_len); + 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, + unsigned int key_data_len, + const u8 *mic, unsigned int mic_len) +{ +#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), key_data_len); + 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", mic, mic_len); +#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, key_data_len; + const struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + struct wpa_eapol_key_192 *key192; + u16 key_info, ver; + u8 *tmp = NULL; + int ret = -1; + struct wpa_peerkey *peerkey = NULL; + u8 *key_data; + size_t mic_len, keyhdrlen; + +#ifdef CONFIG_IEEE80211R + sm->ft_completed = 0; +#endif /* CONFIG_IEEE80211R */ + + mic_len = wpa_mic_len(sm->key_mgmt); + keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); + + if (len < sizeof(*hdr) + keyhdrlen) { + 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) + keyhdrlen); + return 0; + } + + hdr = (const struct ieee802_1x_hdr *) buf; + 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; + } + wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len); + if (plen > len - sizeof(*hdr) || plen < keyhdrlen) { + 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 (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); + } + + /* + * Make a copy of the frame since we need to modify the buffer during + * MAC validation and Key Data decryption. + */ + tmp = os_malloc(data_len); + if (tmp == NULL) + goto out; + os_memcpy(tmp, buf, data_len); + key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr)); + key192 = (struct wpa_eapol_key_192 *) + (tmp + sizeof(struct ieee802_1x_hdr)); + if (mic_len == 24) + key_data = (u8 *) (key192 + 1); + else + key_data = (u8 *) (key + 1); + + 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; + } + + if (mic_len == 24) + key_data_len = WPA_GET_BE16(key192->key_data_length); + else + key_data_len = WPA_GET_BE16(key->key_data_length); + wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len); + + if (key_data_len > plen - keyhdrlen) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " + "frame - key_data overflow (%u > %u)", + (unsigned int) key_data_len, + (unsigned int) (plen - keyhdrlen)); + goto out; + } + + eapol_sm_notify_lower_layer_success(sm->eapol, 0); + 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_key_mgmt_suite_b(sm->key_mgmt) && + sm->key_mgmt != WPA_KEY_MGMT_OSEN) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Unsupported EAPOL-Key descriptor version %d", + ver); + goto out; + } + + if (sm->key_mgmt == WPA_KEY_MGMT_OSEN && + ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "OSEN: Unsupported EAPOL-Key descriptor version %d", + ver); + goto out; + } + + if (wpa_key_mgmt_suite_b(sm->key_mgmt) && + ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)", + 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 && + sm->key_mgmt != WPA_KEY_MGMT_OSEN && + !wpa_key_mgmt_suite_b(sm->key_mgmt)) { + 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 && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && + 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 if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used"); + } else + goto out; + } else if (sm->pairwise_cipher == WPA_CIPHER_GCMP && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: GCMP is used, but EAPOL-Key " + "descriptor version (%d) is not 2", ver); + goto out; + } + +#ifdef CONFIG_PEERKEY + for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { + if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) + break; + } + + if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) { + if (!peerkey->initiator && peerkey->replay_counter_set && + os_memcmp(key->replay_counter, peerkey->replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSN: EAPOL-Key Replay Counter did not " + "increase (STK) - dropping packet"); + goto out; + } else if (peerkey->initiator) { + u8 _tmp[WPA_REPLAY_COUNTER_LEN]; + os_memcpy(_tmp, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN); + if (os_memcmp(_tmp, peerkey->replay_counter, + WPA_REPLAY_COUNTER_LEN) != 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: EAPOL-Key Replay " + "Counter did not match (STK) - " + "dropping packet"); + goto out; + } + } + } + + if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Ack bit in key_info from STK peer"); + goto out; + } +#endif /* CONFIG_PEERKEY */ + + if (!peerkey && sm->rx_replay_counter_set && + os_memcmp(key->replay_counter, sm->rx_replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: EAPOL-Key Replay Counter did not increase - " + "dropping packet"); + goto out; + } + + if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE)) +#ifdef CONFIG_PEERKEY + && (peerkey == NULL || !peerkey->initiator) +#endif /* CONFIG_PEERKEY */ + ) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No Ack bit in key_info"); + goto out; + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: EAPOL-Key with Request bit - dropped"); + goto out; + } + + if ((key_info & WPA_KEY_INFO_MIC) && !peerkey && + wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len)) + goto out; + +#ifdef CONFIG_PEERKEY + if ((key_info & WPA_KEY_INFO_MIC) && peerkey && + peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp, + data_len)) + goto out; +#endif /* CONFIG_PEERKEY */ + + if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) && + (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data, + &key_data_len)) + goto out; + } + + 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, + key_data, key_data_len); + } else if (key_info & WPA_KEY_INFO_MIC) { + /* 3/4 4-Way Handshake */ + wpa_supplicant_process_3_of_4(sm, key, ver, key_data, + key_data_len); + } else { + /* 1/4 4-Way Handshake */ + wpa_supplicant_process_1_of_4(sm, src_addr, key, + ver, key_data, + key_data_len); + } + } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + /* PeerKey SMK Handshake */ + peerkey_rx_eapol_smk(sm, src_addr, key, key_data_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, + key_data, key_data_len, + ver); + } else { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: EAPOL-Key (Group) without Mic bit - " + "dropped"); + } + } + + ret = 1; + +out: + bin_clear_free(tmp, data_len); + return ret; +} + + +#ifdef CONFIG_CTRL_IFACE +static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) +{ + switch (sm->key_mgmt) { + case WPA_KEY_MGMT_IEEE8021X: + return ((sm->proto == WPA_PROTO_RSN || + sm->proto == WPA_PROTO_OSEN) ? + RSN_AUTH_KEY_MGMT_UNSPEC_802_1X : + WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + case WPA_KEY_MGMT_PSK: + return (sm->proto == WPA_PROTO_RSN ? + RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X : + WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_IEEE8021X: + return RSN_AUTH_KEY_MGMT_FT_802_1X; + case WPA_KEY_MGMT_FT_PSK: + return RSN_AUTH_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + case WPA_KEY_MGMT_IEEE8021X_SHA256: + return RSN_AUTH_KEY_MGMT_802_1X_SHA256; + case WPA_KEY_MGMT_PSK_SHA256: + return RSN_AUTH_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ + case WPA_KEY_MGMT_CCKM: + return (sm->proto == WPA_PROTO_RSN ? + RSN_AUTH_KEY_MGMT_CCKM: + WPA_AUTH_KEY_MGMT_CCKM); + case WPA_KEY_MGMT_WPA_NONE: + return WPA_AUTH_KEY_MGMT_NONE; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B: + return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; + default: + return 0; + } +} + + +#define RSN_SUITE "%02x-%02x-%02x-%d" +#define RSN_SUITE_ARG(s) \ +((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff + +/** + * wpa_sm_get_mib - Dump text list of MIB entries + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @buf: Buffer for the list + * @buflen: Length of the buffer + * Returns: Number of bytes written to buffer + * + * This function is used fetch dot11 MIB variables. + */ +int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) +{ + char pmkid_txt[PMKID_LEN * 2 + 1]; + int rsna, ret; + size_t len; + + if (sm->cur_pmksa) { + wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), + sm->cur_pmksa->pmkid, PMKID_LEN); + } else + pmkid_txt[0] = '\0'; + + if ((wpa_key_mgmt_wpa_psk(sm->key_mgmt) || + wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) && + sm->proto == WPA_PROTO_RSN) + rsna = 1; + else + rsna = 0; + + ret = os_snprintf(buf, buflen, + "dot11RSNAOptionImplemented=TRUE\n" + "dot11RSNAPreauthenticationImplemented=TRUE\n" + "dot11RSNAEnabled=%s\n" + "dot11RSNAPreauthenticationEnabled=%s\n" + "dot11RSNAConfigVersion=%d\n" + "dot11RSNAConfigPairwiseKeysSupported=5\n" + "dot11RSNAConfigGroupCipherSize=%d\n" + "dot11RSNAConfigPMKLifetime=%d\n" + "dot11RSNAConfigPMKReauthThreshold=%d\n" + "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n" + "dot11RSNAConfigSATimeout=%d\n", + rsna ? "TRUE" : "FALSE", + rsna ? "TRUE" : "FALSE", + RSN_VERSION, + wpa_cipher_key_len(sm->group_cipher) * 8, + sm->dot11RSNAConfigPMKLifetime, + sm->dot11RSNAConfigPMKReauthThreshold, + sm->dot11RSNAConfigSATimeout); + if (os_snprintf_error(buflen, ret)) + return 0; + len = ret; + + ret = os_snprintf( + buf + len, buflen - len, + "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" + "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" + "dot11RSNAPMKIDUsed=%s\n" + "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" + "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n" + "dot11RSNA4WayHandshakeFailures=%u\n", + RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->group_cipher)), + pmkid_txt, + RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->group_cipher)), + sm->dot11RSNA4WayHandshakeFailures); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + + return (int) len; +} +#endif /* CONFIG_CTRL_IFACE */ + + +static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason) +{ + struct wpa_sm *sm = ctx; + int deauth = 0; + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: " + MACSTR " reason=%d", MAC2STR(entry->aa), reason); + + if (sm->cur_pmksa == entry) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: %s current PMKSA entry", + reason == PMKSA_REPLACE ? "replaced" : "removed"); + pmksa_cache_clear_current(sm); + + /* + * If an entry is simply being replaced, there's no need to + * deauthenticate because it will be immediately re-added. + * This happens when EAP authentication is completed again + * (reauth or failed PMKSA caching attempt). + */ + if (reason != PMKSA_REPLACE) + deauth = 1; + } + + if (reason == PMKSA_EXPIRE && + (sm->pmk_len == entry->pmk_len && + os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: deauthenticating due to expired PMK"); + pmksa_cache_clear_current(sm); + deauth = 1; + } + + if (deauth) { + os_memset(sm->pmk, 0, sizeof(sm->pmk)); + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); + } +} + + +/** + * wpa_sm_init - Initialize WPA state machine + * @ctx: Context pointer for callbacks; this needs to be an allocated buffer + * Returns: Pointer to the allocated WPA state machine data + * + * This function is used to allocate a new WPA state machine and the returned + * value is passed to all WPA state machine calls. + */ +struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) +{ + struct wpa_sm *sm; + + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + dl_list_init(&sm->pmksa_candidates); + sm->renew_snonce = 1; + sm->ctx = ctx; + + sm->dot11RSNAConfigPMKLifetime = 43200; + sm->dot11RSNAConfigPMKReauthThreshold = 70; + sm->dot11RSNAConfigSATimeout = 60; + + sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm); + if (sm->pmksa == NULL) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "RSN: PMKSA cache initialization failed"); + os_free(sm); + return NULL; + } + + return sm; +} + + +/** + * wpa_sm_deinit - Deinitialize WPA state machine + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void wpa_sm_deinit(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + pmksa_cache_deinit(sm->pmksa); + eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); + eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); + os_free(sm->assoc_wpa_ie); + os_free(sm->ap_wpa_ie); + os_free(sm->ap_rsn_ie); + wpa_sm_drop_sa(sm); + 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; + os_memset(&sm->ptk, 0, sizeof(sm->ptk)); + sm->tptk_set = 0; + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + } + +#ifdef CONFIG_TDLS + wpa_tdls_assoc(sm); +#endif /* CONFIG_TDLS */ + +#ifdef CONFIG_P2P + os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr)); +#endif /* CONFIG_P2P */ +} + + +/** + * 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) +{ + eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); + eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); + peerkey_deinit(sm); + rsn_preauth_deinit(sm); + pmksa_cache_clear_current(sm); + if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) + sm->dot11RSNA4WayHandshakeFailures++; +#ifdef CONFIG_TDLS + wpa_tdls_disassoc(sm); +#endif /* CONFIG_TDLS */ + + /* Keys are not needed in the WPA state machine anymore */ + wpa_sm_drop_sa(sm); + + sm->msg_3_of_4_ok = 0; +} + + +/** + * 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 + * @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK + * + * Configure the PMK for WPA state machine. + */ +void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, + const u8 *bssid) +{ + 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 */ + + if (bssid) { + pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 0, + bssid, sm->own_addr, + sm->network_ctx, sm->key_mgmt); + } +} + + +/** + * 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; + sm->p2p = config->p2p; + } 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; + sm->p2p = 0; + } +} + + +/** + * wpa_sm_set_own_addr - Set own MAC address + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @addr: Own MAC address + */ +void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) +{ + if (sm) + os_memcpy(sm->own_addr, addr, ETH_ALEN); +} + + +/** + * wpa_sm_set_ifname - Set network interface name + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ifname: Interface name + * @bridge_ifname: Optional bridge interface name (for pre-auth) + */ +void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, + const char *bridge_ifname) +{ + if (sm) { + sm->ifname = ifname; + sm->bridge_ifname = bridge_ifname; + } +} + + +/** + * wpa_sm_set_eapol - Set EAPOL state machine pointer + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init() + */ +void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol) +{ + if (sm) + sm->eapol = eapol; +} + + +/** + * wpa_sm_set_param - Set WPA state machine parameters + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @param: Parameter field + * @value: Parameter value + * Returns: 0 on success, -1 on failure + */ +int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, + unsigned int value) +{ + int ret = 0; + + if (sm == NULL) + return -1; + + switch (param) { + case RSNA_PMK_LIFETIME: + if (value > 0) + sm->dot11RSNAConfigPMKLifetime = value; + else + ret = -1; + break; + case RSNA_PMK_REAUTH_THRESHOLD: + if (value > 0 && value <= 100) + sm->dot11RSNAConfigPMKReauthThreshold = value; + else + ret = -1; + break; + case RSNA_SA_TIMEOUT: + if (value > 0) + sm->dot11RSNAConfigSATimeout = value; + else + ret = -1; + break; + case WPA_PARAM_PROTO: + sm->proto = value; + break; + case WPA_PARAM_PAIRWISE: + sm->pairwise_cipher = value; + break; + case WPA_PARAM_GROUP: + sm->group_cipher = value; + break; + case WPA_PARAM_KEY_MGMT: + sm->key_mgmt = value; + break; +#ifdef CONFIG_IEEE80211W + case WPA_PARAM_MGMT_GROUP: + sm->mgmt_group_cipher = value; + break; +#endif /* CONFIG_IEEE80211W */ + case WPA_PARAM_RSN_ENABLED: + sm->rsn_enabled = value; + break; + case WPA_PARAM_MFP: + sm->mfp = value; + break; + default: + break; + } + + return ret; +} + + +/** + * wpa_sm_get_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 (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) { + struct wpa_ie_data rsn; + if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) + >= 0 && + rsn.capabilities & (WPA_CAPABILITY_MFPR | + WPA_CAPABILITY_MFPC)) { + ret = os_snprintf(pos, end - pos, "pmf=%d\n", + (rsn.capabilities & + WPA_CAPABILITY_MFPR) ? 2 : 1); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + } + + return pos - buf; +} + + +int wpa_sm_pmf_enabled(struct wpa_sm *sm) +{ + struct wpa_ie_data rsn; + + if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !sm->ap_rsn_ie) + return 0; + + if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 && + rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) + return 1; + + return 0; +} + + +/** + * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @wpa_ie: Pointer to buffer for WPA/RSN IE + * @wpa_ie_len: Pointer to the length of the wpa_ie buffer + * Returns: 0 on success, -1 on failure + */ +int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, + size_t *wpa_ie_len) +{ + int res; + + if (sm == NULL) + return -1; + + res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len); + if (res < 0) + return -1; + *wpa_ie_len = res; + + wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default", + wpa_ie, *wpa_ie_len); + + if (sm->assoc_wpa_ie == NULL) { + /* + * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets + * the correct version of the IE even if PMKSA caching is + * aborted (which would remove PMKID from IE generation). + */ + sm->assoc_wpa_ie = os_malloc(*wpa_ie_len); + if (sm->assoc_wpa_ie == NULL) + return -1; + + os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len); + sm->assoc_wpa_ie_len = *wpa_ie_len; + } + + return 0; +} + + +/** + * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the WPA/RSN IE used in (Re)Association + * Request frame. The IE will be used to override the default value generated + * with wpa_sm_set_assoc_wpa_ie_default(). + */ +int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (sm == NULL) + return -1; + + os_free(sm->assoc_wpa_ie); + if (ie == NULL || len == 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing own WPA/RSN IE"); + sm->assoc_wpa_ie = NULL; + sm->assoc_wpa_ie_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len); + sm->assoc_wpa_ie = os_malloc(len); + if (sm->assoc_wpa_ie == NULL) + return -1; + + os_memcpy(sm->assoc_wpa_ie, ie, len); + sm->assoc_wpa_ie_len = len; + } + + return 0; +} + + +/** + * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the WPA IE used in Beacon / Probe Response + * frame. + */ +int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (sm == NULL) + return -1; + + os_free(sm->ap_wpa_ie); + if (ie == NULL || len == 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing AP WPA IE"); + sm->ap_wpa_ie = NULL; + sm->ap_wpa_ie_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len); + sm->ap_wpa_ie = os_malloc(len); + if (sm->ap_wpa_ie == NULL) + return -1; + + os_memcpy(sm->ap_wpa_ie, ie, len); + sm->ap_wpa_ie_len = len; + } + + return 0; +} + + +/** + * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the RSN IE used in Beacon / Probe Response + * frame. + */ +int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (sm == NULL) + return -1; + + os_free(sm->ap_rsn_ie); + if (ie == NULL || len == 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing AP RSN IE"); + sm->ap_rsn_ie = NULL; + sm->ap_rsn_ie_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len); + sm->ap_rsn_ie = os_malloc(len); + if (sm->ap_rsn_ie == NULL) + return -1; + + os_memcpy(sm->ap_rsn_ie, ie, len); + sm->ap_rsn_ie_len = len; + } + + return 0; +} + + +/** + * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @data: Pointer to data area for parsing results + * Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure + * + * Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the + * parsed data into data. + */ +int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data) +{ + if (sm == NULL) + return -1; + + if (sm->assoc_wpa_ie == NULL) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: No WPA/RSN IE available from association info"); + return -1; + } + if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data)) + return -2; + return 0; +} + + +int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) +{ + return pmksa_cache_list(sm->pmksa, buf, len); +} + + +void wpa_sm_drop_sa(struct wpa_sm *sm) +{ + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK"); + sm->ptk_set = 0; + sm->tptk_set = 0; + os_memset(sm->pmk, 0, sizeof(sm->pmk)); + os_memset(&sm->ptk, 0, sizeof(sm->ptk)); + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); +#ifdef CONFIG_IEEE80211R + os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); + os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0)); + os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); +#endif /* CONFIG_IEEE80211R */ +} + + +int wpa_sm_has_ptk(struct wpa_sm *sm) +{ + if (sm == NULL) + return 0; + return sm->ptk_set; +} + + +void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr) +{ + os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN); +} + + +void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) +{ + pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0); +} + + +#ifdef CONFIG_WNM +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) +{ + u16 keyinfo; + u8 keylen; /* plaintext key len */ + u8 *key_rsc; + + if (subelem_id == WNM_SLEEP_SUBELEM_GTK) { + struct wpa_gtk_data gd; + + os_memset(&gd, 0, sizeof(gd)); + keylen = wpa_cipher_key_len(sm->group_cipher); + gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher); + gd.alg = wpa_cipher_to_alg(sm->group_cipher); + if (gd.alg == WPA_ALG_NONE) { + wpa_printf(MSG_DEBUG, "Unsupported group cipher suite"); + return -1; + } + + key_rsc = buf + 5; + keyinfo = WPA_GET_LE16(buf + 2); + gd.gtk_len = keylen; + if (gd.gtk_len != buf[4]) { + wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d", + gd.gtk_len, buf[4]); + return -1; + } + gd.keyidx = keyinfo & 0x03; /* B0 - B1 */ + gd.tx = wpa_supplicant_gtk_tx_bit_workaround( + sm, !!(keyinfo & WPA_KEY_INFO_TXRX)); + + os_memcpy(gd.gtk, buf + 13, gd.gtk_len); + + wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)", + gd.gtk, gd.gtk_len); + if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) { + os_memset(&gd, 0, sizeof(gd)); + wpa_printf(MSG_DEBUG, "Failed to install the GTK in " + "WNM mode"); + return -1; + } + os_memset(&gd, 0, sizeof(gd)); +#ifdef CONFIG_IEEE80211W + } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { + struct wpa_igtk_kde igd; + u16 keyidx; + + os_memset(&igd, 0, sizeof(igd)); + keylen = wpa_cipher_key_len(sm->mgmt_group_cipher); + os_memcpy(igd.keyid, buf + 2, 2); + os_memcpy(igd.pn, buf + 4, 6); + + keyidx = WPA_GET_LE16(igd.keyid); + os_memcpy(igd.igtk, buf + 10, keylen); + + wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)", + igd.igtk, keylen); + if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), + broadcast_ether_addr, + keyidx, 0, igd.pn, sizeof(igd.pn), + igd.igtk, keylen) < 0) { + wpa_printf(MSG_DEBUG, "Failed to install the IGTK in " + "WNM mode"); + os_memset(&igd, 0, sizeof(igd)); + return -1; + } + os_memset(&igd, 0, sizeof(igd)); +#endif /* CONFIG_IEEE80211W */ + } else { + wpa_printf(MSG_DEBUG, "Unknown element id"); + return -1; + } + + return 0; +} +#endif /* CONFIG_WNM */ + + +#ifdef CONFIG_PEERKEY +int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_peerkey *peerkey; + + for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { + if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) + break; + } + + if (!peerkey) + return 0; + + wpa_sm_rx_eapol(sm, src_addr, buf, len); + + return 1; +} +#endif /* CONFIG_PEERKEY */ + + +#ifdef CONFIG_P2P + +int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf) +{ + if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0) + return -1; + os_memcpy(buf, sm->p2p_ip_addr, 3 * 4); + return 0; +} + +#endif /* CONFIG_P2P */ + + +void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter) +{ + if (rx_replay_counter == NULL) + return; + + os_memcpy(sm->rx_replay_counter, rx_replay_counter, + WPA_REPLAY_COUNTER_LEN); + sm->rx_replay_counter_set = 1; + wpa_printf(MSG_DEBUG, "Updated key replay counter"); +} + + +void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, + const u8 *ptk_kck, size_t ptk_kck_len, + const u8 *ptk_kek, size_t ptk_kek_len) +{ + if (ptk_kck && ptk_kck_len <= WPA_KCK_MAX_LEN) { + os_memcpy(sm->ptk.kck, ptk_kck, ptk_kck_len); + sm->ptk.kck_len = ptk_kck_len; + wpa_printf(MSG_DEBUG, "Updated PTK KCK"); + } + if (ptk_kek && ptk_kek_len <= WPA_KEK_MAX_LEN) { + os_memcpy(sm->ptk.kek, ptk_kek, ptk_kek_len); + sm->ptk.kek_len = ptk_kek_len; + wpa_printf(MSG_DEBUG, "Updated PTK KEK"); + } + sm->ptk_set = 1; +} diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa.h b/freebsd/contrib/wpa/src/rsn_supp/wpa.h new file mode 100644 index 00000000..e163b701 --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/wpa.h @@ -0,0 +1,421 @@ +/* + * wpa_supplicant - WPA definitions + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_H +#define WPA_H + +#include "common/defs.h" +#include "common/eapol_common.h" +#include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" + +struct wpa_sm; +struct eapol_sm; +struct wpa_config_blob; +struct hostapd_freq_params; + +struct wpa_sm_ctx { + void *ctx; /* pointer to arbitrary upper level context */ + void *msg_ctx; /* upper level context for wpa_msg() calls */ + + void (*set_state)(void *ctx, enum wpa_states state); + enum wpa_states (*get_state)(void *ctx); + void (*deauthenticate)(void * ctx, int reason_code); + int (*set_key)(void *ctx, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); + void * (*get_network_ctx)(void *ctx); + int (*get_bssid)(void *ctx, u8 *bssid); + int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf, + size_t len); + int (*get_beacon_ie)(void *ctx); + void (*cancel_auth_timeout)(void *ctx); + u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len, + size_t *msg_len, void **data_pos); + int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); + int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); + void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); + const struct wpa_config_blob * (*get_config_blob)(void *ctx, + const char *name); + int (*mlme_setprotection)(void *ctx, const u8 *addr, + int protection_type, int key_type); + int (*update_ft_ies)(void *ctx, const u8 *md, const u8 *ies, + size_t ies_len); + int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap, + const u8 *ies, size_t ies_len); + int (*mark_authenticated)(void *ctx, const u8 *target_ap); +#ifdef CONFIG_TDLS + int (*tdls_get_capa)(void *ctx, int *tdls_supported, + int *tdls_ext_setup, int *tdls_chan_switch); + int (*send_tdls_mgmt)(void *ctx, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capab, + int initiator, const u8 *buf, size_t len); + int (*tdls_oper)(void *ctx, int oper, const u8 *peer); + int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid, + u16 capability, const u8 *supp_rates, + size_t supp_rates_len, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u8 qosinfo, int wmm, const u8 *ext_capab, + size_t ext_capab_len, const u8 *supp_channels, + size_t supp_channels_len, + const u8 *supp_oper_classes, + size_t supp_oper_classes_len); + int (*tdls_enable_channel_switch)( + void *ctx, const u8 *addr, u8 oper_class, + const struct hostapd_freq_params *params); + int (*tdls_disable_channel_switch)(void *ctx, const u8 *addr); +#endif /* CONFIG_TDLS */ + void (*set_rekey_offload)(void *ctx, const u8 *kek, size_t kek_len, + const u8 *kck, size_t kck_len, + const u8 *replay_ctr); + int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len); +}; + + +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; + int p2p; +}; + +#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, + const u8 *bssid); +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); + +int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose); +int wpa_sm_pmf_enabled(struct wpa_sm *sm); + +void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise); + +int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data); + +void wpa_sm_aborted_cached(struct wpa_sm *sm); +int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len); +int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data); +int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); +void wpa_sm_drop_sa(struct wpa_sm *sm); +int wpa_sm_has_ptk(struct wpa_sm *sm); + +void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr); + +void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); + +int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf); + +void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter); +void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, + const u8 *ptk_kck, size_t ptk_kck_len, + const u8 *ptk_kek, size_t ptk_kek_len); + +#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 int wpa_sm_get_status(struct wpa_sm *sm, char *buf, + size_t buflen, int verbose) +{ + return 0; +} + +static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm) +{ + return 0; +} + +static inline void wpa_sm_key_request(struct wpa_sm *sm, int error, + int pairwise) +{ +} + +static inline int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + return -1; +} + +static inline void wpa_sm_aborted_cached(struct wpa_sm *sm) +{ +} + +static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + return -1; +} + +static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, + struct wpa_ie_data *data) +{ + return -1; +} + +static inline int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, + size_t len) +{ + return -1; +} + +static inline void wpa_sm_drop_sa(struct wpa_sm *sm) +{ +} + +static inline int wpa_sm_has_ptk(struct wpa_sm *sm) +{ + return 0; +} + +static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm, + const u8 *replay_ctr) +{ +} + +static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, + void *network_ctx) +{ +} + +static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, + const u8 *rx_replay_counter) +{ +} + +static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, + const u8 *ptk_kek) +{ +} + +#endif /* CONFIG_NO_WPA */ + +#ifdef CONFIG_PEERKEY +int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer); +int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len); +#else /* CONFIG_PEERKEY */ +static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) +{ + return -1; +} + +static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + return 0; +} +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211R + +int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len); +int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie); +int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, + int ft_action, const u8 *target_ap, + const u8 *ric_ies, size_t ric_ies_len); +int wpa_ft_is_completed(struct wpa_sm *sm); +void wpa_reset_ft_completed(struct wpa_sm *sm); +int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, + size_t ies_len, const u8 *src_addr); +int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, + const u8 *mdie); + +#else /* CONFIG_IEEE80211R */ + +static inline int +wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) +{ + return 0; +} + +static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm, + const u8 *mdie) +{ + return 0; +} + +static inline int +wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, + int ft_action, const u8 *target_ap) +{ + return 0; +} + +static inline int wpa_ft_is_completed(struct wpa_sm *sm) +{ + return 0; +} + +static inline void wpa_reset_ft_completed(struct wpa_sm *sm) +{ +} + +static inline int +wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, + const u8 *src_addr) +{ + return -1; +} + +#endif /* CONFIG_IEEE80211R */ + + +/* tdls.c */ +void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len); +void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len); +int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr); +void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code); +int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_init(struct wpa_sm *sm); +void wpa_tdls_teardown_peers(struct wpa_sm *sm); +void wpa_tdls_deinit(struct wpa_sm *sm); +void wpa_tdls_enable(struct wpa_sm *sm, int enabled); +void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr); +const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_is_external_setup(struct wpa_sm *sm); +int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr, + u8 oper_class, + struct hostapd_freq_params *freq_params); +int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr); + +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); + +#endif /* WPA_H */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa_ft.c b/freebsd/contrib/wpa/src/rsn_supp/wpa_ft.c new file mode 100644 index 00000000..509a20e0 --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/wpa_ft.c @@ -0,0 +1,849 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * WPA Supplicant - IEEE 802.11r - Fast BSS Transition + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/aes_wrap.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "wpa.h" +#include "wpa_i.h" + +#ifdef CONFIG_IEEE80211R + +int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, struct wpa_ptk *ptk) +{ + 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); + return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, + sm->bssid, sm->pmk_r1_name, ptk, ptk_name, + sm->key_mgmt, sm->pairwise_cipher); +} + + +/** + * 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 + * @kck_len: KCK length in octets + * @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, size_t kck_len, + 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 (!wpa_cipher_valid_group(sm->group_cipher)) { + wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", + sm->group_cipher); + os_free(buf); + return NULL; + } + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->group_cipher)); + pos += RSN_SELECTOR_LEN; + + /* Pairwise Suite Count */ + WPA_PUT_LE16(pos, 1); + pos += 2; + + /* Pairwise Suite List */ + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { + wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", + sm->pairwise_cipher); + os_free(buf); + return NULL; + } + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->pairwise_cipher)); + pos += RSN_SELECTOR_LEN; + + /* Authenticated Key Management Suite Count */ + WPA_PUT_LE16(pos, 1); + pos += 2; + + /* Authenticated Key Management Suite List */ + if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); + else { + wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)", + sm->key_mgmt); + 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, kck_len, sm->own_addr, target_ap, 5, + ((u8 *) mdie) - 2, 2 + sizeof(*mdie), + ftie_pos, 2 + *ftie_len, + (u8 *) rsnie, 2 + rsnie->len, ric_ies, + ric_ies_len, ftie->mic) < 0) { + wpa_printf(MSG_INFO, "FT: Failed to calculate MIC"); + os_free(buf); + return NULL; + } + } + + *len = pos - buf; + + return buf; +} + + +static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) +{ + int keylen; + enum wpa_alg alg; + u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 }; + + wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver."); + + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { + wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d", + sm->pairwise_cipher); + return -1; + } + + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + + if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, + sizeof(null_rsc), (u8 *) sm->ptk.tk, 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, 0, 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; + 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 (!wpa_key_mgmt_ft(sm->key_mgmt)) { + wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " + "enabled for this connection"); + return -1; + } + + 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_const(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_const(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; + if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, + sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk, + ptk_name, sm->key_mgmt, sm->pairwise_cipher) < 0) + return -1; + + ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, + sm->pmk_r1_name, + sm->ptk.kck, sm->ptk.kck_len, 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 (!wpa_key_mgmt_ft(sm->key_mgmt)) + return 0; + + return sm->ft_completed; +} + + +void wpa_reset_ft_completed(struct wpa_sm *sm) +{ + if (sm != NULL) + sm->ft_completed = 0; +} + + +static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, + size_t gtk_elem_len) +{ + u8 gtk[32]; + int keyidx; + 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, sm->ptk.kek_len, gtk_len / 8, gtk_elem + 11, + gtk)) { + wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " + "decrypt GTK"); + return -1; + } + + keylen = wpa_cipher_key_len(sm->group_cipher); + rsc_len = wpa_cipher_rsc_len(sm->group_cipher); + alg = wpa_cipher_to_alg(sm->group_cipher); + if (alg == WPA_ALG_NONE) { + wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", + sm->group_cipher); + return -1; + } + + if (gtk_len < keylen) { + wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE"); + return -1; + } + + /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */ + + keyidx = WPA_GET_LE16(gtk_elem) & 0x03; + + if (gtk_elem[2] != keylen) { + wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d " + "negotiated %lu", + gtk_elem[2], (unsigned long) keylen); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); + if (sm->group_cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + u8 tmp[8]; + os_memcpy(tmp, gtk + 16, 8); + os_memcpy(gtk + 16, gtk + 24, 8); + os_memcpy(gtk + 24, tmp, 8); + } + if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0, + gtk_elem + 3, rsc_len, gtk, keylen) < 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the " + "driver."); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_IEEE80211W +static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, + size_t igtk_elem_len) +{ + u8 igtk[WPA_IGTK_LEN]; + u16 keyidx; + + if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) + return 0; + + if (igtk_elem == NULL) { + wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE"); + return 0; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp", + igtk_elem, igtk_elem_len); + + if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) { + wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem " + "length %lu", (unsigned long) igtk_elem_len); + return -1; + } + if (igtk_elem[8] != WPA_IGTK_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length " + "%d", igtk_elem[8]); + return -1; + } + + if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, 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[WPA_EAPOL_KEY_MIC_MAX_LEN]; + + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); + + if (!wpa_key_mgmt_ft(sm->key_mgmt)) { + wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " + "enabled for this connection"); + return -1; + } + + 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_const(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_const(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_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) + { + wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " + "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); + return -1; + } + + count = 3; + if (parse.ric) + count += ieee802_11_ie_count(parse.ric, parse.ric_len); + if (ftie->mic_control[1] != count) { + wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " + "Control: received %u expected %u", + ftie->mic_control[1], count); + return -1; + } + + if (wpa_ft_mic(sm->ptk.kck, sm->ptk.kck_len, 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_const(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); + wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); + return -1; + } + + if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) + return -1; + +#ifdef CONFIG_IEEE80211W + if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0) + return -1; +#endif /* CONFIG_IEEE80211W */ + + if (sm->set_ptk_after_assoc) { + wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we " + "are associated"); + if (wpa_ft_install_ptk(sm, src_addr) < 0) + return -1; + sm->set_ptk_after_assoc = 0; + } + + if (parse.ric) { + wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response", + parse.ric, parse.ric_len); + /* TODO: parse response and inform driver about results when + * using wpa_supplicant SME */ + } + + wpa_printf(MSG_DEBUG, "FT: Completed successfully"); + + return 0; +} + + +/** + * wpa_ft_start_over_ds - Generate over-the-DS auth request + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @target_ap: Target AP Address + * @mdie: Mobility Domain IE from the target AP + * Returns: 0 on success, -1 on failure + */ +int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, + const u8 *mdie) +{ + u8 *ft_ies; + size_t ft_ies_len; + + wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR, + MAC2STR(target_ap)); + + /* Generate a new SNonce */ + if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); + return -1; + } + + ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, + NULL, 0, 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/freebsd/contrib/wpa/src/rsn_supp/wpa_i.h b/freebsd/contrib/wpa/src/rsn_supp/wpa_i.h new file mode 100644 index 00000000..965a9c1d --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/wpa_i.h @@ -0,0 +1,371 @@ +/* + * Internal WPA/RSN supplicant state machine definitions + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_I_H +#define WPA_I_H + +#include "utils/list.h" + +struct wpa_peerkey; +struct wpa_tdls_peer; +struct wpa_eapol_key; + +/** + * struct wpa_sm - Internal WPA state machine data + */ +struct wpa_sm { + u8 pmk[PMK_LEN]; + size_t pmk_len; + struct wpa_ptk ptk, tptk; + int ptk_set, tptk_set; + unsigned int msg_3_of_4_ok:1; + 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; + int p2p; + + 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_chan_switch_prohibited; + int tdls_disabled; + + /* The driver supports TDLS */ + int tdls_supported; + + /* + * The driver requires explicit discovery/setup/teardown frames sent + * to it via tdls_mgmt. + */ + int tdls_external_setup; + + /* The driver supports TDLS channel switching */ + int tdls_chan_switch; +#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 */ + +#ifdef CONFIG_P2P + u8 p2p_ip_addr[3 * 4]; +#endif /* CONFIG_P2P */ +}; + + +static inline void wpa_sm_set_state(struct wpa_sm *sm, enum wpa_states state) +{ + WPA_ASSERT(sm->ctx->set_state); + sm->ctx->set_state(sm->ctx->ctx, state); +} + +static inline enum wpa_states wpa_sm_get_state(struct wpa_sm *sm) +{ + WPA_ASSERT(sm->ctx->get_state); + return sm->ctx->get_state(sm->ctx->ctx); +} + +static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code) +{ + WPA_ASSERT(sm->ctx->deauthenticate); + sm->ctx->deauthenticate(sm->ctx->ctx, reason_code); +} + +static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + WPA_ASSERT(sm->ctx->set_key); + return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx, + seq, seq_len, key, key_len); +} + +static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm) +{ + WPA_ASSERT(sm->ctx->get_network_ctx); + return sm->ctx->get_network_ctx(sm->ctx->ctx); +} + +static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid) +{ + WPA_ASSERT(sm->ctx->get_bssid); + return sm->ctx->get_bssid(sm->ctx->ctx, bssid); +} + +static inline int wpa_sm_ether_send(struct wpa_sm *sm, const u8 *dest, + u16 proto, const u8 *buf, size_t len) +{ + WPA_ASSERT(sm->ctx->ether_send); + return sm->ctx->ether_send(sm->ctx->ctx, dest, proto, buf, len); +} + +static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm) +{ + WPA_ASSERT(sm->ctx->get_beacon_ie); + return sm->ctx->get_beacon_ie(sm->ctx->ctx); +} + +static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm) +{ + WPA_ASSERT(sm->ctx->cancel_auth_timeout); + sm->ctx->cancel_auth_timeout(sm->ctx->ctx); +} + +static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + WPA_ASSERT(sm->ctx->alloc_eapol); + return sm->ctx->alloc_eapol(sm->ctx->ctx, type, data, data_len, + msg_len, data_pos); +} + +static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid, + const u8 *pmkid) +{ + WPA_ASSERT(sm->ctx->add_pmkid); + return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid); +} + +static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid, + const u8 *pmkid) +{ + WPA_ASSERT(sm->ctx->remove_pmkid); + return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid); +} + +static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr, + int protect_type, int key_type) +{ + WPA_ASSERT(sm->ctx->mlme_setprotection); + return sm->ctx->mlme_setprotection(sm->ctx->ctx, addr, protect_type, + key_type); +} + +static inline int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md, + const u8 *ies, size_t ies_len) +{ + if (sm->ctx->update_ft_ies) + return sm->ctx->update_ft_ies(sm->ctx->ctx, md, ies, ies_len); + return -1; +} + +static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action, + const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + if (sm->ctx->send_ft_action) + return sm->ctx->send_ft_action(sm->ctx->ctx, action, target_ap, + ies, ies_len); + return -1; +} + +static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm, + const u8 *target_ap) +{ + if (sm->ctx->mark_authenticated) + return sm->ctx->mark_authenticated(sm->ctx->ctx, target_ap); + return -1; +} + +static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm) +{ + if (!sm->ctx->set_rekey_offload) + return; + sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek, sm->ptk.kek_len, + sm->ptk.kck, sm->ptk.kck_len, + sm->rx_replay_counter); +} + +#ifdef CONFIG_TDLS +static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm, + int *tdls_supported, + int *tdls_ext_setup, + int *tdls_chan_switch) +{ + if (sm->ctx->tdls_get_capa) + return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported, + tdls_ext_setup, tdls_chan_switch); + return -1; +} + +static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capab, + int initiator, 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, + peer_capab, initiator, buf, + len); + return -1; +} + +static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper, + const u8 *peer) +{ + if (sm->ctx->tdls_oper) + return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer); + return -1; +} + +static inline int +wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, + u16 aid, u16 capability, const u8 *supp_rates, + size_t supp_rates_len, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u8 qosinfo, int wmm, const u8 *ext_capab, + size_t ext_capab_len, const u8 *supp_channels, + size_t supp_channels_len, const u8 *supp_oper_classes, + size_t supp_oper_classes_len) +{ + if (sm->ctx->tdls_peer_addset) + return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, + aid, capability, supp_rates, + supp_rates_len, ht_capab, + vht_capab, qosinfo, wmm, + ext_capab, ext_capab_len, + supp_channels, + supp_channels_len, + supp_oper_classes, + supp_oper_classes_len); + return -1; +} + +static inline int +wpa_sm_tdls_enable_channel_switch(struct wpa_sm *sm, const u8 *addr, + u8 oper_class, + const struct hostapd_freq_params *freq_params) +{ + if (sm->ctx->tdls_enable_channel_switch) + return sm->ctx->tdls_enable_channel_switch(sm->ctx->ctx, addr, + oper_class, + freq_params); + return -1; +} + +static inline int +wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr) +{ + if (sm->ctx->tdls_disable_channel_switch) + return sm->ctx->tdls_disable_channel_switch(sm->ctx->ctx, addr); + return -1; +} +#endif /* CONFIG_TDLS */ + +static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm, + const u8 *pmk, size_t pmk_len) +{ + if (!sm->proactive_key_caching) + return 0; + if (!sm->ctx->key_mgmt_set_pmk) + return -1; + return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len); +} + +void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, + 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, + 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); + +void wpa_tdls_assoc(struct wpa_sm *sm); +void wpa_tdls_disassoc(struct wpa_sm *sm); + +#endif /* WPA_I_H */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c b/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c new file mode 100644 index 00000000..2a22d7de --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c @@ -0,0 +1,607 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * wpa_supplicant - WPA/RSN IE and KDE processing + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa.h" +#include "pmksa_cache.h" +#include "common/ieee802_11_defs.h" +#include "wpa_i.h" +#include "wpa_ie.h" + + +/** + * wpa_parse_wpa_ie - Parse WPA/RSN IE + * @wpa_ie: Pointer to WPA or RSN IE + * @wpa_ie_len: Length of the WPA/RSN IE + * @data: Pointer to data area for parsing results + * Returns: 0 on success, -1 on failure + * + * Parse the contents of WPA or RSN IE and write the parsed data into data. + */ +int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN) + return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); + if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC && + wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE) + return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); + else + return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); +} + + +static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt) +{ + u8 *pos; + struct wpa_ie_hdr *hdr; + u32 suite; + + if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN + + 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN) + return -1; + + hdr = (struct wpa_ie_hdr *) wpa_ie; + hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; + RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); + WPA_PUT_LE16(hdr->version, WPA_VERSION); + pos = (u8 *) (hdr + 1); + + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher); + if (suite == 0) { + wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", + group_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += WPA_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher); + if (suite == 0 || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { + wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", + pairwise_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += WPA_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE); + } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM); + } else { + wpa_printf(MSG_WARNING, "Invalid key management type (%d).", + key_mgmt); + return -1; + } + pos += WPA_SELECTOR_LEN; + + /* WPA Capabilities; use defaults, so no need to include it */ + + hdr->len = (pos - wpa_ie) - 2; + + WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); + + return pos - wpa_ie; +} + + +static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt, int mgmt_group_cipher, + struct wpa_sm *sm) +{ + u8 *pos; + struct rsn_ie_hdr *hdr; + u16 capab; + u32 suite; + + if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN + + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + + (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) { + wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)", + (unsigned long) rsn_ie_len); + return -1; + } + + hdr = (struct rsn_ie_hdr *) rsn_ie; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); + if (suite == 0) { + wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", + group_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); + if (suite == 0 || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { + wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", + pairwise_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM); +#ifdef CONFIG_IEEE80211R + } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); + } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + } else if (key_mgmt == WPA_KEY_MGMT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); + } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); +#endif /* CONFIG_SAE */ + } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192); + } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B); + } 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 (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) { + if (!sm->cur_pmksa) { + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + mgmt_group_cipher)); + 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; +} + + +#ifdef CONFIG_HS20 +static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt) +{ + u8 *pos, *len; + u32 suite; + + if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN + + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN) + return -1; + + pos = wpa_ie; + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + len = pos++; /* to be filled */ + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + *pos++ = HS20_OSEN_OUI_TYPE; + + /* Group Data Cipher Suite */ + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); + if (suite == 0) { + wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", + group_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + /* Pairwise Cipher Suite Count and List */ + WPA_PUT_LE16(pos, 1); + pos += 2; + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); + if (suite == 0 || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { + wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", + pairwise_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + /* AKM Suite Count and List */ + WPA_PUT_LE16(pos, 1); + pos += 2; + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); + pos += RSN_SELECTOR_LEN; + + *len = pos - len - 1; + + WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); + + return pos - wpa_ie; +} +#endif /* CONFIG_HS20 */ + + +/** + * 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); +#ifdef CONFIG_HS20 + else if (sm->proto == WPA_PROTO_OSEN) + return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len, + sm->pairwise_cipher, + sm->group_cipher, + sm->key_mgmt); +#endif /* CONFIG_HS20 */ + else + return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len, + sm->pairwise_cipher, + sm->group_cipher, + sm->key_mgmt); +} + + +/** + * wpa_parse_vendor_specific - Parse Vendor Specific 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_vendor_specific(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + unsigned int oui; + + if (pos[1] < 4) { + wpa_printf(MSG_MSGDUMP, "Too short vendor specific IE ignored (len=%u)", + pos[1]); + return 1; + } + + oui = WPA_GET_BE24(&pos[2]); + if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) { + if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) { + ie->wmm = &pos[2]; + ie->wmm_len = pos[1]; + wpa_hexdump(MSG_DEBUG, "WPA: WMM IE", + ie->wmm, ie->wmm_len); + } else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) { + ie->wmm = &pos[2]; + ie->wmm_len = pos[1]; + wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element", + ie->wmm, ie->wmm_len); + } + } + return 0; +} + + +/** + * 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 */ + +#ifdef CONFIG_P2P + if (pos[1] >= RSN_SELECTOR_LEN + 1 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) { + ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key", + ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN); + return 0; + } + + if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) { + ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, + "WPA: IP Address Allocation in EAPOL-Key", + ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN); + return 0; + } +#endif /* CONFIG_P2P */ + + return 0; +} + + +/** + * 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 && + pos[1] >= sizeof(struct rsn_mdie)) { + 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 && + pos[1] >= sizeof(struct rsn_ftie)) { + ie->ftie = pos; + ie->ftie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key", + ie->ftie, ie->ftie_len); + } else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) { + if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) { + ie->reassoc_deadline = pos; + wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline " + "in EAPOL-Key", + ie->reassoc_deadline, pos[1] + 2); + } else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) { + ie->key_lifetime = pos; + wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime " + "in EAPOL-Key", + ie->key_lifetime, pos[1] + 2); + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized " + "EAPOL-Key Key Data IE", + pos, 2 + pos[1]); + } + } else if (*pos == WLAN_EID_LINK_ID) { + if (pos[1] >= 18) { + ie->lnkid = pos; + ie->lnkid_len = pos[1] + 2; + } + } else if (*pos == WLAN_EID_EXT_CAPAB) { + ie->ext_capab = pos; + ie->ext_capab_len = pos[1] + 2; + } else if (*pos == WLAN_EID_SUPP_RATES) { + ie->supp_rates = pos; + ie->supp_rates_len = pos[1] + 2; + } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { + ie->ext_supp_rates = pos; + ie->ext_supp_rates_len = pos[1] + 2; + } else if (*pos == WLAN_EID_HT_CAP && + pos[1] >= sizeof(struct ieee80211_ht_capabilities)) { + ie->ht_capabilities = pos + 2; + } else if (*pos == WLAN_EID_VHT_AID) { + if (pos[1] >= 2) + ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff; + } else if (*pos == WLAN_EID_VHT_CAP && + pos[1] >= sizeof(struct ieee80211_vht_capabilities)) + { + ie->vht_capabilities = pos + 2; + } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) { + ie->qosinfo = pos[2]; + } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) { + ie->supp_channels = pos + 2; + ie->supp_channels_len = pos[1]; + } else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) { + /* + * The value of the Length field of the Supported + * Operating Classes element is between 2 and 253. + * Silently skip invalid elements to avoid interop + * issues when trying to use the value. + */ + if (pos[1] >= 2 && pos[1] <= 253) { + ie->supp_oper_classes = pos + 2; + ie->supp_oper_classes_len = pos[1]; + } + } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + + ret = wpa_parse_vendor_specific(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/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.h b/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.h new file mode 100644 index 00000000..fe95af0a --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.h @@ -0,0 +1,72 @@ +/* + * wpa_supplicant - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_IE_H +#define WPA_IE_H + +struct wpa_sm; + +struct wpa_eapol_ie_parse { + const u8 *wpa_ie; + size_t wpa_ie_len; + const u8 *rsn_ie; + size_t rsn_ie_len; + const u8 *pmkid; + const u8 *gtk; + size_t gtk_len; + const u8 *mac_addr; + size_t mac_addr_len; +#ifdef CONFIG_PEERKEY + const u8 *smk; + size_t smk_len; + const u8 *nonce; + size_t nonce_len; + const u8 *lifetime; + size_t lifetime_len; + const u8 *error; + size_t error_len; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W + const u8 *igtk; + size_t igtk_len; +#endif /* CONFIG_IEEE80211W */ + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *reassoc_deadline; + const u8 *key_lifetime; + const u8 *lnkid; + size_t lnkid_len; + const u8 *ext_capab; + size_t ext_capab_len; + const u8 *supp_rates; + size_t supp_rates_len; + const u8 *ext_supp_rates; + size_t ext_supp_rates_len; + const u8 *ht_capabilities; + const u8 *vht_capabilities; + const u8 *supp_channels; + size_t supp_channels_len; + const u8 *supp_oper_classes; + size_t supp_oper_classes_len; + u8 qosinfo; + u16 aid; + const u8 *wmm; + size_t wmm_len; +#ifdef CONFIG_P2P + const u8 *ip_addr_req; + const u8 *ip_addr_alloc; +#endif /* CONFIG_P2P */ +}; + +int wpa_supplicant_parse_ies(const u8 *buf, size_t len, + 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/freebsd/contrib/wpa/src/tls/tlsv1_client.h b/freebsd/contrib/wpa/src/tls/tlsv1_client.h new file mode 100644 index 00000000..a4e25e96 --- /dev/null +++ b/freebsd/contrib/wpa/src/tls/tlsv1_client.h @@ -0,0 +1,54 @@ +/* + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_CLIENT_H +#define TLSV1_CLIENT_H + +#include "tlsv1_cred.h" + +struct tlsv1_client; + +int tlsv1_client_global_init(void); +void tlsv1_client_global_deinit(void); +struct tlsv1_client * tlsv1_client_init(void); +void tlsv1_client_deinit(struct tlsv1_client *conn); +int tlsv1_client_established(struct tlsv1_client *conn); +int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + int server_random_first, u8 *out, size_t out_len); +u8 * tlsv1_client_handshake(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + size_t *out_len, u8 **appl_data, + size_t *appl_data_len, int *need_more_data); +int tlsv1_client_encrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + int *need_more_data); +int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, + size_t buflen); +int tlsv1_client_shutdown(struct tlsv1_client *conn); +int tlsv1_client_resumed(struct tlsv1_client *conn); +int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, + const u8 *data, size_t data_len); +int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *data); +int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); +int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); +int tlsv1_client_set_cred(struct tlsv1_client *conn, + struct tlsv1_credentials *cred); +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled); + +typedef int (*tlsv1_client_session_ticket_cb) +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, + const u8 *server_random, u8 *master_secret); + +void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, + tlsv1_client_session_ticket_cb cb, + void *ctx); + +#endif /* TLSV1_CLIENT_H */ diff --git a/freebsd/contrib/wpa/src/tls/tlsv1_cred.h b/freebsd/contrib/wpa/src/tls/tlsv1_cred.h new file mode 100644 index 00000000..68fbdc92 --- /dev/null +++ b/freebsd/contrib/wpa/src/tls/tlsv1_cred.h @@ -0,0 +1,40 @@ +/* + * TLSv1 credentials + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_CRED_H +#define TLSV1_CRED_H + +struct tlsv1_credentials { + struct x509_certificate *trusted_certs; + struct x509_certificate *cert; + struct crypto_private_key *key; + + /* Diffie-Hellman parameters */ + u8 *dh_p; /* prime */ + size_t dh_p_len; + u8 *dh_g; /* generator */ + size_t dh_g_len; +}; + + +struct tlsv1_credentials * tlsv1_cred_alloc(void); +void tlsv1_cred_free(struct tlsv1_credentials *cred); +int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len, + const char *path); +int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len); +int tlsv1_set_private_key(struct tlsv1_credentials *cred, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t private_key_blob_len); +int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, + const u8 *dh_blob, size_t dh_blob_len); + +#endif /* TLSV1_CRED_H */ diff --git a/freebsd/contrib/wpa/src/tls/tlsv1_server.h b/freebsd/contrib/wpa/src/tls/tlsv1_server.h new file mode 100644 index 00000000..10e76993 --- /dev/null +++ b/freebsd/contrib/wpa/src/tls/tlsv1_server.h @@ -0,0 +1,53 @@ +/* + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_SERVER_H +#define TLSV1_SERVER_H + +#include "tlsv1_cred.h" + +struct tlsv1_server; + +int tlsv1_server_global_init(void); +void tlsv1_server_global_deinit(void); +struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred); +void tlsv1_server_deinit(struct tlsv1_server *conn); +int tlsv1_server_established(struct tlsv1_server *conn); +int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + int server_random_first, u8 *out, size_t out_len); +u8 * tlsv1_server_handshake(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, size_t *out_len); +int tlsv1_server_encrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +int tlsv1_server_decrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, + size_t buflen); +int tlsv1_server_shutdown(struct tlsv1_server *conn); +int tlsv1_server_resumed(struct tlsv1_server *conn); +int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *data); +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); + +void tlsv1_server_set_log_cb(struct tlsv1_server *conn, + void (*cb)(void *ctx, const char *msg), void *ctx); + +void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags); + +#endif /* TLSV1_SERVER_H */ diff --git a/freebsd/contrib/wpa/src/utils/base64.c b/freebsd/contrib/wpa/src/utils/base64.c new file mode 100644 index 00000000..4127950f --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/base64.c @@ -0,0 +1,159 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "os.h" +#include "base64.h" + +static const unsigned char base64_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * base64_encode - Base64 encode + * @src: Data to be encoded + * @len: Length of the data to be encoded + * @out_len: Pointer to output length variable, or %NULL if not used + * Returns: Allocated buffer of out_len bytes of encoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. Returned buffer is + * nul terminated to make it easier to use as a C string. The nul terminator is + * not included in out_len. + */ +unsigned char * base64_encode(const unsigned char *src, size_t len, + size_t *out_len) +{ + unsigned char *out, *pos; + const unsigned char *end, *in; + size_t olen; + int line_len; + + olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ + olen += olen / 72; /* line feeds */ + olen++; /* nul termination */ + if (olen < len) + return NULL; /* integer overflow */ + out = os_malloc(olen); + if (out == NULL) + return NULL; + + end = src + len; + in = src; + pos = out; + line_len = 0; + while (end - in >= 3) { + *pos++ = base64_table[(in[0] >> 2) & 0x3f]; + *pos++ = base64_table[(((in[0] & 0x03) << 4) | + (in[1] >> 4)) & 0x3f]; + *pos++ = base64_table[(((in[1] & 0x0f) << 2) | + (in[2] >> 6)) & 0x3f]; + *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) & 0x3f]; + if (end - in == 1) { + *pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f]; + *pos++ = '='; + } else { + *pos++ = base64_table[(((in[0] & 0x03) << 4) | + (in[1] >> 4)) & 0x3f]; + *pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f]; + } + *pos++ = '='; + line_len += 4; + } + + if (line_len) + *pos++ = '\n'; + + *pos = '\0'; + if (out_len) + *out_len = pos - out; + return out; +} + + +/** + * base64_decode - Base64 decode + * @src: Data to be decoded + * @len: Length of the data to be decoded + * @out_len: Pointer to output length variable + * Returns: Allocated buffer of out_len bytes of decoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. + */ +unsigned char * base64_decode(const unsigned char *src, size_t len, + size_t *out_len) +{ + unsigned char dtable[256], *out, *pos, block[4], tmp; + size_t i, count, olen; + int pad = 0; + + os_memset(dtable, 0x80, 256); + for (i = 0; i < sizeof(base64_table) - 1; i++) + dtable[base64_table[i]] = (unsigned char) i; + dtable['='] = 0; + + count = 0; + for (i = 0; i < len; i++) { + if (dtable[src[i]] != 0x80) + count++; + } + + if (count == 0 || count % 4) + return NULL; + + olen = count / 4 * 3; + pos = out = os_malloc(olen); + if (out == NULL) + return NULL; + + count = 0; + for (i = 0; i < len; i++) { + tmp = dtable[src[i]]; + if (tmp == 0x80) + continue; + + if (src[i] == '=') + pad++; + block[count] = tmp; + count++; + if (count == 4) { + *pos++ = (block[0] << 2) | (block[1] >> 4); + *pos++ = (block[1] << 4) | (block[2] >> 2); + *pos++ = (block[2] << 6) | block[3]; + count = 0; + if (pad) { + if (pad == 1) + pos--; + else if (pad == 2) + pos -= 2; + else { + /* Invalid padding */ + os_free(out); + return NULL; + } + break; + } + } + } + + *out_len = pos - out; + return out; +} diff --git a/freebsd/contrib/wpa/src/utils/base64.h b/freebsd/contrib/wpa/src/utils/base64.h new file mode 100644 index 00000000..aa21fd0f --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/base64.h @@ -0,0 +1,17 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BASE64_H +#define BASE64_H + +unsigned char * base64_encode(const unsigned char *src, size_t len, + size_t *out_len); +unsigned char * base64_decode(const unsigned char *src, size_t len, + size_t *out_len); + +#endif /* BASE64_H */ diff --git a/freebsd/contrib/wpa/src/utils/build_config.h b/freebsd/contrib/wpa/src/utils/build_config.h new file mode 100644 index 00000000..c6f4e437 --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/build_config.h @@ -0,0 +1,50 @@ +/* + * wpa_supplicant/hostapd - Build time configuration defines + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This header file can be used to define configuration defines that were + * originally defined in Makefile. This is mainly meant for IDE use or for + * systems that do not have suitable 'make' tool. In these cases, it may be + * easier to have a single place for defining all the needed C pre-processor + * defines. + */ + +#ifndef BUILD_CONFIG_H +#define BUILD_CONFIG_H + +/* Insert configuration defines, e.g., #define EAP_MD5, here, if needed. */ + +#ifdef CONFIG_WIN32_DEFAULTS +#define CONFIG_NATIVE_WINDOWS +#define CONFIG_ANSI_C_EXTRA +#define CONFIG_WINPCAP +#define IEEE8021X_EAPOL +#define PKCS12_FUNCS +#define PCSC_FUNCS +#define CONFIG_CTRL_IFACE +#define CONFIG_CTRL_IFACE_NAMED_PIPE +#define CONFIG_DRIVER_NDIS +#define CONFIG_NDIS_EVENTS_INTEGRATED +#define CONFIG_DEBUG_FILE +#define EAP_MD5 +#define EAP_TLS +#define EAP_MSCHAPv2 +#define EAP_PEAP +#define EAP_TTLS +#define EAP_GTC +#define EAP_OTP +#define EAP_LEAP +#define EAP_TNC +#define _CRT_SECURE_NO_DEPRECATE + +#ifdef USE_INTERNAL_CRYPTO +#define CONFIG_TLS_INTERNAL_CLIENT +#define CONFIG_INTERNAL_LIBTOMMATH +#define CONFIG_CRYPTO_INTERNAL +#endif /* USE_INTERNAL_CRYPTO */ +#endif /* CONFIG_WIN32_DEFAULTS */ + +#endif /* BUILD_CONFIG_H */ diff --git a/freebsd/contrib/wpa/src/utils/common.c b/freebsd/contrib/wpa/src/utils/common.c new file mode 100644 index 00000000..9665bd3c --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/common.c @@ -0,0 +1,1127 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common/ieee802_11_defs.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; +} + + +static const char * hwaddr_parse(const char *txt, u8 *addr) +{ + size_t i; + + for (i = 0; i < ETH_ALEN; i++) { + int a; + + a = hex2byte(txt); + if (a < 0) + return NULL; + txt += 2; + addr[i] = a; + if (i < ETH_ALEN - 1 && *txt++ != ':') + return NULL; + } + return txt; +} + + +/** + * 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) +{ + return hwaddr_parse(txt, addr) ? 0 : -1; +} + + +/** + * hwaddr_masked_aton - Convert ASCII string with optional mask to MAC address (colon-delimited format) + * @txt: MAC address with optional mask as a string (e.g., "00:11:22:33:44:55/ff:ff:ff:ff:00:00") + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) + * @mask: Buffer for the MAC address mask (ETH_ALEN = 6 bytes) + * @maskable: Flag to indicate whether a mask is allowed + * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) + */ +int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable) +{ + const char *r; + + /* parse address part */ + r = hwaddr_parse(txt, addr); + if (!r) + return -1; + + /* check for optional mask */ + if (*r == '\0' || isspace(*r)) { + /* no mask specified, assume default */ + os_memset(mask, 0xff, ETH_ALEN); + } else if (maskable && *r == '/') { + /* mask specified and allowed */ + r = hwaddr_parse(r + 1, mask); + /* parser error? */ + if (!r) + return -1; + } else { + /* mask specified but not allowed or trailing garbage */ + 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; +} + + +int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask) +{ + size_t i; + int print_mask = 0; + int res; + + for (i = 0; i < ETH_ALEN; i++) { + if (mask[i] != 0xff) { + print_mask = 1; + break; + } + } + + if (print_mask) + res = os_snprintf(buf, len, MACSTR "/" MACSTR, + MAC2STR(addr), MAC2STR(mask)); + else + res = os_snprintf(buf, len, MACSTR, MAC2STR(addr)); + if (os_snprintf_error(len, res)) + return -1; + return res; +} + + +/** + * 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); +} + +/** + * wpa_scnprintf - Simpler-to-use snprintf function + * @buf: Output buffer + * @size: Buffer size + * @fmt: format + * + * Simpler snprintf version that doesn't require further error checks - the + * return value only indicates how many bytes were actually written, excluding + * the NULL byte (i.e., 0 on error, size-1 if buffer is not big enough). + */ +int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...) +{ + va_list ap; + int ret; + + if (!size) + return 0; + + va_start(ap, fmt); + ret = vsnprintf(buf, size, fmt, ap); + va_end(ap); + + if (ret < 0) + return 0; + if ((size_t) ret >= size) + return size - 1; + + return ret; +} + + +int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len, + char sep) +{ + 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, "%02x%c", + data[i], sep); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return pos - buf; + } + pos += ret; + } + pos[-1] = '\0'; + return pos - buf; +} + + +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 (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return pos - buf; + } + pos += ret; + } + end[-1] = '\0'; + return pos - buf; +} + +/** + * wpa_snprintf_hex - Print data as a hex string into a buffer + * @buf: Memory area to use as the output buffer + * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1) + * @data: Data to be printed + * @len: Length of data in bytes + * Returns: Number of bytes written + */ +int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len) +{ + return _wpa_snprintf_hex(buf, buf_size, data, len, 0); +} + + +/** + * wpa_snprintf_hex_uppercase - Print data as a upper case hex string into buf + * @buf: Memory area to use as the output buffer + * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1) + * @data: Data to be printed + * @len: Length of data in bytes + * Returns: Number of bytes written + */ +int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, + size_t len) +{ + return _wpa_snprintf_hex(buf, buf_size, data, len, 1); +} + + +#ifdef CONFIG_ANSI_C_EXTRA + +#ifdef _WIN32_WCE +void perror(const char *s) +{ + wpa_printf(MSG_ERROR, "%s: GetLastError: %d", + s, (int) GetLastError()); +} +#endif /* _WIN32_WCE */ + + +int optind = 1; +int optopt; +char *optarg; + +int getopt(int argc, char *const argv[], const char *optstring) +{ + static int optchr = 1; + char *cp; + + if (optchr == 1) { + if (optind >= argc) { + /* all arguments processed */ + return EOF; + } + + if (argv[optind][0] != '-' || argv[optind][1] == '\0') { + /* no option characters */ + return EOF; + } + } + + if (os_strcmp(argv[optind], "--") == 0) { + /* no more options */ + optind++; + return EOF; + } + + optopt = argv[optind][optchr]; + cp = os_strchr(optstring, optopt); + if (cp == NULL || optopt == ':') { + if (argv[optind][++optchr] == '\0') { + optchr = 1; + optind++; + } + return '?'; + } + + if (cp[1] == ':') { + /* Argument required */ + optchr = 1; + if (argv[optind][optchr + 1]) { + /* No space between option and argument */ + optarg = &argv[optind++][optchr + 1]; + } else if (++optind >= argc) { + /* option requires an argument */ + return '?'; + } else { + /* Argument in the next argv */ + optarg = argv[optind++]; + } + } else { + /* No argument */ + if (argv[optind][++optchr] == '\0') { + optchr = 1; + optind++; + } + optarg = NULL; + } + return *cp; +} +#endif /* CONFIG_ANSI_C_EXTRA */ + + +#ifdef CONFIG_NATIVE_WINDOWS +/** + * wpa_unicode2ascii_inplace - Convert unicode string into ASCII + * @str: Pointer to string to convert + * + * This function converts a unicode string to ASCII using the same + * buffer for output. If UNICODE is not set, the buffer is not + * modified. + */ +void wpa_unicode2ascii_inplace(TCHAR *str) +{ +#ifdef UNICODE + char *dst = (char *) str; + while (*str) + *dst++ = (char) *str++; + *dst = '\0'; +#endif /* UNICODE */ +} + + +TCHAR * wpa_strdup_tchar(const char *str) +{ +#ifdef UNICODE + TCHAR *buf; + buf = os_malloc((strlen(str) + 1) * sizeof(TCHAR)); + if (buf == NULL) + return NULL; + wsprintf(buf, L"%S", str); + return buf; +#else /* UNICODE */ + return os_strdup(str); +#endif /* UNICODE */ +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) +{ + char *end = txt + maxlen; + size_t i; + + for (i = 0; i < len; i++) { + if (txt + 4 >= end) + break; + + switch (data[i]) { + case '\"': + *txt++ = '\\'; + *txt++ = '\"'; + break; + case '\\': + *txt++ = '\\'; + *txt++ = '\\'; + break; + case '\033': + *txt++ = '\\'; + *txt++ = 'e'; + break; + case '\n': + *txt++ = '\\'; + *txt++ = 'n'; + break; + case '\r': + *txt++ = '\\'; + *txt++ = 'r'; + break; + case '\t': + *txt++ = '\\'; + *txt++ = 't'; + break; + default: + if (data[i] >= 32 && data[i] <= 127) { + *txt++ = data[i]; + } else { + txt += os_snprintf(txt, end - txt, "\\x%02x", + data[i]); + } + break; + } + } + + *txt = '\0'; +} + + +size_t printf_decode(u8 *buf, size_t maxlen, const char *str) +{ + const char *pos = str; + size_t len = 0; + int val; + + while (*pos) { + if (len + 1 >= maxlen) + break; + switch (*pos) { + case '\\': + pos++; + switch (*pos) { + case '\\': + buf[len++] = '\\'; + pos++; + break; + case '"': + buf[len++] = '"'; + pos++; + break; + case 'n': + buf[len++] = '\n'; + pos++; + break; + case 'r': + buf[len++] = '\r'; + pos++; + break; + case 't': + buf[len++] = '\t'; + pos++; + break; + case 'e': + buf[len++] = '\033'; + pos++; + break; + case 'x': + pos++; + val = hex2byte(pos); + if (val < 0) { + val = hex2num(*pos); + if (val < 0) + break; + buf[len++] = val; + pos++; + } else { + buf[len++] = val; + pos += 2; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + val = *pos++ - '0'; + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + buf[len++] = val; + break; + default: + break; + } + break; + default: + buf[len++] = *pos++; + break; + } + } + if (maxlen > len) + buf[len] = '\0'; + + return len; +} + + +/** + * wpa_ssid_txt - Convert SSID to a printable string + * @ssid: SSID (32-octet string) + * @ssid_len: Length of ssid in octets + * Returns: Pointer to a printable string + * + * This function can be used to convert SSIDs into printable form. In most + * cases, SSIDs do not use unprintable characters, but IEEE 802.11 standard + * does not limit the used character set, so anything could be used in an SSID. + * + * This function uses a static buffer, so only one call can be used at the + * time, i.e., this is not re-entrant and the returned buffer must be used + * before calling this again. + */ +const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len) +{ + static char ssid_txt[SSID_MAX_LEN * 4 + 1]; + + if (ssid == NULL) { + ssid_txt[0] = '\0'; + return ssid_txt; + } + + printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len); + return ssid_txt; +} + + +void * __hide_aliasing_typecast(void *foo) +{ + return foo; +} + + +char * wpa_config_parse_string(const char *value, size_t *len) +{ + if (*value == '"') { + const char *pos; + char *str; + value++; + pos = os_strrchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + *len = pos - value; + str = dup_binstr(value, *len); + if (str == NULL) + return NULL; + return str; + } else if (*value == 'P' && value[1] == '"') { + const char *pos; + char *tstr, *str; + size_t tlen; + value += 2; + pos = os_strrchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + tlen = pos - value; + tstr = dup_binstr(value, tlen); + if (tstr == NULL) + return NULL; + + str = os_malloc(tlen + 1); + if (str == NULL) { + os_free(tstr); + return NULL; + } + + *len = printf_decode((u8 *) str, tlen + 1, tstr); + os_free(tstr); + + return str; + } else { + u8 *str; + size_t tlen, hlen = os_strlen(value); + if (hlen & 1) + return NULL; + tlen = hlen / 2; + str = os_malloc(tlen + 1); + if (str == NULL) + return NULL; + if (hexstr2bin(value, str, tlen)) { + os_free(str); + return NULL; + } + str[tlen] = '\0'; + *len = tlen; + return (char *) str; + } +} + + +int is_hex(const u8 *data, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (data[i] < 32 || data[i] >= 127) + return 1; + } + return 0; +} + + +size_t merge_byte_arrays(u8 *res, size_t res_len, + const u8 *src1, size_t src1_len, + const u8 *src2, size_t src2_len) +{ + size_t len = 0; + + os_memset(res, 0, res_len); + + if (src1) { + if (src1_len >= res_len) { + os_memcpy(res, src1, res_len); + return res_len; + } + + os_memcpy(res, src1, src1_len); + len += src1_len; + } + + if (src2) { + if (len + src2_len >= res_len) { + os_memcpy(res + len, src2, res_len - len); + return res_len; + } + + os_memcpy(res + len, src2, src2_len); + len += src2_len; + } + + return len; +} + + +char * dup_binstr(const void *src, size_t len) +{ + char *res; + + if (src == NULL) + return NULL; + res = os_malloc(len + 1); + if (res == NULL) + return NULL; + os_memcpy(res, src, len); + res[len] = '\0'; + + return res; +} + + +int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value) +{ + struct wpa_freq_range *freq = NULL, *n; + unsigned int count = 0; + const char *pos, *pos2, *pos3; + + /* + * Comma separated list of frequency ranges. + * For example: 2412-2432,2462,5000-6000 + */ + pos = value; + while (pos && pos[0]) { + n = os_realloc_array(freq, count + 1, + sizeof(struct wpa_freq_range)); + if (n == NULL) { + os_free(freq); + return -1; + } + freq = n; + freq[count].min = atoi(pos); + pos2 = os_strchr(pos, '-'); + pos3 = os_strchr(pos, ','); + if (pos2 && (!pos3 || pos2 < pos3)) { + pos2++; + freq[count].max = atoi(pos2); + } else + freq[count].max = freq[count].min; + pos = pos3; + if (pos) + pos++; + count++; + } + + os_free(res->range); + res->range = freq; + res->num = count; + + return 0; +} + + +int freq_range_list_includes(const struct wpa_freq_range_list *list, + unsigned int freq) +{ + unsigned int i; + + if (list == NULL) + return 0; + + for (i = 0; i < list->num; i++) { + if (freq >= list->range[i].min && freq <= list->range[i].max) + return 1; + } + + return 0; +} + + +char * freq_range_list_str(const struct wpa_freq_range_list *list) +{ + char *buf, *pos, *end; + size_t maxlen; + unsigned int i; + int res; + + if (list->num == 0) + return NULL; + + maxlen = list->num * 30; + buf = os_malloc(maxlen); + if (buf == NULL) + return NULL; + pos = buf; + end = buf + maxlen; + + for (i = 0; i < list->num; i++) { + struct wpa_freq_range *range = &list->range[i]; + + if (range->min == range->max) + res = os_snprintf(pos, end - pos, "%s%u", + i == 0 ? "" : ",", range->min); + else + res = os_snprintf(pos, end - pos, "%s%u-%u", + i == 0 ? "" : ",", + range->min, range->max); + if (os_snprintf_error(end - pos, res)) { + os_free(buf); + return NULL; + } + pos += res; + } + + return buf; +} + + +int int_array_len(const int *a) +{ + int i; + for (i = 0; a && a[i]; i++) + ; + return i; +} + + +void int_array_concat(int **res, const int *a) +{ + int reslen, alen, i; + int *n; + + reslen = int_array_len(*res); + alen = int_array_len(a); + + n = os_realloc_array(*res, reslen + alen + 1, sizeof(int)); + if (n == NULL) { + os_free(*res); + *res = NULL; + return; + } + for (i = 0; i <= alen; i++) + n[reslen + i] = a[i]; + *res = n; +} + + +static int freq_cmp(const void *a, const void *b) +{ + int _a = *(int *) a; + int _b = *(int *) b; + + if (_a == 0) + return 1; + if (_b == 0) + return -1; + return _a - _b; +} + + +void int_array_sort_unique(int *a) +{ + int alen; + int i, j; + + if (a == NULL) + return; + + alen = int_array_len(a); + qsort(a, alen, sizeof(int), freq_cmp); + + i = 0; + j = 1; + while (a[i] && a[j]) { + if (a[i] == a[j]) { + j++; + continue; + } + a[++i] = a[j++]; + } + if (a[i]) + i++; + a[i] = 0; +} + + +void int_array_add_unique(int **res, int a) +{ + int reslen; + int *n; + + for (reslen = 0; *res && (*res)[reslen]; reslen++) { + if ((*res)[reslen] == a) + return; /* already in the list */ + } + + n = os_realloc_array(*res, reslen + 2, sizeof(int)); + if (n == NULL) { + os_free(*res); + *res = NULL; + return; + } + + n[reslen] = a; + n[reslen + 1] = 0; + + *res = n; +} + + +void str_clear_free(char *str) +{ + if (str) { + size_t len = os_strlen(str); + os_memset(str, 0, len); + os_free(str); + } +} + + +void bin_clear_free(void *bin, size_t len) +{ + if (bin) { + os_memset(bin, 0, len); + os_free(bin); + } +} + + +int random_mac_addr(u8 *addr) +{ + if (os_get_random(addr, ETH_ALEN) < 0) + return -1; + addr[0] &= 0xfe; /* unicast */ + addr[0] |= 0x02; /* locally administered */ + return 0; +} + + +int random_mac_addr_keep_oui(u8 *addr) +{ + if (os_get_random(addr + 3, 3) < 0) + return -1; + addr[0] &= 0xfe; /* unicast */ + addr[0] |= 0x02; /* locally administered */ + return 0; +} + + +/** + * cstr_token - Get next token from const char string + * @str: a constant string to tokenize + * @delim: a string of delimiters + * @last: a pointer to a character following the returned token + * It has to be set to NULL for the first call and passed for any + * futher call. + * Returns: a pointer to token position in str or NULL + * + * This function is similar to str_token, but it can be used with both + * char and const char strings. Differences: + * - The str buffer remains unmodified + * - The returned token is not a NULL terminated string, but a token + * position in str buffer. If a return value is not NULL a size + * of the returned token could be calculated as (last - token). + */ +const char * cstr_token(const char *str, const char *delim, const char **last) +{ + const char *end, *token = str; + + if (!str || !delim || !last) + return NULL; + + if (*last) + token = *last; + + while (*token && os_strchr(delim, *token)) + token++; + + if (!*token) + return NULL; + + end = token + 1; + + while (*end && !os_strchr(delim, *end)) + end++; + + *last = end; + return token; +} + + +/** + * str_token - Get next token from a string + * @buf: String to tokenize. Note that the string might be modified. + * @delim: String of delimiters + * @context: Pointer to save our context. Should be initialized with + * NULL on the first call, and passed for any further call. + * Returns: The next token, NULL if there are no more valid tokens. + */ +char * str_token(char *str, const char *delim, char **context) +{ + char *token = (char *) cstr_token(str, delim, (const char **) context); + + if (token && **context) + *(*context)++ = '\0'; + + return token; +} + + +size_t utf8_unescape(const char *inp, size_t in_size, + char *outp, size_t out_size) +{ + size_t res_size = 0; + + if (!inp || !outp) + return 0; + + if (!in_size) + in_size = os_strlen(inp); + + /* Advance past leading single quote */ + if (*inp == '\'' && in_size) { + inp++; + in_size--; + } + + while (in_size--) { + if (res_size >= out_size) + return 0; + + switch (*inp) { + case '\'': + /* Terminate on bare single quote */ + *outp = '\0'; + return res_size; + + case '\\': + if (!in_size--) + return 0; + inp++; + /* fall through */ + + default: + *outp++ = *inp++; + res_size++; + } + } + + /* NUL terminate if space allows */ + if (res_size < out_size) + *outp = '\0'; + + return res_size; +} + + +size_t utf8_escape(const char *inp, size_t in_size, + char *outp, size_t out_size) +{ + size_t res_size = 0; + + if (!inp || !outp) + return 0; + + /* inp may or may not be NUL terminated, but must be if 0 size + * is specified */ + if (!in_size) + in_size = os_strlen(inp); + + while (in_size--) { + if (res_size++ >= out_size) + return 0; + + switch (*inp) { + case '\\': + case '\'': + if (res_size++ >= out_size) + return 0; + *outp++ = '\\'; + /* fall through */ + + default: + *outp++ = *inp++; + break; + } + } + + /* NUL terminate if space allows */ + if (res_size < out_size) + *outp = '\0'; + + return res_size; +} + + +int is_ctrl_char(char c) +{ + return c > 0 && c < 32; +} diff --git a/freebsd/contrib/wpa/src/utils/common.h b/freebsd/contrib/wpa/src/utils/common.h new file mode 100644 index 00000000..0b9cc3d8 --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/common.h @@ -0,0 +1,559 @@ +/* + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef COMMON_H +#define COMMON_H + +#include "os.h" + +#if defined(__linux__) || defined(__GLIBC__) +#include <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_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 */ + +#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 host_to_le32(n) (n) +#define be_to_host32(n) wpa_swap_32(n) +#define host_to_be32(n) wpa_swap_32(n) + +#define WPA_BYTE_SWAP_DEFINED + +#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */ + + +#ifndef WPA_BYTE_SWAP_DEFINED + +#ifndef __BYTE_ORDER +#ifndef __LITTLE_ENDIAN +#ifndef __BIG_ENDIAN +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#if defined(sparc) +#define __BYTE_ORDER __BIG_ENDIAN +#endif +#endif /* __BIG_ENDIAN */ +#endif /* __LITTLE_ENDIAN */ +#endif /* __BYTE_ORDER */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define le_to_host16(n) ((__force u16) (le16) (n)) +#define host_to_le16(n) ((__force le16) (u16) (n)) +#define be_to_host16(n) bswap_16((__force u16) (be16) (n)) +#define host_to_be16(n) ((__force be16) bswap_16((n))) +#define le_to_host32(n) ((__force u32) (le32) (n)) +#define host_to_le32(n) ((__force le32) (u32) (n)) +#define be_to_host32(n) bswap_32((__force u32) (be32) (n)) +#define host_to_be32(n) ((__force be32) bswap_32((n))) +#define le_to_host64(n) ((__force u64) (le64) (n)) +#define host_to_le64(n) ((__force le64) (u64) (n)) +#define be_to_host64(n) bswap_64((__force u64) (be64) (n)) +#define host_to_be64(n) ((__force be64) bswap_64((n))) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define le_to_host16(n) bswap_16(n) +#define host_to_le16(n) bswap_16(n) +#define be_to_host16(n) (n) +#define host_to_be16(n) (n) +#define le_to_host32(n) bswap_32(n) +#define host_to_le32(n) bswap_32(n) +#define be_to_host32(n) (n) +#define host_to_be32(n) (n) +#define le_to_host64(n) bswap_64(n) +#define host_to_le64(n) bswap_64(n) +#define be_to_host64(n) (n) +#define host_to_be64(n) (n) +#ifndef WORDS_BIGENDIAN +#define WORDS_BIGENDIAN +#endif +#else +#error Could not determine CPU byte order +#endif + +#define WPA_BYTE_SWAP_DEFINED +#endif /* !WPA_BYTE_SWAP_DEFINED */ + + +/* Macros for handling unaligned memory accesses */ + +static inline u16 WPA_GET_BE16(const u8 *a) +{ + return (a[0] << 8) | a[1]; +} + +static inline void WPA_PUT_BE16(u8 *a, u16 val) +{ + a[0] = val >> 8; + a[1] = val & 0xff; +} + +static inline u16 WPA_GET_LE16(const u8 *a) +{ + return (a[1] << 8) | a[0]; +} + +static inline void WPA_PUT_LE16(u8 *a, u16 val) +{ + a[1] = val >> 8; + a[0] = val & 0xff; +} + +static inline u32 WPA_GET_BE24(const u8 *a) +{ + return (a[0] << 16) | (a[1] << 8) | a[2]; +} + +static inline void WPA_PUT_BE24(u8 *a, u32 val) +{ + a[0] = (val >> 16) & 0xff; + a[1] = (val >> 8) & 0xff; + a[2] = val & 0xff; +} + +static inline u32 WPA_GET_BE32(const u8 *a) +{ + return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]; +} + +static inline void WPA_PUT_BE32(u8 *a, u32 val) +{ + a[0] = (val >> 24) & 0xff; + a[1] = (val >> 16) & 0xff; + a[2] = (val >> 8) & 0xff; + a[3] = val & 0xff; +} + +static inline u32 WPA_GET_LE32(const u8 *a) +{ + return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; +} + +static inline void WPA_PUT_LE32(u8 *a, u32 val) +{ + a[3] = (val >> 24) & 0xff; + a[2] = (val >> 16) & 0xff; + a[1] = (val >> 8) & 0xff; + a[0] = val & 0xff; +} + +static inline u64 WPA_GET_BE64(const u8 *a) +{ + return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) | + (((u64) a[2]) << 40) | (((u64) a[3]) << 32) | + (((u64) a[4]) << 24) | (((u64) a[5]) << 16) | + (((u64) a[6]) << 8) | ((u64) a[7]); +} + +static inline void WPA_PUT_BE64(u8 *a, u64 val) +{ + a[0] = val >> 56; + a[1] = val >> 48; + a[2] = val >> 40; + a[3] = val >> 32; + a[4] = val >> 24; + a[5] = val >> 16; + a[6] = val >> 8; + a[7] = val & 0xff; +} + +static inline u64 WPA_GET_LE64(const u8 *a) +{ + return (((u64) a[7]) << 56) | (((u64) a[6]) << 48) | + (((u64) a[5]) << 40) | (((u64) a[4]) << 32) | + (((u64) a[3]) << 24) | (((u64) a[2]) << 16) | + (((u64) a[1]) << 8) | ((u64) a[0]); +} + +static inline void WPA_PUT_LE64(u8 *a, u64 val) +{ + a[7] = val >> 56; + a[6] = val >> 48; + a[5] = val >> 40; + a[4] = val >> 32; + a[3] = val >> 24; + a[2] = val >> 16; + a[1] = val >> 8; + a[0] = val & 0xff; +} + + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif +#ifndef ETH_HLEN +#define ETH_HLEN 14 +#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) (1U << (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 */ + +#ifndef __maybe_unused +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define __maybe_unused __attribute__((unused)) +#else +#define __maybe_unused +#endif /* __GNUC__ */ +#endif /* __must_check */ + +int hwaddr_aton(const char *txt, u8 *addr); +int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable); +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_scnprintf(char *buf, size_t size, const char *fmt, ...); +int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len, + char sep); +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); + +int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask); + +#ifdef CONFIG_NATIVE_WINDOWS +void wpa_unicode2ascii_inplace(TCHAR *str); +TCHAR * wpa_strdup_tchar(const char *str); +#else /* CONFIG_NATIVE_WINDOWS */ +#define wpa_unicode2ascii_inplace(s) do { } while (0) +#define wpa_strdup_tchar(s) strdup((s)) +#endif /* CONFIG_NATIVE_WINDOWS */ + +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len); +size_t printf_decode(u8 *buf, size_t maxlen, const char *str); + +const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); + +char * wpa_config_parse_string(const char *value, size_t *len); +int is_hex(const u8 *data, size_t len); +size_t merge_byte_arrays(u8 *res, size_t res_len, + const u8 *src1, size_t src1_len, + const u8 *src2, size_t src2_len); +char * dup_binstr(const void *src, size_t len); + +static inline int is_zero_ether_addr(const u8 *a) +{ + return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]); +} + +static inline int is_broadcast_ether_addr(const u8 *a) +{ + return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff; +} + +static inline int is_multicast_ether_addr(const u8 *a) +{ + return a[0] & 0x01; +} + +#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff" + +#include "wpa_debug.h" + + +struct wpa_freq_range_list { + struct wpa_freq_range { + unsigned int min; + unsigned int max; + } *range; + unsigned int num; +}; + +int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value); +int freq_range_list_includes(const struct wpa_freq_range_list *list, + unsigned int freq); +char * freq_range_list_str(const struct wpa_freq_range_list *list); + +int int_array_len(const int *a); +void int_array_concat(int **res, const int *a); +void int_array_sort_unique(int *a); +void int_array_add_unique(int **res, int a); + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +void str_clear_free(char *str); +void bin_clear_free(void *bin, size_t len); + +int random_mac_addr(u8 *addr); +int random_mac_addr_keep_oui(u8 *addr); + +const char * cstr_token(const char *str, const char *delim, const char **last); +char * str_token(char *str, const char *delim, char **context); +size_t utf8_escape(const char *inp, size_t in_size, + char *outp, size_t out_size); +size_t utf8_unescape(const char *inp, size_t in_size, + char *outp, size_t out_size); +int is_ctrl_char(char c); + + +/* + * 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/freebsd/contrib/wpa/src/utils/eloop.c b/freebsd/contrib/wpa/src/utils/eloop.c new file mode 100644 index 00000000..5b1cc862 --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/eloop.c @@ -0,0 +1,1144 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Event loop based on select() loop + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <assert.h> + +#include "common.h" +#include "trace.h" +#include "list.h" +#include "eloop.h" + +#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL) +#error Do not define both of poll and epoll +#endif + +#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL) +#define CONFIG_ELOOP_SELECT +#endif + +#ifdef CONFIG_ELOOP_POLL +#include <poll.h> +#endif /* CONFIG_ELOOP_POLL */ + +#ifdef CONFIG_ELOOP_EPOLL +#include <sys/epoll.h> +#endif /* CONFIG_ELOOP_EPOLL */ + +struct eloop_sock { + int sock; + void *eloop_data; + void *user_data; + eloop_sock_handler handler; + WPA_TRACE_REF(eloop); + WPA_TRACE_REF(user); + WPA_TRACE_INFO +}; + +struct eloop_timeout { + struct dl_list list; + struct os_reltime time; + void *eloop_data; + void *user_data; + eloop_timeout_handler handler; + WPA_TRACE_REF(eloop); + WPA_TRACE_REF(user); + WPA_TRACE_INFO +}; + +struct eloop_signal { + int sig; + void *user_data; + eloop_signal_handler handler; + int signaled; +}; + +struct eloop_sock_table { + int count; + struct eloop_sock *table; + eloop_event_type type; + int changed; +}; + +struct eloop_data { + int max_sock; + + int count; /* sum of all table counts */ +#ifdef CONFIG_ELOOP_POLL + int max_pollfd_map; /* number of pollfds_map currently allocated */ + int max_poll_fds; /* number of pollfds currently allocated */ + struct pollfd *pollfds; + struct pollfd **pollfds_map; +#endif /* CONFIG_ELOOP_POLL */ +#ifdef CONFIG_ELOOP_EPOLL + int epollfd; + int epoll_max_event_num; + int epoll_max_fd; + struct eloop_sock *epoll_table; + struct epoll_event *epoll_events; +#endif /* CONFIG_ELOOP_EPOLL */ + 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; +}; + +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 CONFIG_ELOOP_EPOLL + eloop.epollfd = epoll_create1(0); + if (eloop.epollfd < 0) { + wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s\n", + __func__, strerror(errno)); + return -1; + } + eloop.readers.type = EVENT_TYPE_READ; + eloop.writers.type = EVENT_TYPE_WRITE; + eloop.exceptions.type = EVENT_TYPE_EXCEPTION; +#endif /* CONFIG_ELOOP_EPOLL */ +#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) +{ +#ifdef CONFIG_ELOOP_EPOLL + struct eloop_sock *temp_table; + struct epoll_event ev, *temp_events; + int next; +#endif /* CONFIG_ELOOP_EPOLL */ + struct eloop_sock *tmp; + int new_max_sock; + + if (sock > eloop.max_sock) + new_max_sock = sock; + else + new_max_sock = eloop.max_sock; + + if (table == NULL) + return -1; + +#ifdef CONFIG_ELOOP_POLL + if (new_max_sock >= eloop.max_pollfd_map) { + struct pollfd **nmap; + nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50, + sizeof(struct pollfd *)); + if (nmap == NULL) + return -1; + + eloop.max_pollfd_map = new_max_sock + 50; + eloop.pollfds_map = nmap; + } + + if (eloop.count + 1 > eloop.max_poll_fds) { + struct pollfd *n; + int nmax = eloop.count + 1 + 50; + n = os_realloc_array(eloop.pollfds, nmax, + sizeof(struct pollfd)); + if (n == NULL) + return -1; + + eloop.max_poll_fds = nmax; + eloop.pollfds = n; + } +#endif /* CONFIG_ELOOP_POLL */ +#ifdef CONFIG_ELOOP_EPOLL + if (new_max_sock >= eloop.epoll_max_fd) { + next = eloop.epoll_max_fd == 0 ? 16 : eloop.epoll_max_fd * 2; + temp_table = os_realloc_array(eloop.epoll_table, next, + sizeof(struct eloop_sock)); + if (temp_table == NULL) + return -1; + + eloop.epoll_max_fd = next; + eloop.epoll_table = temp_table; + } + + if (eloop.count + 1 > eloop.epoll_max_event_num) { + next = eloop.epoll_max_event_num == 0 ? 8 : + eloop.epoll_max_event_num * 2; + temp_events = os_realloc_array(eloop.epoll_events, next, + sizeof(struct epoll_event)); + if (temp_events == NULL) { + wpa_printf(MSG_ERROR, "%s: malloc for epoll failed. " + "%s\n", __func__, strerror(errno)); + return -1; + } + + eloop.epoll_max_event_num = next; + eloop.epoll_events = temp_events; + } +#endif /* CONFIG_ELOOP_EPOLL */ + + eloop_trace_sock_remove_ref(table); + tmp = os_realloc_array(table->table, table->count + 1, + sizeof(struct eloop_sock)); + if (tmp == NULL) { + eloop_trace_sock_add_ref(table); + return -1; + } + + tmp[table->count].sock = sock; + tmp[table->count].eloop_data = eloop_data; + tmp[table->count].user_data = user_data; + tmp[table->count].handler = handler; + wpa_trace_record(&tmp[table->count]); + table->count++; + table->table = tmp; + eloop.max_sock = new_max_sock; + eloop.count++; + table->changed = 1; + eloop_trace_sock_add_ref(table); + +#ifdef CONFIG_ELOOP_EPOLL + os_memset(&ev, 0, sizeof(ev)); + switch (table->type) { + case EVENT_TYPE_READ: + ev.events = EPOLLIN; + break; + case EVENT_TYPE_WRITE: + ev.events = EPOLLOUT; + break; + /* + * Exceptions are always checked when using epoll, but I suppose it's + * possible that someone registered a socket *only* for exception + * handling. + */ + case EVENT_TYPE_EXCEPTION: + ev.events = EPOLLERR | EPOLLHUP; + break; + } + ev.data.fd = sock; + if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) { + wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d " + "failed. %s\n", __func__, sock, strerror(errno)); + return -1; + } + os_memcpy(&eloop.epoll_table[sock], &table->table[table->count - 1], + sizeof(struct eloop_sock)); +#endif /* CONFIG_ELOOP_EPOLL */ + return 0; +} + + +static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, + int sock) +{ + int i; + + if (table == NULL || table->table == NULL || table->count == 0) + return; + + for (i = 0; i < table->count; i++) { + if (table->table[i].sock == sock) + break; + } + if (i == table->count) + return; + eloop_trace_sock_remove_ref(table); + if (i != table->count - 1) { + os_memmove(&table->table[i], &table->table[i + 1], + (table->count - i - 1) * + sizeof(struct eloop_sock)); + } + table->count--; + eloop.count--; + table->changed = 1; + eloop_trace_sock_add_ref(table); +#ifdef CONFIG_ELOOP_EPOLL + if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) { + wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d " + "failed. %s\n", __func__, sock, strerror(errno)); + return; + } + os_memset(&eloop.epoll_table[sock], 0, sizeof(struct eloop_sock)); +#endif /* CONFIG_ELOOP_EPOLL */ +} + + +#ifdef CONFIG_ELOOP_POLL + +static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx) +{ + if (fd < mx && fd >= 0) + return pollfds_map[fd]; + return NULL; +} + + +static int eloop_sock_table_set_fds(struct eloop_sock_table *readers, + struct eloop_sock_table *writers, + struct eloop_sock_table *exceptions, + struct pollfd *pollfds, + struct pollfd **pollfds_map, + int max_pollfd_map) +{ + int i; + int nxt = 0; + int fd; + struct pollfd *pfd; + + /* Clear pollfd lookup map. It will be re-populated below. */ + os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map); + + if (readers && readers->table) { + for (i = 0; i < readers->count; i++) { + fd = readers->table[i].sock; + assert(fd >= 0 && fd < max_pollfd_map); + pollfds[nxt].fd = fd; + pollfds[nxt].events = POLLIN; + pollfds[nxt].revents = 0; + pollfds_map[fd] = &(pollfds[nxt]); + nxt++; + } + } + + if (writers && writers->table) { + for (i = 0; i < writers->count; i++) { + /* + * See if we already added this descriptor, update it + * if so. + */ + fd = writers->table[i].sock; + assert(fd >= 0 && fd < max_pollfd_map); + pfd = pollfds_map[fd]; + if (!pfd) { + pfd = &(pollfds[nxt]); + pfd->events = 0; + pfd->fd = fd; + pollfds[i].revents = 0; + pollfds_map[fd] = pfd; + nxt++; + } + pfd->events |= POLLOUT; + } + } + + /* + * Exceptions are always checked when using poll, but I suppose it's + * possible that someone registered a socket *only* for exception + * handling. Set the POLLIN bit in this case. + */ + if (exceptions && exceptions->table) { + for (i = 0; i < exceptions->count; i++) { + /* + * See if we already added this descriptor, just use it + * if so. + */ + fd = exceptions->table[i].sock; + assert(fd >= 0 && fd < max_pollfd_map); + pfd = pollfds_map[fd]; + if (!pfd) { + pfd = &(pollfds[nxt]); + pfd->events = POLLIN; + pfd->fd = fd; + pollfds[i].revents = 0; + pollfds_map[fd] = pfd; + nxt++; + } + } + } + + return nxt; +} + + +static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table, + struct pollfd **pollfds_map, + int max_pollfd_map, + short int revents) +{ + int i; + struct pollfd *pfd; + + if (!table || !table->table) + return 0; + + table->changed = 0; + for (i = 0; i < table->count; i++) { + pfd = find_pollfd(pollfds_map, table->table[i].sock, + max_pollfd_map); + if (!pfd) + continue; + + if (!(pfd->revents & revents)) + continue; + + table->table[i].handler(table->table[i].sock, + table->table[i].eloop_data, + table->table[i].user_data); + if (table->changed) + return 1; + } + + return 0; +} + + +static void eloop_sock_table_dispatch(struct eloop_sock_table *readers, + struct eloop_sock_table *writers, + struct eloop_sock_table *exceptions, + struct pollfd **pollfds_map, + int max_pollfd_map) +{ + if (eloop_sock_table_dispatch_table(readers, pollfds_map, + max_pollfd_map, POLLIN | POLLERR | + POLLHUP)) + return; /* pollfds may be invalid at this point */ + + if (eloop_sock_table_dispatch_table(writers, pollfds_map, + max_pollfd_map, POLLOUT)) + return; /* pollfds may be invalid at this point */ + + eloop_sock_table_dispatch_table(exceptions, pollfds_map, + max_pollfd_map, POLLERR | POLLHUP); +} + +#endif /* CONFIG_ELOOP_POLL */ + +#ifdef CONFIG_ELOOP_SELECT + +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++) { + assert(table->table[i].sock >= 0); + FD_SET(table->table[i].sock, fds); + } +} + + +static void eloop_sock_table_dispatch(struct eloop_sock_table *table, + fd_set *fds) +{ + int i; + + if (table == NULL || table->table == NULL) + return; + + table->changed = 0; + for (i = 0; i < table->count; i++) { + if (FD_ISSET(table->table[i].sock, fds)) { + table->table[i].handler(table->table[i].sock, + table->table[i].eloop_data, + table->table[i].user_data); + if (table->changed) + break; + } + } +} + +#endif /* CONFIG_ELOOP_SELECT */ + + +#ifdef CONFIG_ELOOP_EPOLL +static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds) +{ + struct eloop_sock *table; + int i; + + for (i = 0; i < nfds; i++) { + table = &eloop.epoll_table[events[i].data.fd]; + if (table->handler == NULL) + continue; + table->handler(table->sock, table->eloop_data, + table->user_data); + if (eloop.readers.changed || + eloop.writers.changed || + eloop.exceptions.changed) + break; + } +} +#endif /* CONFIG_ELOOP_EPOLL */ + + +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; + + assert(sock >= 0); + table = eloop_get_sock_table(type); + return eloop_sock_table_add_sock(table, sock, handler, + eloop_data, user_data); +} + + +void eloop_unregister_sock(int sock, eloop_event_type type) +{ + struct eloop_sock_table *table; + + table = eloop_get_sock_table(type); + eloop_sock_table_remove_sock(table, sock); +} + + +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *tmp; + os_time_t now_sec; + + timeout = os_zalloc(sizeof(*timeout)); + if (timeout == NULL) + return -1; + if (os_get_reltime(&timeout->time) < 0) { + os_free(timeout); + return -1; + } + now_sec = timeout->time.sec; + timeout->time.sec += secs; + if (timeout->time.sec < now_sec) { + /* + * Integer overflow - assume long enough timeout to be assumed + * to be infinite, i.e., the timeout would never happen. + */ + wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to " + "ever happen - ignore it", secs); + os_free(timeout); + return 0; + } + timeout->time.usec += usecs; + while (timeout->time.usec >= 1000000) { + timeout->time.sec++; + timeout->time.usec -= 1000000; + } + timeout->eloop_data = eloop_data; + timeout->user_data = user_data; + timeout->handler = handler; + wpa_trace_add_ref(timeout, eloop, eloop_data); + wpa_trace_add_ref(timeout, user, user_data); + wpa_trace_record(timeout); + + /* Maintain timeouts in order of increasing time */ + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (os_reltime_before(&timeout->time, &tmp->time)) { + dl_list_add(tmp->list.prev, &timeout->list); + return 0; + } + } + dl_list_add_tail(&eloop.timeout, &timeout->list); + + return 0; +} + + +static void eloop_remove_timeout(struct eloop_timeout *timeout) +{ + dl_list_del(&timeout->list); + wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data); + wpa_trace_remove_ref(timeout, user, timeout->user_data); + os_free(timeout); +} + + +int eloop_cancel_timeout(eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *prev; + int removed = 0; + + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data || + eloop_data == ELOOP_ALL_CTX) && + (timeout->user_data == user_data || + user_data == ELOOP_ALL_CTX)) { + eloop_remove_timeout(timeout); + removed++; + } + } + + return removed; +} + + +int eloop_cancel_timeout_one(eloop_timeout_handler handler, + void *eloop_data, void *user_data, + struct os_reltime *remaining) +{ + struct eloop_timeout *timeout, *prev; + int removed = 0; + struct os_reltime now; + + os_get_reltime(&now); + remaining->sec = remaining->usec = 0; + + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data) && + (timeout->user_data == user_data)) { + removed = 1; + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, remaining); + eloop_remove_timeout(timeout); + break; + } + } + return removed; +} + + +int eloop_is_timeout_registered(eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *tmp; + + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) + return 1; + } + + return 0; +} + + +int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data) +{ + struct os_reltime now, requested, remaining; + struct eloop_timeout *tmp; + + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) { + requested.sec = req_secs; + requested.usec = req_usecs; + os_get_reltime(&now); + os_reltime_sub(&tmp->time, &now, &remaining); + if (os_reltime_before(&requested, &remaining)) { + eloop_cancel_timeout(handler, eloop_data, + user_data); + eloop_register_timeout(requested.sec, + requested.usec, + handler, eloop_data, + user_data); + return 1; + } + return 0; + } + } + + return -1; +} + + +int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data) +{ + struct os_reltime now, requested, remaining; + struct eloop_timeout *tmp; + + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) { + requested.sec = req_secs; + requested.usec = req_usecs; + os_get_reltime(&now); + os_reltime_sub(&tmp->time, &now, &remaining); + if (os_reltime_before(&remaining, &requested)) { + eloop_cancel_timeout(handler, eloop_data, + user_data); + eloop_register_timeout(requested.sec, + requested.usec, + handler, eloop_data, + user_data); + return 1; + } + return 0; + } + } + + return -1; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static void eloop_handle_alarm(int sig) +{ + wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in " + "two seconds. Looks like there\n" + "is a bug that ends up in a busy loop that " + "prevents clean shutdown.\n" + "Killing program forcefully.\n"); + exit(1); +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static void eloop_handle_signal(int sig) +{ + int i; + +#ifndef CONFIG_NATIVE_WINDOWS + if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) { + /* Use SIGALRM to break out from potential busy loops that + * would not allow the program to be killed. */ + eloop.pending_terminate = 1; + signal(SIGALRM, eloop_handle_alarm); + alarm(2); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + eloop.signaled++; + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].sig == sig) { + eloop.signals[i].signaled++; + break; + } + } +} + + +static void eloop_process_pending_signals(void) +{ + int i; + + if (eloop.signaled == 0) + return; + eloop.signaled = 0; + + if (eloop.pending_terminate) { +#ifndef CONFIG_NATIVE_WINDOWS + alarm(0); +#endif /* CONFIG_NATIVE_WINDOWS */ + eloop.pending_terminate = 0; + } + + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].signaled) { + eloop.signals[i].signaled = 0; + eloop.signals[i].handler(eloop.signals[i].sig, + eloop.signals[i].user_data); + } + } +} + + +int eloop_register_signal(int sig, eloop_signal_handler handler, + void *user_data) +{ + struct eloop_signal *tmp; + + tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, + sizeof(struct eloop_signal)); + if (tmp == NULL) + return -1; + + tmp[eloop.signal_count].sig = sig; + tmp[eloop.signal_count].user_data = user_data; + tmp[eloop.signal_count].handler = handler; + tmp[eloop.signal_count].signaled = 0; + eloop.signal_count++; + eloop.signals = tmp; + signal(sig, eloop_handle_signal); + + return 0; +} + + +int eloop_register_signal_terminate(eloop_signal_handler handler, + void *user_data) +{ + int ret = eloop_register_signal(SIGINT, handler, user_data); + if (ret == 0) + ret = eloop_register_signal(SIGTERM, handler, user_data); + return ret; +} + + +int eloop_register_signal_reconfig(eloop_signal_handler handler, + void *user_data) +{ +#ifdef CONFIG_NATIVE_WINDOWS + return 0; +#else /* CONFIG_NATIVE_WINDOWS */ + return eloop_register_signal(SIGHUP, handler, user_data); +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + +void eloop_run(void) +{ +#ifdef CONFIG_ELOOP_POLL + int num_poll_fds; + int timeout_ms = 0; +#endif /* CONFIG_ELOOP_POLL */ +#ifdef CONFIG_ELOOP_SELECT + fd_set *rfds, *wfds, *efds; + struct timeval _tv; +#endif /* CONFIG_ELOOP_SELECT */ +#ifdef CONFIG_ELOOP_EPOLL + int timeout_ms = -1; +#endif /* CONFIG_ELOOP_EPOLL */ + int res; + struct os_reltime tv, now; + +#ifdef CONFIG_ELOOP_SELECT + rfds = os_malloc(sizeof(*rfds)); + wfds = os_malloc(sizeof(*wfds)); + efds = os_malloc(sizeof(*efds)); + if (rfds == NULL || wfds == NULL || efds == NULL) + goto out; +#endif /* CONFIG_ELOOP_SELECT */ + + while (!eloop.terminate && + (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || + eloop.writers.count > 0 || eloop.exceptions.count > 0)) { + struct eloop_timeout *timeout; + + if (eloop.pending_terminate) { + /* + * This may happen in some corner cases where a signal + * is received during a blocking operation. We need to + * process the pending signals and exit if requested to + * avoid hitting the SIGALRM limit if the blocking + * operation took more than two seconds. + */ + eloop_process_pending_signals(); + if (eloop.terminate) + break; + } + + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { + os_get_reltime(&now); + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, &tv); + else + tv.sec = tv.usec = 0; +#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) + timeout_ms = tv.sec * 1000 + tv.usec / 1000; +#endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */ +#ifdef CONFIG_ELOOP_SELECT + _tv.tv_sec = tv.sec; + _tv.tv_usec = tv.usec; +#endif /* CONFIG_ELOOP_SELECT */ + } + +#ifdef CONFIG_ELOOP_POLL + num_poll_fds = eloop_sock_table_set_fds( + &eloop.readers, &eloop.writers, &eloop.exceptions, + eloop.pollfds, eloop.pollfds_map, + eloop.max_pollfd_map); + res = poll(eloop.pollfds, num_poll_fds, + timeout ? timeout_ms : -1); +#endif /* CONFIG_ELOOP_POLL */ +#ifdef CONFIG_ELOOP_SELECT + 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); +#endif /* CONFIG_ELOOP_SELECT */ +#ifdef CONFIG_ELOOP_EPOLL + if (eloop.count == 0) { + res = 0; + } else { + res = epoll_wait(eloop.epollfd, eloop.epoll_events, + eloop.count, timeout_ms); + } +#endif /* CONFIG_ELOOP_EPOLL */ + if (res < 0 && errno != EINTR && errno != 0) { + wpa_printf(MSG_ERROR, "eloop: %s: %s", +#ifdef CONFIG_ELOOP_POLL + "poll" +#endif /* CONFIG_ELOOP_POLL */ +#ifdef CONFIG_ELOOP_SELECT + "select" +#endif /* CONFIG_ELOOP_SELECT */ +#ifdef CONFIG_ELOOP_EPOLL + "epoll" +#endif /* CONFIG_ELOOP_EPOLL */ + , strerror(errno)); + goto out; + } + + eloop.readers.changed = 0; + eloop.writers.changed = 0; + eloop.exceptions.changed = 0; + + eloop_process_pending_signals(); + + /* check if some registered timeouts have occurred */ + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { + os_get_reltime(&now); + if (!os_reltime_before(&now, &timeout->time)) { + void *eloop_data = timeout->eloop_data; + void *user_data = timeout->user_data; + eloop_timeout_handler handler = + timeout->handler; + eloop_remove_timeout(timeout); + handler(eloop_data, user_data); + } + + } + + if (res <= 0) + continue; + + if (eloop.readers.changed || + eloop.writers.changed || + eloop.exceptions.changed) { + /* + * Sockets may have been closed and reopened with the + * same FD in the signal or timeout handlers, so we + * must skip the previous results and check again + * whether any of the currently registered sockets have + * events. + */ + continue; + } + +#ifdef CONFIG_ELOOP_POLL + eloop_sock_table_dispatch(&eloop.readers, &eloop.writers, + &eloop.exceptions, eloop.pollfds_map, + eloop.max_pollfd_map); +#endif /* CONFIG_ELOOP_POLL */ +#ifdef CONFIG_ELOOP_SELECT + eloop_sock_table_dispatch(&eloop.readers, rfds); + eloop_sock_table_dispatch(&eloop.writers, wfds); + eloop_sock_table_dispatch(&eloop.exceptions, efds); +#endif /* CONFIG_ELOOP_SELECT */ +#ifdef CONFIG_ELOOP_EPOLL + eloop_sock_table_dispatch(eloop.epoll_events, res); +#endif /* CONFIG_ELOOP_EPOLL */ + } + + eloop.terminate = 0; +out: +#ifdef CONFIG_ELOOP_SELECT + os_free(rfds); + os_free(wfds); + os_free(efds); +#endif /* CONFIG_ELOOP_SELECT */ + return; +} + + +void eloop_terminate(void) +{ + eloop.terminate = 1; +} + + +void eloop_destroy(void) +{ + struct eloop_timeout *timeout, *prev; + struct os_reltime now; + + os_get_reltime(&now); + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + int sec, usec; + sec = timeout->time.sec - now.sec; + usec = timeout->time.usec - now.usec; + if (timeout->time.usec < now.usec) { + sec--; + usec += 1000000; + } + wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d " + "eloop_data=%p user_data=%p handler=%p", + sec, usec, timeout->eloop_data, timeout->user_data, + timeout->handler); + wpa_trace_dump_funcname("eloop unregistered timeout handler", + timeout->handler); + wpa_trace_dump("eloop timeout", timeout); + eloop_remove_timeout(timeout); + } + eloop_sock_table_destroy(&eloop.readers); + eloop_sock_table_destroy(&eloop.writers); + eloop_sock_table_destroy(&eloop.exceptions); + os_free(eloop.signals); + +#ifdef CONFIG_ELOOP_POLL + os_free(eloop.pollfds); + os_free(eloop.pollfds_map); +#endif /* CONFIG_ELOOP_POLL */ +#ifdef CONFIG_ELOOP_EPOLL + os_free(eloop.epoll_table); + os_free(eloop.epoll_events); + close(eloop.epollfd); +#endif /* CONFIG_ELOOP_EPOLL */ +} + + +int eloop_terminated(void) +{ + return eloop.terminate || eloop.pending_terminate; +} + + +void eloop_wait_for_read_sock(int sock) +{ +#ifdef CONFIG_ELOOP_POLL + struct pollfd pfd; + + if (sock < 0) + return; + + os_memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sock; + pfd.events = POLLIN; + + poll(&pfd, 1, -1); +#endif /* CONFIG_ELOOP_POLL */ +#if defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) + /* + * We can use epoll() here. But epoll() requres 4 system calls. + * epoll_create1(), epoll_ctl() for ADD, epoll_wait, and close() for + * epoll fd. So select() is better for performance here. + */ + fd_set rfds; + + if (sock < 0) + return; + + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + select(sock + 1, &rfds, NULL, NULL, NULL); +#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */ +} + +#ifdef CONFIG_ELOOP_SELECT +#undef CONFIG_ELOOP_SELECT +#endif /* CONFIG_ELOOP_SELECT */ diff --git a/freebsd/contrib/wpa/src/utils/eloop.h b/freebsd/contrib/wpa/src/utils/eloop.h new file mode 100644 index 00000000..07b8c0dc --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/eloop.h @@ -0,0 +1,359 @@ +/* + * Event loop + * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This file defines an event loop interface that supports processing events + * from registered timeouts (i.e., do something after N seconds), sockets + * (e.g., a new packet available for reading), and signals. eloop.c is an + * implementation of this interface using select() and sockets. This is + * suitable for most UNIX/POSIX systems. When porting to other operating + * systems, it may be necessary to replace that implementation with OS specific + * mechanisms. + */ + +#ifndef ELOOP_H +#define ELOOP_H + +/** + * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts + */ +#define ELOOP_ALL_CTX (void *) -1 + +/** + * eloop_event_type - eloop socket event type for eloop_register_sock() + * @EVENT_TYPE_READ: Socket has data available for reading + * @EVENT_TYPE_WRITE: Socket has room for new data to be written + * @EVENT_TYPE_EXCEPTION: An exception has been reported + */ +typedef enum { + EVENT_TYPE_READ = 0, + EVENT_TYPE_WRITE, + EVENT_TYPE_EXCEPTION +} eloop_event_type; + +/** + * eloop_sock_handler - eloop socket event callback type + * @sock: File descriptor number for the socket + * @eloop_ctx: Registered callback context data (eloop_data) + * @sock_ctx: Registered callback context data (user_data) + */ +typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx); + +/** + * eloop_event_handler - eloop generic event callback type + * @eloop_ctx: Registered callback context data (eloop_data) + * @sock_ctx: Registered callback context data (user_data) + */ +typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx); + +/** + * eloop_timeout_handler - eloop timeout event callback type + * @eloop_ctx: Registered callback context data (eloop_data) + * @sock_ctx: Registered callback context data (user_data) + */ +typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx); + +/** + * eloop_signal_handler - eloop signal event callback type + * @sig: Signal number + * @signal_ctx: Registered callback context data (user_data from + * eloop_register_signal(), eloop_register_signal_terminate(), or + * eloop_register_signal_reconfig() call) + */ +typedef void (*eloop_signal_handler)(int sig, void *signal_ctx); + +/** + * eloop_init() - Initialize global event loop data + * Returns: 0 on success, -1 on failure + * + * This function must be called before any other eloop_* function. + */ +int eloop_init(void); + +/** + * eloop_register_read_sock - Register handler for read events + * @sock: File descriptor number for the socket + * @handler: Callback function to be called when data is available for reading + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a read socket notifier for the given file descriptor. The handler + * function will be called whenever data is available for reading from the + * socket. The handler function is responsible for clearing the event after + * having processed it in order to avoid eloop from calling the handler again + * for the same event. + */ +int eloop_register_read_sock(int sock, eloop_sock_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_unregister_read_sock - Unregister handler for read events + * @sock: File descriptor number for the socket + * + * Unregister a read socket notifier that was previously registered with + * eloop_register_read_sock(). + */ +void eloop_unregister_read_sock(int sock); + +/** + * eloop_register_sock - Register handler for socket events + * @sock: File descriptor number for the socket + * @type: Type of event to wait for + * @handler: Callback function to be called when the event is triggered + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register an event notifier for the given socket's file descriptor. The + * handler function will be called whenever the that event is triggered for the + * socket. The handler function is responsible for clearing the event after + * having processed it in order to avoid eloop from calling the handler again + * for the same event. + */ +int eloop_register_sock(int sock, eloop_event_type type, + eloop_sock_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_unregister_sock - Unregister handler for socket events + * @sock: File descriptor number for the socket + * @type: Type of event for which sock was registered + * + * Unregister a socket event notifier that was previously registered with + * eloop_register_sock(). + */ +void eloop_unregister_sock(int sock, eloop_event_type type); + +/** + * eloop_register_event - Register handler for generic events + * @event: Event to wait (eloop implementation specific) + * @event_size: Size of event data + * @handler: Callback function to be called when event is triggered + * @eloop_data: Callback context data (eloop_data) + * @user_data: Callback context data (user_data) + * Returns: 0 on success, -1 on failure + * + * Register an event handler for the given event. This function is used to + * register eloop implementation specific events which are mainly targeted for + * operating system specific code (driver interface and l2_packet) since the + * portable code will not be able to use such an OS-specific call. The handler + * function will be called whenever the event is triggered. The handler + * function is responsible for clearing the event after having processed it in + * order to avoid eloop from calling the handler again for the same event. + * + * In case of Windows implementation (eloop_win.c), event pointer is of HANDLE + * type, i.e., void*. The callers are likely to have 'HANDLE h' type variable, + * and they would call this function with eloop_register_event(h, sizeof(h), + * ...). + */ +int eloop_register_event(void *event, size_t event_size, + eloop_event_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_unregister_event - Unregister handler for a generic event + * @event: Event to cancel (eloop implementation specific) + * @event_size: Size of event data + * + * Unregister a generic event notifier that was previously registered with + * eloop_register_event(). + */ +void eloop_unregister_event(void *event, size_t event_size); + +/** + * eloop_register_timeout - Register timeout + * @secs: Number of seconds to the timeout + * @usecs: Number of microseconds to the timeout + * @handler: Callback function to be called when timeout occurs + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a timeout that will cause the handler function to be called after + * given time. + */ +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + eloop_timeout_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_cancel_timeout - Cancel timeouts + * @handler: Matching callback function + * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all + * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all + * Returns: Number of cancelled timeouts + * + * Cancel matching <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_cancel_timeout_one - Cancel a single timeout + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * @remaining: Time left on the cancelled timer + * Returns: Number of cancelled timeouts + * + * Cancel matching <handler,eloop_data,user_data> timeout registered with + * eloop_register_timeout() and return the remaining time left. + */ +int eloop_cancel_timeout_one(eloop_timeout_handler handler, + void *eloop_data, void *user_data, + struct os_reltime *remaining); + +/** + * eloop_is_timeout_registered - Check if a timeout is already registered + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * Returns: 1 if the timeout is registered, 0 if the timeout is not registered + * + * Determine if a matching <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_deplete_timeout - Deplete a timeout that is already registered + * @req_secs: Requested number of seconds to the timeout + * @req_usecs: Requested number of microseconds to the timeout + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * Returns: 1 if the timeout is depleted, 0 if no change is made, -1 if no + * timeout matched + * + * Find a registered matching <handler,eloop_data,user_data> timeout. If found, + * deplete the timeout if remaining time is more than the requested time. + */ +int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data); + +/** + * eloop_replenish_timeout - Replenish a timeout that is already registered + * @req_secs: Requested number of seconds to the timeout + * @req_usecs: Requested number of microseconds to the timeout + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * Returns: 1 if the timeout is replenished, 0 if no change is made, -1 if no + * timeout matched + * + * Find a registered matching <handler,eloop_data,user_data> timeout. If found, + * replenish the timeout if remaining time is less than the requested time. + */ +int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data); + +/** + * eloop_register_signal - Register handler for signals + * @sig: Signal number (e.g., SIGHUP) + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a signal is received. + * The callback function is actually called only after the system signal + * handler has returned. This means that the normal limits for sighandlers + * (i.e., only "safe functions" allowed) do not apply for the registered + * callback. + */ +int eloop_register_signal(int sig, eloop_signal_handler handler, + void *user_data); + +/** + * eloop_register_signal_terminate - Register handler for terminate signals + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a process termination + * signal is received. The callback function is actually called only after the + * system signal handler has returned. This means that the normal limits for + * sighandlers (i.e., only "safe functions" allowed) do not apply for the + * registered callback. + * + * This function is a more portable version of eloop_register_signal() since + * the knowledge of exact details of the signals is hidden in eloop + * implementation. In case of operating systems using signal(), this function + * registers handlers for SIGINT and SIGTERM. + */ +int eloop_register_signal_terminate(eloop_signal_handler handler, + void *user_data); + +/** + * eloop_register_signal_reconfig - Register handler for reconfig signals + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a reconfiguration / + * hangup signal is received. The callback function is actually called only + * after the system signal handler has returned. This means that the normal + * limits for sighandlers (i.e., only "safe functions" allowed) do not apply + * for the registered callback. + * + * This function is a more portable version of eloop_register_signal() since + * the knowledge of exact details of the signals is hidden in eloop + * implementation. In case of operating systems using signal(), this function + * registers a handler for SIGHUP. + */ +int eloop_register_signal_reconfig(eloop_signal_handler handler, + void *user_data); + +/** + * eloop_run - Start the event loop + * + * Start the event loop and continue running as long as there are any + * registered event handlers. This function is run after event loop has been + * initialized with event_init() and one or more events have been registered. + */ +void eloop_run(void); + +/** + * eloop_terminate - Terminate event loop + * + * Terminate event loop even if there are registered events. This can be used + * to request the program to be terminated cleanly. + */ +void eloop_terminate(void); + +/** + * eloop_destroy - Free any resources allocated for the event loop + * + * After calling eloop_destroy(), other eloop_* functions must not be called + * before re-running eloop_init(). + */ +void eloop_destroy(void); + +/** + * eloop_terminated - Check whether event loop has been terminated + * Returns: 1 = event loop terminate, 0 = event loop still running + * + * This function can be used to check whether eloop_terminate() has been called + * to request termination of the event loop. This is normally used to abort + * operations that may still be queued to be run when eloop_terminate() was + * called. + */ +int eloop_terminated(void); + +/** + * eloop_wait_for_read_sock - Wait for a single reader + * @sock: File descriptor number for the socket + * + * Do a blocking wait for a single read socket. + */ +void eloop_wait_for_read_sock(int sock); + +#endif /* ELOOP_H */ diff --git a/freebsd/contrib/wpa/src/utils/ext_password.h b/freebsd/contrib/wpa/src/utils/ext_password.h new file mode 100644 index 00000000..e3e46ea0 --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/ext_password.h @@ -0,0 +1,33 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_H +#define EXT_PASSWORD_H + +struct ext_password_data; + +#ifdef CONFIG_EXT_PASSWORD + +struct ext_password_data * ext_password_init(const char *backend, + const char *params); +void ext_password_deinit(struct ext_password_data *data); + +struct wpabuf * ext_password_get(struct ext_password_data *data, + const char *name); +void ext_password_free(struct wpabuf *pw); + +#else /* CONFIG_EXT_PASSWORD */ + +#define ext_password_init(b, p) ((void *) 1) +#define ext_password_deinit(d) do { } while (0) +#define ext_password_get(d, n) (NULL) +#define ext_password_free(p) do { } while (0) + +#endif /* CONFIG_EXT_PASSWORD */ + +#endif /* EXT_PASSWORD_H */ diff --git a/freebsd/contrib/wpa/src/utils/includes.h b/freebsd/contrib/wpa/src/utils/includes.h new file mode 100644 index 00000000..75513fc8 --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/includes.h @@ -0,0 +1,45 @@ +/* + * wpa_supplicant/hostapd - Default include files + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This header file is included into all C files so that commonly used header + * files can be selected with OS specific ifdef blocks in one place instead of + * having to have OS/C library specific selection in many files. + */ + +#ifndef INCLUDES_H +#define INCLUDES_H + +/* Include possible build time configuration before including anything else */ +#include "build_config.h" + +#include <stdlib.h> +#include <stddef.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#ifndef _WIN32_WCE +#include <signal.h> +#include <sys/types.h> +#include <errno.h> +#endif /* _WIN32_WCE */ +#include <ctype.h> + +#ifndef _MSC_VER +#include <unistd.h> +#endif /* _MSC_VER */ + +#ifndef CONFIG_NATIVE_WINDOWS +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#ifndef __vxworks +#include <sys/uio.h> +#include <sys/time.h> +#endif /* __vxworks */ +#endif /* CONFIG_NATIVE_WINDOWS */ + +#endif /* INCLUDES_H */ diff --git a/freebsd/contrib/wpa/src/utils/ip_addr.h b/freebsd/contrib/wpa/src/utils/ip_addr.h new file mode 100644 index 00000000..0670411c --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/ip_addr.h @@ -0,0 +1,27 @@ +/* + * IP address processing + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IP_ADDR_H +#define IP_ADDR_H + +struct hostapd_ip_addr { + int af; /* AF_INET / AF_INET6 */ + union { + struct in_addr v4; +#ifdef CONFIG_IPV6 + struct in6_addr v6; +#endif /* CONFIG_IPV6 */ + u8 max_len[16]; + } u; +}; + +const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, + size_t buflen); +int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr); + +#endif /* IP_ADDR_H */ diff --git a/freebsd/contrib/wpa/src/utils/list.h b/freebsd/contrib/wpa/src/utils/list.h new file mode 100644 index 00000000..ee2f4856 --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/list.h @@ -0,0 +1,97 @@ +/* + * Doubly-linked list + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef LIST_H +#define LIST_H + +/** + * struct dl_list - Doubly-linked list + */ +struct dl_list { + struct dl_list *next; + struct dl_list *prev; +}; + +#define DL_LIST_HEAD_INIT(l) { &(l), &(l) } + +static inline void dl_list_init(struct dl_list *list) +{ + list->next = list; + list->prev = list; +} + +static inline void dl_list_add(struct dl_list *list, struct dl_list *item) +{ + item->next = list->next; + item->prev = list; + list->next->prev = item; + list->next = item; +} + +static inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item) +{ + dl_list_add(list->prev, item); +} + +static inline void dl_list_del(struct dl_list *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; + item->next = NULL; + item->prev = NULL; +} + +static inline int dl_list_empty(struct dl_list *list) +{ + return list->next == list; +} + +static inline unsigned int dl_list_len(struct dl_list *list) +{ + struct dl_list *item; + int count = 0; + for (item = list->next; item != list; item = item->next) + count++; + return count; +} + +#ifndef offsetof +#define offsetof(type, member) ((long) &((type *) 0)->member) +#endif + +#define dl_list_entry(item, type, member) \ + ((type *) ((char *) item - offsetof(type, member))) + +#define dl_list_first(list, type, member) \ + (dl_list_empty((list)) ? NULL : \ + dl_list_entry((list)->next, type, member)) + +#define dl_list_last(list, type, member) \ + (dl_list_empty((list)) ? NULL : \ + dl_list_entry((list)->prev, type, member)) + +#define dl_list_for_each(item, list, type, member) \ + for (item = dl_list_entry((list)->next, type, member); \ + &item->member != (list); \ + item = dl_list_entry(item->member.next, type, member)) + +#define dl_list_for_each_safe(item, n, list, type, member) \ + for (item = dl_list_entry((list)->next, type, member), \ + n = dl_list_entry(item->member.next, type, member); \ + &item->member != (list); \ + item = n, n = dl_list_entry(n->member.next, type, member)) + +#define dl_list_for_each_reverse(item, list, type, member) \ + for (item = dl_list_entry((list)->prev, type, member); \ + &item->member != (list); \ + item = dl_list_entry(item->member.prev, type, member)) + +#define DEFINE_DL_LIST(name) \ + struct dl_list name = { &(name), &(name) } + +#endif /* LIST_H */ diff --git a/freebsd/contrib/wpa/src/utils/os.h b/freebsd/contrib/wpa/src/utils/os.h new file mode 100644 index 00000000..9e496fb6 --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/os.h @@ -0,0 +1,664 @@ +/* + * OS specific functions + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef OS_H +#define OS_H + +typedef long os_time_t; + +/** + * os_sleep - Sleep (sec, usec) + * @sec: Number of seconds to sleep + * @usec: Number of microseconds to sleep + */ +void os_sleep(os_time_t sec, os_time_t usec); + +struct os_time { + os_time_t sec; + os_time_t usec; +}; + +struct os_reltime { + os_time_t sec; + os_time_t usec; +}; + +/** + * os_get_time - Get current time (sec, usec) + * @t: Pointer to buffer for the time + * Returns: 0 on success, -1 on failure + */ +int os_get_time(struct os_time *t); + +/** + * os_get_reltime - Get relative time (sec, usec) + * @t: Pointer to buffer for the time + * Returns: 0 on success, -1 on failure + */ +int os_get_reltime(struct os_reltime *t); + + +/* Helpers for handling struct os_time */ + +static inline int os_time_before(struct os_time *a, struct os_time *b) +{ + return (a->sec < b->sec) || + (a->sec == b->sec && a->usec < b->usec); +} + + +static inline void os_time_sub(struct os_time *a, struct os_time *b, + struct os_time *res) +{ + res->sec = a->sec - b->sec; + res->usec = a->usec - b->usec; + if (res->usec < 0) { + res->sec--; + res->usec += 1000000; + } +} + + +/* Helpers for handling struct os_reltime */ + +static inline int os_reltime_before(struct os_reltime *a, + struct os_reltime *b) +{ + return (a->sec < b->sec) || + (a->sec == b->sec && a->usec < b->usec); +} + + +static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b, + struct os_reltime *res) +{ + res->sec = a->sec - b->sec; + res->usec = a->usec - b->usec; + if (res->usec < 0) { + res->sec--; + res->usec += 1000000; + } +} + + +static inline void os_reltime_age(struct os_reltime *start, + struct os_reltime *age) +{ + struct os_reltime now; + + os_get_reltime(&now); + os_reltime_sub(&now, start, age); +} + + +static inline int os_reltime_expired(struct os_reltime *now, + struct os_reltime *ts, + os_time_t timeout_secs) +{ + struct os_reltime age; + + os_reltime_sub(now, ts, &age); + return (age.sec > timeout_secs) || + (age.sec == timeout_secs && age.usec > 0); +} + + +static inline int os_reltime_initialized(struct os_reltime *t) +{ + return t->sec != 0 || t->usec != 0; +} + + +/** + * os_mktime - Convert broken-down time into seconds since 1970-01-01 + * @year: Four digit year + * @month: Month (1 .. 12) + * @day: Day of month (1 .. 31) + * @hour: Hour (0 .. 23) + * @min: Minute (0 .. 59) + * @sec: Second (0 .. 60) + * @t: Buffer for returning calendar time representation (seconds since + * 1970-01-01 00:00:00) + * Returns: 0 on success, -1 on failure + * + * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time + * which is used by POSIX mktime(). + */ +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t); + +struct os_tm { + int sec; /* 0..59 or 60 for leap seconds */ + int min; /* 0..59 */ + int hour; /* 0..23 */ + int day; /* 1..31 */ + int month; /* 1..12 */ + int year; /* Four digit year */ +}; + +int os_gmtime(os_time_t t, struct os_tm *tm); + +/** + * os_daemonize - Run in the background (detach from the controlling terminal) + * @pid_file: File name to write the process ID to or %NULL to skip this + * Returns: 0 on success, -1 on failure + */ +int os_daemonize(const char *pid_file); + +/** + * os_daemonize_terminate - Stop running in the background (remove pid file) + * @pid_file: File name to write the process ID to or %NULL to skip this + */ +void os_daemonize_terminate(const char *pid_file); + +/** + * os_get_random - Get cryptographically strong pseudo random data + * @buf: Buffer for pseudo random data + * @len: Length of the buffer + * Returns: 0 on success, -1 on failure + */ +int os_get_random(unsigned char *buf, size_t len); + +/** + * os_random - Get pseudo random value (not necessarily very strong) + * Returns: Pseudo random value + */ +unsigned long os_random(void); + +/** + * os_rel2abs_path - Get an absolute path for a file + * @rel_path: Relative path to a file + * Returns: Absolute path for the file or %NULL on failure + * + * This function tries to convert a relative path of a file to an absolute path + * in order for the file to be found even if current working directory has + * changed. The returned value is allocated and caller is responsible for + * freeing it. It is acceptable to just return the same path in an allocated + * buffer, e.g., return strdup(rel_path). This function is only used to find + * configuration files when os_daemonize() may have changed the current working + * directory and relative path would be pointing to a different location. + */ +char * os_rel2abs_path(const char *rel_path); + +/** + * os_program_init - Program initialization (called at start) + * Returns: 0 on success, -1 on failure + * + * This function is called when a programs starts. If there are any OS specific + * processing that is needed, it can be placed here. It is also acceptable to + * just return 0 if not special processing is needed. + */ +int os_program_init(void); + +/** + * os_program_deinit - Program deinitialization (called just before exit) + * + * This function is called just before a program exists. If there are any OS + * specific processing, e.g., freeing resourced allocated in os_program_init(), + * it should be done here. It is also acceptable for this function to do + * nothing. + */ +void os_program_deinit(void); + +/** + * os_setenv - Set environment variable + * @name: Name of the variable + * @value: Value to set to the variable + * @overwrite: Whether existing variable should be overwritten + * Returns: 0 on success, -1 on error + * + * This function is only used for wpa_cli action scripts. OS wrapper does not + * need to implement this if such functionality is not needed. + */ +int os_setenv(const char *name, const char *value, int overwrite); + +/** + * os_unsetenv - Delete environent variable + * @name: Name of the variable + * Returns: 0 on success, -1 on error + * + * This function is only used for wpa_cli action scripts. OS wrapper does not + * need to implement this if such functionality is not needed. + */ +int os_unsetenv(const char *name); + +/** + * os_readfile - Read a file to an allocated memory buffer + * @name: Name of the file to read + * @len: For returning the length of the allocated buffer + * Returns: Pointer to the allocated buffer or %NULL on failure + * + * This function allocates memory and reads the given file to this buffer. Both + * binary and text files can be read with this function. The caller is + * responsible for freeing the returned buffer with os_free(). + */ +char * os_readfile(const char *name, size_t *len); + +/** + * os_file_exists - Check whether the specified file exists + * @fname: Path and name of the file + * Returns: 1 if the file exists or 0 if not + */ +int os_file_exists(const char *fname); + +/** + * os_fdatasync - Sync a file's (for a given stream) state with storage device + * @stream: the stream to be flushed + * Returns: 0 if the operation succeeded or -1 on failure + */ +int os_fdatasync(FILE *stream); + +/** + * os_zalloc - Allocate and zero memory + * @size: Number of bytes to allocate + * Returns: Pointer to allocated and zeroed memory or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +void * os_zalloc(size_t size); + +/** + * os_calloc - Allocate and zero memory for an array + * @nmemb: Number of members in the array + * @size: Number of bytes in each member + * Returns: Pointer to allocated and zeroed memory or %NULL on failure + * + * This function can be used as a wrapper for os_zalloc(nmemb * size) when an + * allocation is used for an array. The main benefit over os_zalloc() is in + * having an extra check to catch integer overflows in multiplication. + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +static inline void * os_calloc(size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_zalloc(nmemb * size); +} + + +/* + * The following functions are wrapper for standard ANSI C or POSIX functions. + * By default, they are just defined to use the standard function name and no + * os_*.c implementation is needed for them. This avoids extra function calls + * by allowing the C pre-processor take care of the function name mapping. + * + * If the target system uses a C library that does not provide these functions, + * build_config.h can be used to define the wrappers to use a different + * function name. This can be done on function-by-function basis since the + * defines here are only used if build_config.h does not define the os_* name. + * If needed, os_*.c file can be used to implement the functions that are not + * included in the C library on the target system. Alternatively, + * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case + * these functions need to be implemented in os_*.c file for the target system. + */ + +#ifdef OS_NO_C_LIB_DEFINES + +/** + * os_malloc - Allocate dynamic memory + * @size: Size of the buffer to allocate + * Returns: Allocated buffer or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +void * os_malloc(size_t size); + +/** + * os_realloc - Re-allocate dynamic memory + * @ptr: Old buffer from os_malloc() or os_realloc() + * @size: Size of the new buffer + * Returns: Allocated buffer or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + * If re-allocation fails, %NULL is returned and the original buffer (ptr) is + * not freed and caller is still responsible for freeing it. + */ +void * os_realloc(void *ptr, size_t size); + +/** + * os_free - Free dynamic memory + * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL + */ +void os_free(void *ptr); + +/** + * os_memcpy - Copy memory area + * @dest: Destination + * @src: Source + * @n: Number of bytes to copy + * Returns: dest + * + * The memory areas src and dst must not overlap. os_memmove() can be used with + * overlapping memory. + */ +void * os_memcpy(void *dest, const void *src, size_t n); + +/** + * os_memmove - Copy memory area + * @dest: Destination + * @src: Source + * @n: Number of bytes to copy + * Returns: dest + * + * The memory areas src and dst may overlap. + */ +void * os_memmove(void *dest, const void *src, size_t n); + +/** + * os_memset - Fill memory with a constant byte + * @s: Memory area to be filled + * @c: Constant byte + * @n: Number of bytes started from s to fill with c + * Returns: s + */ +void * os_memset(void *s, int c, size_t n); + +/** + * os_memcmp - Compare memory areas + * @s1: First buffer + * @s2: Second buffer + * @n: Maximum numbers of octets to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_memcmp(const void *s1, const void *s2, size_t n); + +/** + * os_strdup - Duplicate a string + * @s: Source string + * Returns: Allocated buffer with the string copied into it or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +char * os_strdup(const char *s); + +/** + * os_strlen - Calculate the length of a string + * @s: '\0' terminated string + * Returns: Number of characters in s (not counting the '\0' terminator) + */ +size_t os_strlen(const char *s); + +/** + * os_strcasecmp - Compare two strings ignoring case + * @s1: First string + * @s2: Second string + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greatred than s2 + */ +int os_strcasecmp(const char *s1, const char *s2); + +/** + * os_strncasecmp - Compare two strings ignoring case + * @s1: First string + * @s2: Second string + * @n: Maximum numbers of characters to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_strncasecmp(const char *s1, const char *s2, size_t n); + +/** + * os_strchr - Locate the first occurrence of a character in string + * @s: String + * @c: Character to search for + * Returns: Pointer to the matched character or %NULL if not found + */ +char * os_strchr(const char *s, int c); + +/** + * os_strrchr - Locate the last occurrence of a character in string + * @s: String + * @c: Character to search for + * Returns: Pointer to the matched character or %NULL if not found + */ +char * os_strrchr(const char *s, int c); + +/** + * os_strcmp - Compare two strings + * @s1: First string + * @s2: Second string + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greatred than s2 + */ +int os_strcmp(const char *s1, const char *s2); + +/** + * os_strncmp - Compare two strings + * @s1: First string + * @s2: Second string + * @n: Maximum numbers of characters to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_strncmp(const char *s1, const char *s2, size_t n); + +/** + * os_strstr - Locate a substring + * @haystack: String (haystack) to search from + * @needle: Needle to search from haystack + * Returns: Pointer to the beginning of the substring or %NULL if not found + */ +char * os_strstr(const char *haystack, const char *needle); + +/** + * os_snprintf - Print to a memory buffer + * @str: Memory buffer to print into + * @size: Maximum length of the str buffer + * @format: printf format + * Returns: Number of characters printed (not including trailing '\0'). + * + * If the output buffer is truncated, number of characters which would have + * been written is returned. Since some C libraries return -1 in such a case, + * the caller must be prepared on that value, too, to indicate truncation. + * + * Note: Some C library implementations of snprintf() may not guarantee null + * termination in case the output is truncated. The OS wrapper function of + * os_snprintf() should provide this guarantee, i.e., to null terminate the + * output buffer if a C library version of the function is used and if that + * function does not guarantee null termination. + * + * If the target system does not include snprintf(), see, e.g., + * http://www.ijs.si/software/snprintf/ for an example of a portable + * implementation of snprintf. + */ +int os_snprintf(char *str, size_t size, const char *format, ...); + +#else /* OS_NO_C_LIB_DEFINES */ + +#ifdef WPA_TRACE +void * os_malloc(size_t size); +void * os_realloc(void *ptr, size_t size); +void os_free(void *ptr); +char * os_strdup(const char *s); +#else /* WPA_TRACE */ +#ifndef os_malloc +#define os_malloc(s) malloc((s)) +#endif +#ifndef os_realloc +#define os_realloc(p, s) realloc((p), (s)) +#endif +#ifndef os_free +#define os_free(p) free((p)) +#endif +#ifndef os_strdup +#ifdef _MSC_VER +#define os_strdup(s) _strdup(s) +#else +#define os_strdup(s) strdup(s) +#endif +#endif +#endif /* WPA_TRACE */ + +#ifndef os_memcpy +#define os_memcpy(d, s, n) memcpy((d), (s), (n)) +#endif +#ifndef os_memmove +#define os_memmove(d, s, n) memmove((d), (s), (n)) +#endif +#ifndef os_memset +#define os_memset(s, c, n) memset(s, c, n) +#endif +#ifndef os_memcmp +#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n)) +#endif + +#ifndef os_strlen +#define os_strlen(s) strlen(s) +#endif +#ifndef os_strcasecmp +#ifdef _MSC_VER +#define os_strcasecmp(s1, s2) _stricmp((s1), (s2)) +#else +#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2)) +#endif +#endif +#ifndef os_strncasecmp +#ifdef _MSC_VER +#define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n)) +#else +#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n)) +#endif +#endif +#ifndef os_strchr +#define os_strchr(s, c) strchr((s), (c)) +#endif +#ifndef os_strcmp +#define os_strcmp(s1, s2) strcmp((s1), (s2)) +#endif +#ifndef os_strncmp +#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) +#endif +#ifndef os_strrchr +#define os_strrchr(s, c) strrchr((s), (c)) +#endif +#ifndef os_strstr +#define os_strstr(h, n) strstr((h), (n)) +#endif + +#ifndef os_snprintf +#ifdef _MSC_VER +#define os_snprintf _snprintf +#else +#define os_snprintf snprintf +#endif +#endif + +#endif /* OS_NO_C_LIB_DEFINES */ + + +static inline int os_snprintf_error(size_t size, int res) +{ + return res < 0 || (unsigned int) res >= size; +} + + +static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_realloc(ptr, nmemb * size); +} + +/** + * os_remove_in_array - Remove a member from an array by index + * @ptr: Pointer to the array + * @nmemb: Current member count of the array + * @size: The size per member of the array + * @idx: Index of the member to be removed + */ +static inline void os_remove_in_array(void *ptr, size_t nmemb, size_t size, + size_t idx) +{ + if (idx < nmemb - 1) + os_memmove(((unsigned char *) ptr) + idx * size, + ((unsigned char *) ptr) + (idx + 1) * size, + (nmemb - idx - 1) * size); +} + +/** + * os_strlcpy - Copy a string with size bound and NUL-termination + * @dest: Destination + * @src: Source + * @siz: Size of the target buffer + * Returns: Total length of the target string (length of src) (not including + * NUL-termination) + * + * This function matches in behavior with the strlcpy(3) function in OpenBSD. + */ +size_t os_strlcpy(char *dest, const char *src, size_t siz); + +/** + * os_memcmp_const - Constant time memory comparison + * @a: First buffer to compare + * @b: Second buffer to compare + * @len: Number of octets to compare + * Returns: 0 if buffers are equal, non-zero if not + * + * This function is meant for comparing passwords or hash values where + * difference in execution time could provide external observer information + * about the location of the difference in the memory buffers. The return value + * does not behave like os_memcmp(), i.e., os_memcmp_const() cannot be used to + * sort items into a defined order. Unlike os_memcmp(), execution time of + * os_memcmp_const() does not depend on the contents of the compared memory + * buffers, but only on the total compared length. + */ +int os_memcmp_const(const void *a, const void *b, size_t len); + +/** + * os_exec - Execute an external program + * @program: Path to the program + * @arg: Command line argument string + * @wait_completion: Whether to wait until the program execution completes + * Returns: 0 on success, -1 on error + */ +int os_exec(const char *program, const char *arg, int wait_completion); + + +#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 */ + + +#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) +#define TEST_FAIL() testing_test_fail() +int testing_test_fail(void); +#else +#define TEST_FAIL() 0 +#endif + +#endif /* OS_H */ diff --git a/freebsd/contrib/wpa/src/utils/os_unix.c b/freebsd/contrib/wpa/src/utils/os_unix.c new file mode 100644 index 00000000..a3abce4c --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/os_unix.c @@ -0,0 +1,851 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * OS specific functions for UNIX/POSIX systems + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include <time.h> +#include <sys/wait.h> + +#ifdef ANDROID +#include <sys/capability.h> +#include <sys/prctl.h> +#include <private/android_filesystem_config.h> +#endif /* ANDROID */ + +#ifdef __MACH__ +#include <CoreServices/CoreServices.h> +#include <mach/mach.h> +#include <mach/mach_time.h> +#endif /* __MACH__ */ + +#include "os.h" +#include "common.h" + +#ifdef WPA_TRACE + +#include "wpa_debug.h" +#include "trace.h" +#include "list.h" + +static struct dl_list alloc_list = DL_LIST_HEAD_INIT(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 +} __attribute__((aligned(16))); + +#endif /* WPA_TRACE */ + + +void os_sleep(os_time_t sec, os_time_t usec) +{ + if (sec) + sleep(sec); + if (usec) + usleep(usec); +} + + +int os_get_time(struct os_time *t) +{ + int res; + struct timeval tv; + res = gettimeofday(&tv, NULL); + t->sec = tv.tv_sec; + t->usec = tv.tv_usec; + return res; +} + + +int os_get_reltime(struct os_reltime *t) +{ +#ifndef __MACH__ +#if defined(CLOCK_BOOTTIME) + static clockid_t clock_id = CLOCK_BOOTTIME; +#elif defined(CLOCK_MONOTONIC) + static clockid_t clock_id = CLOCK_MONOTONIC; +#else + static clockid_t clock_id = CLOCK_REALTIME; +#endif + struct timespec ts; + int res; + + while (1) { + res = clock_gettime(clock_id, &ts); + if (res == 0) { + t->sec = ts.tv_sec; + t->usec = ts.tv_nsec / 1000; + return 0; + } + switch (clock_id) { +#ifdef CLOCK_BOOTTIME + case CLOCK_BOOTTIME: + clock_id = CLOCK_MONOTONIC; + break; +#endif +#ifdef CLOCK_MONOTONIC + case CLOCK_MONOTONIC: + clock_id = CLOCK_REALTIME; + break; +#endif + case CLOCK_REALTIME: + return -1; + } + } +#else /* __MACH__ */ + uint64_t abstime, nano; + static mach_timebase_info_data_t info = { 0, 0 }; + + if (!info.denom) { + if (mach_timebase_info(&info) != KERN_SUCCESS) + return -1; + } + + abstime = mach_absolute_time(); + nano = (abstime * info.numer) / info.denom; + + t->sec = nano / NSEC_PER_SEC; + t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC; + + return 0; +#endif /* __MACH__ */ +} + + +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t) +{ + struct tm tm, *tm1; + time_t t_local, t1, t2; + os_time_t tz_offset; + + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || + hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || + sec > 60) + return -1; + + memset(&tm, 0, sizeof(tm)); + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + + t_local = mktime(&tm); + + /* figure out offset to UTC */ + tm1 = localtime(&t_local); + if (tm1) { + t1 = mktime(tm1); + tm1 = gmtime(&t_local); + if (tm1) { + t2 = mktime(tm1); + tz_offset = t2 - t1; + } else + tz_offset = 0; + } else + tz_offset = 0; + + *t = (os_time_t) t_local - tz_offset; + return 0; +} + + +int os_gmtime(os_time_t t, struct os_tm *tm) +{ + struct tm *tm2; + time_t t2 = t; + + tm2 = gmtime(&t2); + if (tm2 == NULL) + return -1; + tm->sec = tm2->tm_sec; + tm->min = tm2->tm_min; + tm->hour = tm2->tm_hour; + tm->day = tm2->tm_mday; + tm->month = tm2->tm_mon + 1; + tm->year = tm2->tm_year + 1900; + return 0; +} + + +#ifdef __APPLE__ +#include <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__ */ + + +#ifdef __FreeBSD__ +#include <err.h> +#include <libutil.h> +#include <stdint.h> +#endif /* __FreeBSD__ */ + +int os_daemonize(const char *pid_file) +{ +#if defined(__uClinux__) || defined(__sun__) + return -1; +#else /* defined(__uClinux__) || defined(__sun__) */ +#ifdef __FreeBSD__ + pid_t otherpid; + struct pidfh *pfh; + + pfh = pidfile_open(pid_file, 0600, &otherpid); + if (pfh == NULL) { + if (errno == EEXIST) { + errx(1, "Daemon already running, pid: %jd.", + (intmax_t)otherpid); + } + warn("Cannot open or create pidfile."); + } +#endif /* __FreeBSD__ */ + + if (os_daemon(0, 0)) { + perror("daemon"); +#ifdef __FreeBSD__ + pidfile_remove(pfh); +#endif /* __FreeBSD__ */ + return -1; + } + +#ifndef __FreeBSD__ + if (pid_file) { + FILE *f = fopen(pid_file, "w"); + if (f) { + fprintf(f, "%u\n", getpid()); + fclose(f); + } + } +#else /* __FreeBSD__ */ + pidfile_write(pfh); +#endif /* __FreeBSD__ */ + + 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; + + if (TEST_FAIL()) + return -1; + + f = fopen("/dev/urandom", "rb"); + if (f == NULL) { + printf("Could not open /dev/urandom.\n"); + return -1; + } + + rc = fread(buf, 1, len, f); + fclose(f); + + return rc != len ? -1 : 0; +} + + +unsigned long os_random(void) +{ + return random(); +} + + +char * os_rel2abs_path(const char *rel_path) +{ + char *buf = NULL, *cwd, *ret; + size_t len = 128, cwd_len, rel_len, ret_len; + int last_errno; + + if (!rel_path) + return NULL; + + if (rel_path[0] == '/') + return os_strdup(rel_path); + + for (;;) { + buf = os_malloc(len); + if (buf == NULL) + return NULL; + cwd = getcwd(buf, len); + if (cwd == NULL) { + last_errno = errno; + os_free(buf); + if (last_errno != ERANGE) + return NULL; + len *= 2; + if (len > 2000) + return NULL; + } else { + buf[len - 1] = '\0'; + break; + } + } + + cwd_len = os_strlen(cwd); + rel_len = os_strlen(rel_path); + ret_len = cwd_len + 1 + rel_len + 1; + ret = os_malloc(ret_len); + if (ret) { + os_memcpy(ret, cwd, cwd_len); + ret[cwd_len] = '/'; + os_memcpy(ret + cwd_len + 1, rel_path, rel_len); + ret[ret_len - 1] = '\0'; + } + os_free(buf); + return ret; +} + + +int os_program_init(void) +{ +#ifdef ANDROID + /* + * We ignore errors here since errors are normal if we + * are already running as non-root. + */ +#ifdef ANDROID_SETGROUPS_OVERRIDE + gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE }; +#else /* ANDROID_SETGROUPS_OVERRIDE */ + gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE }; +#endif /* ANDROID_SETGROUPS_OVERRIDE */ + struct __user_cap_header_struct header; + struct __user_cap_data_struct cap; + + setgroups(ARRAY_SIZE(groups), groups); + + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + + setgid(AID_WIFI); + setuid(AID_WIFI); + + header.version = _LINUX_CAPABILITY_VERSION; + header.pid = 0; + cap.effective = cap.permitted = + (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW); + cap.inheritable = 0; + capset(&header, &cap); +#endif /* ANDROID */ + + 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; +} + + +int os_file_exists(const char *fname) +{ + FILE *f = fopen(fname, "rb"); + if (f == NULL) + return 0; + fclose(f); + return 1; +} + + +int os_fdatasync(FILE *stream) +{ + if (!fflush(stream)) { +#ifdef __linux__ + return fdatasync(fileno(stream)); +#else /* !__linux__ */ +#ifdef F_FULLFSYNC + /* OS X does not implement fdatasync(). */ + return fcntl(fileno(stream), F_FULLFSYNC); +#else /* F_FULLFSYNC */ + return fsync(fileno(stream)); +#endif /* F_FULLFSYNC */ +#endif /* __linux__ */ + } + + return -1; +} + + +#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; +} + + +int os_memcmp_const(const void *a, const void *b, size_t len) +{ + const u8 *aa = a; + const u8 *bb = b; + size_t i; + u8 res; + + for (res = 0, i = 0; i < len; i++) + res |= aa[i] ^ bb[i]; + + return res; +} + + +#ifdef WPA_TRACE + +#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) +char wpa_trace_fail_func[256] = { 0 }; +unsigned int wpa_trace_fail_after; + +static int testing_fail_alloc(void) +{ + const char *func[WPA_TRACE_LEN]; + size_t i, res, len; + char *pos, *next; + int match; + + if (!wpa_trace_fail_after) + return 0; + + res = wpa_trace_calling_func(func, WPA_TRACE_LEN); + i = 0; + if (i < res && os_strcmp(func[i], __func__) == 0) + i++; + if (i < res && os_strcmp(func[i], "os_malloc") == 0) + i++; + if (i < res && os_strcmp(func[i], "os_zalloc") == 0) + i++; + if (i < res && os_strcmp(func[i], "os_calloc") == 0) + i++; + if (i < res && os_strcmp(func[i], "os_realloc") == 0) + i++; + if (i < res && os_strcmp(func[i], "os_realloc_array") == 0) + i++; + if (i < res && os_strcmp(func[i], "os_strdup") == 0) + i++; + + pos = wpa_trace_fail_func; + + match = 0; + while (i < res) { + int allow_skip = 1; + int maybe = 0; + + if (*pos == '=') { + allow_skip = 0; + pos++; + } else if (*pos == '?') { + maybe = 1; + pos++; + } + next = os_strchr(pos, ';'); + if (next) + len = next - pos; + else + len = os_strlen(pos); + if (os_memcmp(pos, func[i], len) != 0) { + if (maybe && next) { + pos = next + 1; + continue; + } + if (allow_skip) { + i++; + continue; + } + return 0; + } + if (!next) { + match = 1; + break; + } + pos = next + 1; + i++; + } + if (!match) + return 0; + + wpa_trace_fail_after--; + if (wpa_trace_fail_after == 0) { + wpa_printf(MSG_INFO, "TESTING: fail allocation at %s", + wpa_trace_fail_func); + for (i = 0; i < res; i++) + wpa_printf(MSG_INFO, "backtrace[%d] = %s", + (int) i, func[i]); + return 1; + } + + return 0; +} + + +char wpa_trace_test_fail_func[256] = { 0 }; +unsigned int wpa_trace_test_fail_after; + +int testing_test_fail(void) +{ + const char *func[WPA_TRACE_LEN]; + size_t i, res, len; + char *pos, *next; + int match; + + if (!wpa_trace_test_fail_after) + return 0; + + res = wpa_trace_calling_func(func, WPA_TRACE_LEN); + i = 0; + if (i < res && os_strcmp(func[i], __func__) == 0) + i++; + + pos = wpa_trace_test_fail_func; + + match = 0; + while (i < res) { + int allow_skip = 1; + int maybe = 0; + + if (*pos == '=') { + allow_skip = 0; + pos++; + } else if (*pos == '?') { + maybe = 1; + pos++; + } + next = os_strchr(pos, ';'); + if (next) + len = next - pos; + else + len = os_strlen(pos); + if (os_memcmp(pos, func[i], len) != 0) { + if (maybe && next) { + pos = next + 1; + continue; + } + if (allow_skip) { + i++; + continue; + } + return 0; + } + if (!next) { + match = 1; + break; + } + pos = next + 1; + i++; + } + if (!match) + return 0; + + wpa_trace_test_fail_after--; + if (wpa_trace_test_fail_after == 0) { + wpa_printf(MSG_INFO, "TESTING: fail at %s", + wpa_trace_test_fail_func); + for (i = 0; i < res; i++) + wpa_printf(MSG_INFO, "backtrace[%d] = %s", + (int) i, func[i]); + return 1; + } + + return 0; +} + +#else + +static inline int testing_fail_alloc(void) +{ + return 0; +} +#endif + +void * os_malloc(size_t size) +{ + struct os_alloc_trace *a; + + if (testing_fail_alloc()) + return NULL; + + 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 */ + + +int os_exec(const char *program, const char *arg, int wait_completion) +{ + pid_t pid; + int pid_status; + + pid = fork(); + if (pid < 0) { + perror("fork"); + return -1; + } + + if (pid == 0) { + /* run the external command in the child process */ + const int MAX_ARG = 30; + char *_program, *_arg, *pos; + char *argv[MAX_ARG + 1]; + int i; + + _program = os_strdup(program); + _arg = os_strdup(arg); + + argv[0] = _program; + + i = 1; + pos = _arg; + while (i < MAX_ARG && pos && *pos) { + while (*pos == ' ') + pos++; + if (*pos == '\0') + break; + argv[i++] = pos; + pos = os_strchr(pos, ' '); + if (pos) + *pos++ = '\0'; + } + argv[i] = NULL; + + execv(program, argv); + perror("execv"); + os_free(_program); + os_free(_arg); + exit(0); + return -1; + } + + if (wait_completion) { + /* wait for the child process to complete in the parent */ + waitpid(pid, &pid_status, 0); + } + + return 0; +} diff --git a/freebsd/contrib/wpa/src/utils/pcsc_funcs.h b/freebsd/contrib/wpa/src/utils/pcsc_funcs.h new file mode 100644 index 00000000..eacd2a2d --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/pcsc_funcs.h @@ -0,0 +1,42 @@ +/* + * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM + * Copyright (c) 2004-2006, 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PCSC_FUNCS_H +#define PCSC_FUNCS_H + +#ifdef PCSC_FUNCS +struct scard_data * scard_init(const char *reader); +void scard_deinit(struct scard_data *scard); + +int scard_set_pin(struct scard_data *scard, const char *pin); +int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len); +int scard_get_mnc_len(struct scard_data *scard); +int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand, + unsigned char *sres, unsigned char *kc); +int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, + const unsigned char *autn, + unsigned char *res, size_t *res_len, + unsigned char *ik, unsigned char *ck, unsigned char *auts); +int scard_get_pin_retry_counter(struct scard_data *scard); +int scard_supports_umts(struct scard_data *scard); + +#else /* PCSC_FUNCS */ + +#define scard_init(r) NULL +#define scard_deinit(s) do { } while (0) +#define scard_set_pin(s, p) -1 +#define scard_get_imsi(s, i, l) -1 +#define scard_get_mnc_len(s) -1 +#define scard_gsm_auth(s, r, s2, k) -1 +#define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1 +#define scard_get_pin_retry_counter(s) -1 +#define scard_supports_umts(s) 0 + +#endif /* PCSC_FUNCS */ + +#endif /* PCSC_FUNCS_H */ diff --git a/freebsd/contrib/wpa/src/utils/platform.h b/freebsd/contrib/wpa/src/utils/platform.h new file mode 100644 index 00000000..46cfe785 --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/platform.h @@ -0,0 +1,21 @@ +#ifndef PLATFORM_H +#define PLATFORM_H + +#include "includes.h" +#include "common.h" + +#define le16_to_cpu le_to_host16 +#define le32_to_cpu le_to_host32 + +#define get_unaligned(p) \ +({ \ + struct packed_dummy_struct { \ + typeof(*(p)) __val; \ + } __attribute__((packed)) *__ptr = (void *) (p); \ + \ + __ptr->__val; \ +}) +#define get_unaligned_le16(p) le16_to_cpu(get_unaligned((uint16_t *)(p))) +#define get_unaligned_le32(p) le32_to_cpu(get_unaligned((uint32_t *)(p))) + +#endif /* PLATFORM_H */ diff --git a/freebsd/contrib/wpa/src/utils/state_machine.h b/freebsd/contrib/wpa/src/utils/state_machine.h new file mode 100644 index 00000000..a5143157 --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/state_machine.h @@ -0,0 +1,138 @@ +/* + * wpa_supplicant/hostapd - State machine definitions + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This file includes a set of pre-processor macros that can be used to + * implement a state machine. In addition to including this header file, each + * file implementing a state machine must define STATE_MACHINE_DATA to be the + * data structure including state variables (enum machine_state, + * Boolean changed), and STATE_MACHINE_DEBUG_PREFIX to be a string that is used + * as a prefix for all debug messages. If SM_ENTRY_MA macro is used to define + * a group of state machines with shared data structure, STATE_MACHINE_ADDR + * needs to be defined to point to the MAC address used in debug output. + * SM_ENTRY_M macro can be used to define similar group of state machines + * without this additional debug info. + */ + +#ifndef STATE_MACHINE_H +#define STATE_MACHINE_H + +/** + * SM_STATE - Declaration of a state machine function + * @machine: State machine name + * @state: State machine state + * + * This macro is used to declare a state machine function. It is used in place + * of a C function definition to declare functions to be run when the state is + * entered by calling SM_ENTER or SM_ENTER_GLOBAL. + */ +#define SM_STATE(machine, state) \ +static void sm_ ## machine ## _ ## state ## _Enter(STATE_MACHINE_DATA *sm, \ + int global) + +/** + * SM_ENTRY - State machine function entry point + * @machine: State machine name + * @state: State machine state + * + * This macro is used inside each state machine function declared with + * SM_STATE. SM_ENTRY should be in the beginning of the function body, but + * after declaration of possible local variables. This macro prints debug + * information about state transition and update the state machine state. + */ +#define SM_ENTRY(machine, state) \ +if (!global || sm->machine ## _state != machine ## _ ## state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " #machine \ + " entering state " #state); \ +} \ +sm->machine ## _state = machine ## _ ## state; + +/** + * SM_ENTRY_M - State machine function entry point for state machine group + * @machine: State machine name + * @_state: State machine state + * @data: State variable prefix (full variable: prefix_state) + * + * This macro is like SM_ENTRY, but for state machine groups that use a shared + * data structure for more than one state machine. Both machine and prefix + * parameters are set to "sub-state machine" name. prefix is used to allow more + * than one state variable to be stored in the same data structure. + */ +#define SM_ENTRY_M(machine, _state, data) \ +if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " \ + #machine " entering state " #_state); \ +} \ +sm->data ## _ ## state = machine ## _ ## _state; + +/** + * SM_ENTRY_MA - State machine function entry point for state machine group + * @machine: State machine name + * @_state: State machine state + * @data: State variable prefix (full variable: prefix_state) + * + * This macro is like SM_ENTRY_M, but a MAC address is included in debug + * output. STATE_MACHINE_ADDR has to be defined to point to the MAC address to + * be included in debug. + */ +#define SM_ENTRY_MA(machine, _state, data) \ +if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " MACSTR " " \ + #machine " entering state " #_state, \ + MAC2STR(STATE_MACHINE_ADDR)); \ +} \ +sm->data ## _ ## state = machine ## _ ## _state; + +/** + * SM_ENTER - Enter a new state machine state + * @machine: State machine name + * @state: State machine state + * + * This macro expands to a function call to a state machine function defined + * with SM_STATE macro. SM_ENTER is used in a state machine step function to + * move the state machine to a new state. + */ +#define SM_ENTER(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 0) + +/** + * SM_ENTER_GLOBAL - Enter a new state machine state based on global rule + * @machine: State machine name + * @state: State machine state + * + * This macro is like SM_ENTER, but this is used when entering a new state + * based on a global (not specific to any particular state) rule. A separate + * macro is used to avoid unwanted debug message floods when the same global + * rule is forcing a state machine to remain in on state. + */ +#define SM_ENTER_GLOBAL(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 1) + +/** + * SM_STEP - Declaration of a state machine step function + * @machine: State machine name + * + * This macro is used to declare a state machine step function. It is used in + * place of a C function definition to declare a function that is used to move + * state machine to a new state based on state variables. This function uses + * SM_ENTER and SM_ENTER_GLOBAL macros to enter new state. + */ +#define SM_STEP(machine) \ +static void sm_ ## machine ## _Step(STATE_MACHINE_DATA *sm) + +/** + * SM_STEP_RUN - Call the state machine step function + * @machine: State machine name + * + * This macro expands to a function call to a state machine step function + * defined with SM_STEP macro. + */ +#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) + +#endif /* STATE_MACHINE_H */ diff --git a/freebsd/contrib/wpa/src/utils/trace.h b/freebsd/contrib/wpa/src/utils/trace.h new file mode 100644 index 00000000..43ed86c1 --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/trace.h @@ -0,0 +1,69 @@ +/* + * Backtrace debugging + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TRACE_H +#define TRACE_H + +#define WPA_TRACE_LEN 16 + +#ifdef WPA_TRACE +#include <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); +size_t wpa_trace_calling_func(const char *buf[], size_t len); + +#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/freebsd/contrib/wpa/src/utils/uuid.c b/freebsd/contrib/wpa/src/utils/uuid.c new file mode 100644 index 00000000..a1dedf6e --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/uuid.c @@ -0,0 +1,73 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Universally Unique IDentifier (UUID) + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "uuid.h" + +int uuid_str2bin(const char *str, u8 *bin) +{ + const char *pos; + u8 *opos; + + pos = str; + opos = bin; + + if (hexstr2bin(pos, opos, 4)) + return -1; + pos += 8; + opos += 4; + + if (*pos++ != '-' || hexstr2bin(pos, opos, 2)) + return -1; + pos += 4; + opos += 2; + + if (*pos++ != '-' || hexstr2bin(pos, opos, 2)) + return -1; + pos += 4; + opos += 2; + + if (*pos++ != '-' || hexstr2bin(pos, opos, 2)) + return -1; + pos += 4; + opos += 2; + + if (*pos++ != '-' || hexstr2bin(pos, opos, 6)) + return -1; + + return 0; +} + + +int uuid_bin2str(const u8 *bin, char *str, size_t max_len) +{ + int len; + len = os_snprintf(str, max_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x-%02x%02x%02x%02x%02x%02x", + bin[0], bin[1], bin[2], bin[3], + bin[4], bin[5], bin[6], bin[7], + bin[8], bin[9], bin[10], bin[11], + bin[12], bin[13], bin[14], bin[15]); + if (os_snprintf_error(max_len, 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/freebsd/contrib/wpa/src/utils/uuid.h b/freebsd/contrib/wpa/src/utils/uuid.h new file mode 100644 index 00000000..5e860cbc --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/uuid.h @@ -0,0 +1,18 @@ +/* + * Universally Unique IDentifier (UUID) + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef UUID_H +#define UUID_H + +#define UUID_LEN 16 + +int uuid_str2bin(const char *str, u8 *bin); +int uuid_bin2str(const u8 *bin, char *str, size_t max_len); +int is_nil_uuid(const u8 *uuid); + +#endif /* UUID_H */ diff --git a/freebsd/contrib/wpa/src/utils/wpa_debug.c b/freebsd/contrib/wpa/src/utils/wpa_debug.c new file mode 100644 index 00000000..1b725ad5 --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/wpa_debug.c @@ -0,0 +1,862 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * wpa_supplicant/hostapd / Debug prints + * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" + +#ifdef CONFIG_DEBUG_SYSLOG +#include <syslog.h> + +static int wpa_debug_syslog = 0; +#endif /* CONFIG_DEBUG_SYSLOG */ + +#ifdef CONFIG_DEBUG_LINUX_TRACING +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#include <stdio.h> + +static FILE *wpa_debug_tracing_file = NULL; + +#define WPAS_TRACE_PFX "wpas <%d>: " +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + + +int wpa_debug_level = MSG_INFO; +int wpa_debug_show_keys = 0; +int wpa_debug_timestamp = 0; + + +#ifdef CONFIG_ANDROID_LOG + +#include <android/log.h> + +#ifndef ANDROID_LOG_NAME +#define ANDROID_LOG_NAME "wpa_supplicant" +#endif /* ANDROID_LOG_NAME */ + +static int wpa_to_android_level(int level) +{ + if (level == MSG_ERROR) + return ANDROID_LOG_ERROR; + if (level == MSG_WARNING) + return ANDROID_LOG_WARN; + if (level == MSG_INFO) + return ANDROID_LOG_INFO; + return ANDROID_LOG_DEBUG; +} + +#endif /* CONFIG_ANDROID_LOG */ + +#ifndef CONFIG_NO_STDOUT_DEBUG + +#ifdef CONFIG_DEBUG_FILE +static FILE *out_file = NULL; +#endif /* CONFIG_DEBUG_FILE */ + + +void wpa_debug_print_timestamp(void) +{ +#ifndef CONFIG_ANDROID_LOG + struct os_time tv; + + if (!wpa_debug_timestamp) + return; + + os_get_time(&tv); +#ifdef CONFIG_DEBUG_FILE + if (out_file) { + fprintf(out_file, "%ld.%06u: ", (long) tv.sec, + (unsigned int) tv.usec); + } else +#endif /* CONFIG_DEBUG_FILE */ + printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec); +#endif /* CONFIG_ANDROID_LOG */ +} + + +#ifdef CONFIG_DEBUG_SYSLOG +#ifndef LOG_HOSTAPD +#define LOG_HOSTAPD LOG_DAEMON +#endif /* LOG_HOSTAPD */ + +void wpa_debug_open_syslog(void) +{ + openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD); + wpa_debug_syslog++; +} + + +void wpa_debug_close_syslog(void) +{ + if (wpa_debug_syslog) + closelog(); +} + + +static int syslog_priority(int level) +{ + switch (level) { + case MSG_MSGDUMP: + case MSG_DEBUG: + return LOG_DEBUG; + case MSG_INFO: + return LOG_NOTICE; + case MSG_WARNING: + return LOG_WARNING; + case MSG_ERROR: + return LOG_ERR; + } + return LOG_INFO; +} +#endif /* CONFIG_DEBUG_SYSLOG */ + + +#ifdef CONFIG_DEBUG_LINUX_TRACING + +int wpa_debug_open_linux_tracing(void) +{ + int mounts, trace_fd; + char buf[4096] = {}; + ssize_t buflen; + char *line, *tmp1, *path = NULL; + + mounts = open("/proc/mounts", O_RDONLY); + if (mounts < 0) { + printf("no /proc/mounts\n"); + return -1; + } + + buflen = read(mounts, buf, sizeof(buf) - 1); + close(mounts); + if (buflen < 0) { + printf("failed to read /proc/mounts\n"); + return -1; + } + + line = strtok_r(buf, "\n", &tmp1); + while (line) { + char *tmp2, *tmp_path, *fstype; + /* "<dev> <mountpoint> <fs type> ..." */ + strtok_r(line, " ", &tmp2); + tmp_path = strtok_r(NULL, " ", &tmp2); + fstype = strtok_r(NULL, " ", &tmp2); + if (strcmp(fstype, "debugfs") == 0) { + path = tmp_path; + break; + } + + line = strtok_r(NULL, "\n", &tmp1); + } + + if (path == NULL) { + printf("debugfs mountpoint not found\n"); + return -1; + } + + snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path); + + trace_fd = open(buf, O_WRONLY); + if (trace_fd < 0) { + printf("failed to open trace_marker file\n"); + return -1; + } + wpa_debug_tracing_file = fdopen(trace_fd, "w"); + if (wpa_debug_tracing_file == NULL) { + close(trace_fd); + printf("failed to fdopen()\n"); + return -1; + } + + return 0; +} + + +void wpa_debug_close_linux_tracing(void) +{ + if (wpa_debug_tracing_file == NULL) + return; + fclose(wpa_debug_tracing_file); + wpa_debug_tracing_file = NULL; +} + +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + + +/** + * wpa_printf - conditional printf + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +void wpa_printf(int level, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (level >= wpa_debug_level) { +#ifdef CONFIG_ANDROID_LOG + __android_log_vprint(wpa_to_android_level(level), + ANDROID_LOG_NAME, fmt, ap); +#else /* CONFIG_ANDROID_LOG */ +#ifdef CONFIG_DEBUG_SYSLOG + if (wpa_debug_syslog) { + vsyslog(syslog_priority(level), fmt, ap); + } else { +#endif /* CONFIG_DEBUG_SYSLOG */ + wpa_debug_print_timestamp(); +#ifdef CONFIG_DEBUG_FILE + if (out_file) { + vfprintf(out_file, fmt, ap); + fprintf(out_file, "\n"); + } else { +#endif /* CONFIG_DEBUG_FILE */ + vprintf(fmt, ap); + printf("\n"); +#ifdef CONFIG_DEBUG_FILE + } +#endif /* CONFIG_DEBUG_FILE */ +#ifdef CONFIG_DEBUG_SYSLOG + } +#endif /* CONFIG_DEBUG_SYSLOG */ +#endif /* CONFIG_ANDROID_LOG */ + } + va_end(ap); + +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (wpa_debug_tracing_file != NULL) { + va_start(ap, fmt); + fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level); + vfprintf(wpa_debug_tracing_file, fmt, ap); + fprintf(wpa_debug_tracing_file, "\n"); + fflush(wpa_debug_tracing_file); + va_end(ap); + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ +} + + +static void _wpa_hexdump(int level, const char *title, const u8 *buf, + size_t len, int show) +{ + size_t i; + +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (wpa_debug_tracing_file != NULL) { + fprintf(wpa_debug_tracing_file, + WPAS_TRACE_PFX "%s - hexdump(len=%lu):", + level, title, (unsigned long) len); + if (buf == NULL) { + fprintf(wpa_debug_tracing_file, " [NULL]\n"); + } else if (!show) { + fprintf(wpa_debug_tracing_file, " [REMOVED]\n"); + } else { + for (i = 0; i < len; i++) + fprintf(wpa_debug_tracing_file, + " %02x", buf[i]); + } + fflush(wpa_debug_tracing_file); + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + + if (level < wpa_debug_level) + return; +#ifdef CONFIG_ANDROID_LOG + { + const char *display; + char *strbuf = NULL; + size_t slen = len; + if (buf == NULL) { + display = " [NULL]"; + } else if (len == 0) { + display = ""; + } else if (show && len) { + /* Limit debug message length for Android log */ + if (slen > 32) + slen = 32; + strbuf = os_malloc(1 + 3 * slen); + if (strbuf == NULL) { + wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to " + "allocate message buffer"); + return; + } + + for (i = 0; i < slen; i++) + os_snprintf(&strbuf[i * 3], 4, " %02x", + buf[i]); + + display = strbuf; + } else { + display = " [REMOVED]"; + } + + __android_log_print(wpa_to_android_level(level), + ANDROID_LOG_NAME, + "%s - hexdump(len=%lu):%s%s", + title, (long unsigned int) len, display, + len > slen ? " ..." : ""); + bin_clear_free(strbuf, 1 + 3 * slen); + return; + } +#else /* CONFIG_ANDROID_LOG */ +#ifdef CONFIG_DEBUG_SYSLOG + if (wpa_debug_syslog) { + const char *display; + char *strbuf = NULL; + + if (buf == NULL) { + display = " [NULL]"; + } else if (len == 0) { + display = ""; + } else if (show && len) { + strbuf = os_malloc(1 + 3 * len); + if (strbuf == NULL) { + wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to " + "allocate message buffer"); + return; + } + + for (i = 0; i < len; i++) + os_snprintf(&strbuf[i * 3], 4, " %02x", + buf[i]); + + display = strbuf; + } else { + display = " [REMOVED]"; + } + + syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s", + title, (unsigned long) len, display); + bin_clear_free(strbuf, 1 + 3 * len); + return; + } +#endif /* CONFIG_DEBUG_SYSLOG */ + wpa_debug_print_timestamp(); +#ifdef CONFIG_DEBUG_FILE + if (out_file) { + fprintf(out_file, "%s - hexdump(len=%lu):", + title, (unsigned long) len); + if (buf == NULL) { + fprintf(out_file, " [NULL]"); + } else if (show) { + for (i = 0; i < len; i++) + fprintf(out_file, " %02x", buf[i]); + } else { + fprintf(out_file, " [REMOVED]"); + } + fprintf(out_file, "\n"); + } else { +#endif /* CONFIG_DEBUG_FILE */ + printf("%s - hexdump(len=%lu):", title, (unsigned long) len); + if (buf == NULL) { + printf(" [NULL]"); + } else if (show) { + for (i = 0; i < len; i++) + printf(" %02x", buf[i]); + } else { + printf(" [REMOVED]"); + } + printf("\n"); +#ifdef CONFIG_DEBUG_FILE + } +#endif /* CONFIG_DEBUG_FILE */ +#endif /* CONFIG_ANDROID_LOG */ +} + +void wpa_hexdump(int level, const char *title, const void *buf, size_t len) +{ + _wpa_hexdump(level, title, buf, len, 1); +} + + +void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len) +{ + _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys); +} + + +static void _wpa_hexdump_ascii(int level, const char *title, const void *buf, + size_t len, int show) +{ + size_t i, llen; + const u8 *pos = buf; + const size_t line_len = 16; + +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (wpa_debug_tracing_file != NULL) { + fprintf(wpa_debug_tracing_file, + WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):", + level, title, (unsigned long) len); + if (buf == NULL) { + fprintf(wpa_debug_tracing_file, " [NULL]\n"); + } else if (!show) { + fprintf(wpa_debug_tracing_file, " [REMOVED]\n"); + } else { + /* can do ascii processing in userspace */ + for (i = 0; i < len; i++) + fprintf(wpa_debug_tracing_file, + " %02x", pos[i]); + } + fflush(wpa_debug_tracing_file); + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + + if (level < wpa_debug_level) + return; +#ifdef CONFIG_ANDROID_LOG + _wpa_hexdump(level, title, buf, len, show); +#else /* CONFIG_ANDROID_LOG */ + wpa_debug_print_timestamp(); +#ifdef CONFIG_DEBUG_FILE + if (out_file) { + if (!show) { + fprintf(out_file, + "%s - hexdump_ascii(len=%lu): [REMOVED]\n", + title, (unsigned long) len); + return; + } + if (buf == NULL) { + fprintf(out_file, + "%s - hexdump_ascii(len=%lu): [NULL]\n", + title, (unsigned long) len); + return; + } + fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n", + title, (unsigned long) len); + while (len) { + llen = len > line_len ? line_len : len; + fprintf(out_file, " "); + for (i = 0; i < llen; i++) + fprintf(out_file, " %02x", pos[i]); + for (i = llen; i < line_len; i++) + fprintf(out_file, " "); + fprintf(out_file, " "); + for (i = 0; i < llen; i++) { + if (isprint(pos[i])) + fprintf(out_file, "%c", pos[i]); + else + fprintf(out_file, "_"); + } + for (i = llen; i < line_len; i++) + fprintf(out_file, " "); + fprintf(out_file, "\n"); + pos += llen; + len -= llen; + } + } else { +#endif /* CONFIG_DEBUG_FILE */ + if (!show) { + printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n", + title, (unsigned long) len); + return; + } + if (buf == NULL) { + printf("%s - hexdump_ascii(len=%lu): [NULL]\n", + title, (unsigned long) len); + return; + } + printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len); + while (len) { + llen = len > line_len ? line_len : len; + printf(" "); + for (i = 0; i < llen; i++) + printf(" %02x", pos[i]); + for (i = llen; i < line_len; i++) + printf(" "); + printf(" "); + for (i = 0; i < llen; i++) { + if (isprint(pos[i])) + printf("%c", pos[i]); + else + printf("_"); + } + for (i = llen; i < line_len; i++) + printf(" "); + printf("\n"); + pos += llen; + len -= llen; + } +#ifdef CONFIG_DEBUG_FILE + } +#endif /* CONFIG_DEBUG_FILE */ +#endif /* CONFIG_ANDROID_LOG */ +} + + +void wpa_hexdump_ascii(int level, const char *title, const void *buf, + size_t len) +{ + _wpa_hexdump_ascii(level, title, buf, len, 1); +} + + +void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, + size_t len) +{ + _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); +} + + +#ifdef CONFIG_DEBUG_FILE +static char *last_path = NULL; +#endif /* CONFIG_DEBUG_FILE */ + +int wpa_debug_reopen_file(void) +{ +#ifdef CONFIG_DEBUG_FILE + int rv; + if (last_path) { + char *tmp = os_strdup(last_path); + wpa_debug_close_file(); + rv = wpa_debug_open_file(tmp); + os_free(tmp); + } else { + wpa_printf(MSG_ERROR, "Last-path was not set, cannot " + "re-open log file."); + rv = -1; + } + return rv; +#else /* CONFIG_DEBUG_FILE */ + return 0; +#endif /* CONFIG_DEBUG_FILE */ +} + + +int wpa_debug_open_file(const char *path) +{ +#ifdef CONFIG_DEBUG_FILE + if (!path) + return 0; + + if (last_path == NULL || os_strcmp(last_path, path) != 0) { + /* Save our path to enable re-open */ + os_free(last_path); + last_path = os_strdup(path); + } + + out_file = fopen(path, "a"); + if (out_file == NULL) { + wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open " + "output file, using standard output"); + return -1; + } +#ifndef _WIN32 + setvbuf(out_file, NULL, _IOLBF, 0); +#endif /* _WIN32 */ +#else /* CONFIG_DEBUG_FILE */ + (void)path; +#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 */ +} + + +void wpa_debug_setup_stdout(void) +{ +#ifndef _WIN32 + setvbuf(stdout, NULL, _IOLBF, 0); +#endif /* _WIN32 */ +} + +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +#ifndef CONFIG_NO_WPA_MSG +static wpa_msg_cb_func wpa_msg_cb = NULL; + +void wpa_msg_register_cb(wpa_msg_cb_func func) +{ + wpa_msg_cb = func; +} + + +static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL; + +void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func) +{ + wpa_msg_ifname_cb = func; +} + + +void wpa_msg(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + int len; + char prefix[130]; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + 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 (os_snprintf_error(sizeof(prefix), res)) + 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, WPA_MSG_PER_INTERFACE, buf, len); + bin_clear_free(buf, buflen); +} + + +void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + int len; + + if (!wpa_msg_cb) + return; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + 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, WPA_MSG_PER_INTERFACE, buf, len); + bin_clear_free(buf, buflen); +} + + +void wpa_msg_global(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + int len; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "wpa_msg_global: Failed to allocate " + "message buffer"); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + if (wpa_msg_cb) + wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len); + bin_clear_free(buf, buflen); +} + + +void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + int len; + + if (!wpa_msg_cb) + return; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, + "wpa_msg_global_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, WPA_MSG_GLOBAL, buf, len); + bin_clear_free(buf, buflen); +} + + +void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + int len; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "wpa_msg_no_global: Failed to allocate " + "message buffer"); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + if (wpa_msg_cb) + wpa_msg_cb(ctx, level, WPA_MSG_NO_GLOBAL, buf, len); + bin_clear_free(buf, buflen); +} + + +void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + int len; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate message buffer", + __func__); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_printf(level, "%s", buf); + if (wpa_msg_cb) + wpa_msg_cb(ctx, level, WPA_MSG_ONLY_GLOBAL, 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; + int buflen; + int len; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + 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); + bin_clear_free(buf, buflen); +} +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ + + +const char * debug_level_str(int level) +{ + switch (level) { + case MSG_EXCESSIVE: + return "EXCESSIVE"; + case MSG_MSGDUMP: + return "MSGDUMP"; + case MSG_DEBUG: + return "DEBUG"; + case MSG_INFO: + return "INFO"; + case MSG_WARNING: + return "WARNING"; + case MSG_ERROR: + return "ERROR"; + default: + return "?"; + } +} + + +int str_to_debug_level(const char *s) +{ + if (os_strcasecmp(s, "EXCESSIVE") == 0) + return MSG_EXCESSIVE; + if (os_strcasecmp(s, "MSGDUMP") == 0) + return MSG_MSGDUMP; + if (os_strcasecmp(s, "DEBUG") == 0) + return MSG_DEBUG; + if (os_strcasecmp(s, "INFO") == 0) + return MSG_INFO; + if (os_strcasecmp(s, "WARNING") == 0) + return MSG_WARNING; + if (os_strcasecmp(s, "ERROR") == 0) + return MSG_ERROR; + return -1; +} diff --git a/freebsd/contrib/wpa/src/utils/wpa_debug.h b/freebsd/contrib/wpa/src/utils/wpa_debug.h new file mode 100644 index 00000000..17d8f963 --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/wpa_debug.h @@ -0,0 +1,370 @@ +/* + * wpa_supplicant/hostapd / Debug prints + * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_DEBUG_H +#define WPA_DEBUG_H + +#include "wpabuf.h" + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; +extern int wpa_debug_timestamp; + +/* Debugging function - conditional printf and hex dump. Driver wrappers can + * use these for debugging purposes. */ + +enum { + MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR +}; + +#ifdef CONFIG_NO_STDOUT_DEBUG + +#define wpa_debug_print_timestamp() do { } while (0) +#define wpa_printf(args...) do { } while (0) +#define wpa_hexdump(l,t,b,le) do { } while (0) +#define wpa_hexdump_buf(l,t,b) do { } while (0) +#define wpa_hexdump_key(l,t,b,le) do { } while (0) +#define wpa_hexdump_buf_key(l,t,b) do { } while (0) +#define wpa_hexdump_ascii(l,t,b,le) do { } while (0) +#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0) +#define wpa_debug_open_file(p) do { } while (0) +#define wpa_debug_close_file() do { } while (0) +#define wpa_debug_setup_stdout() 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); +void wpa_debug_setup_stdout(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 void *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 void *buf, size_t len); + +static inline void wpa_hexdump_buf_key(int level, const char *title, + const struct wpabuf *buf) +{ + wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : NULL, + buf ? wpabuf_len(buf) : 0); +} + +/** + * wpa_hexdump_ascii - conditional hex dump + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump with both + * the hex numbers and ASCII characters (for printable range) are shown. 16 + * bytes per line will be shown. + */ +void wpa_hexdump_ascii(int level, const char *title, const void *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 void *buf, + size_t len); + +/* + * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce + * binary size. As such, it should be used with debugging messages that are not + * needed in the control interface while wpa_msg() has to be used for anything + * that needs to shown to control interface monitors. + */ +#define wpa_dbg(args...) wpa_msg(args) + +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +#ifdef CONFIG_NO_WPA_MSG +#define wpa_msg(args...) do { } while (0) +#define wpa_msg_ctrl(args...) do { } while (0) +#define wpa_msg_global(args...) do { } while (0) +#define wpa_msg_global_ctrl(args...) do { } while (0) +#define wpa_msg_no_global(args...) do { } while (0) +#define wpa_msg_global_only(args...) do { } while (0) +#define wpa_msg_register_cb(f) do { } while (0) +#define wpa_msg_register_ifname_cb(f) do { } while (0) +#else /* CONFIG_NO_WPA_MSG */ +/** + * wpa_msg - Conditional printf for default target and ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. This function is like wpa_printf(), but it also sends the + * same message to all attached ctrl_iface monitors. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); + +/** + * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg(), but it sends the output only to the + * attached ctrl_iface monitors. In other words, it can be used for frequent + * events that do not need to be sent to syslog. + */ +void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +/** + * wpa_msg_global - Global printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg(), but it sends the output as a global event, + * i.e., without being specific to an interface. For backwards compatibility, + * an old style event is also delivered on one of the interfaces (the one + * specified by the context data). + */ +void wpa_msg_global(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +/** + * wpa_msg_global_ctrl - Conditional global printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg_global(), but it sends the output only to the + * attached global 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_global_ctrl(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +/** + * wpa_msg_no_global - Conditional printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg(), but it does not send the output as a global + * event. + */ +void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +/** + * wpa_msg_global_only - 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_global(), but it sends the output only as a + * global event. + */ +void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +enum wpa_msg_type { + WPA_MSG_PER_INTERFACE, + WPA_MSG_GLOBAL, + WPA_MSG_NO_GLOBAL, + WPA_MSG_ONLY_GLOBAL, +}; + +typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type, + const char *txt, size_t len); + +/** + * wpa_msg_register_cb - Register callback function for wpa_msg() messages + * @func: Callback function (%NULL to unregister) + */ +void wpa_msg_register_cb(wpa_msg_cb_func func); + +typedef const char * (*wpa_msg_get_ifname_func)(void *ctx); +void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func); + +#endif /* CONFIG_NO_WPA_MSG */ + +#ifdef CONFIG_NO_HOSTAPD_LOGGER +#define hostapd_logger(args...) do { } while (0) +#define hostapd_logger_register_cb(f) do { } while (0) +#else /* CONFIG_NO_HOSTAPD_LOGGER */ +void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, + const char *fmt, ...) PRINTF_FORMAT(5, 6); + +typedef void (*hostapd_logger_cb_func)(void *ctx, const u8 *addr, + unsigned int module, int level, + const char *txt, size_t len); + +/** + * hostapd_logger_register_cb - Register callback function for hostapd_logger() + * @func: Callback function (%NULL to unregister) + */ +void hostapd_logger_register_cb(hostapd_logger_cb_func func); +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ + +#define HOSTAPD_MODULE_IEEE80211 0x00000001 +#define HOSTAPD_MODULE_IEEE8021X 0x00000002 +#define HOSTAPD_MODULE_RADIUS 0x00000004 +#define HOSTAPD_MODULE_WPA 0x00000008 +#define HOSTAPD_MODULE_DRIVER 0x00000010 +#define HOSTAPD_MODULE_IAPP 0x00000020 +#define HOSTAPD_MODULE_MLME 0x00000040 + +enum hostapd_logger_level { + HOSTAPD_LEVEL_DEBUG_VERBOSE = 0, + HOSTAPD_LEVEL_DEBUG = 1, + HOSTAPD_LEVEL_INFO = 2, + HOSTAPD_LEVEL_NOTICE = 3, + HOSTAPD_LEVEL_WARNING = 4 +}; + + +#ifdef CONFIG_DEBUG_SYSLOG + +void wpa_debug_open_syslog(void); +void wpa_debug_close_syslog(void); + +#else /* CONFIG_DEBUG_SYSLOG */ + +static inline void wpa_debug_open_syslog(void) +{ +} + +static inline void wpa_debug_close_syslog(void) +{ +} + +#endif /* CONFIG_DEBUG_SYSLOG */ + +#ifdef CONFIG_DEBUG_LINUX_TRACING + +int wpa_debug_open_linux_tracing(void); +void wpa_debug_close_linux_tracing(void); + +#else /* CONFIG_DEBUG_LINUX_TRACING */ + +static inline int wpa_debug_open_linux_tracing(void) +{ + return 0; +} + +static inline void wpa_debug_close_linux_tracing(void) +{ +} + +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + + +#ifdef EAPOL_TEST +#define WPA_ASSERT(a) \ + do { \ + if (!(a)) { \ + printf("WPA_ASSERT FAILED '" #a "' " \ + "%s %s:%d\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + exit(1); \ + } \ + } while (0) +#else +#define WPA_ASSERT(a) do { } while (0) +#endif + +const char * debug_level_str(int level); +int str_to_debug_level(const char *s); + +#endif /* WPA_DEBUG_H */ diff --git a/freebsd/contrib/wpa/src/utils/wpabuf.c b/freebsd/contrib/wpa/src/utils/wpabuf.c new file mode 100644 index 00000000..c6e22aeb --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/wpabuf.c @@ -0,0 +1,314 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Dynamic data buffer + * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "trace.h" +#include "wpabuf.h" + +#ifdef WPA_TRACE +#define WPABUF_MAGIC 0x51a974e3 + +struct wpabuf_trace { + unsigned int magic; +} __attribute__((aligned(8))); + +static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf) +{ + return (struct wpabuf_trace *) + ((const u8 *) buf - sizeof(struct wpabuf_trace)); +} +#endif /* WPA_TRACE */ + + +static void wpabuf_overflow(const struct wpabuf *buf, size_t len) +{ +#ifdef WPA_TRACE + struct wpabuf_trace *trace = wpabuf_get_trace(buf); + if (trace->magic != WPABUF_MAGIC) { + wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x", + trace->magic); + } +#endif /* WPA_TRACE */ + wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu", + buf, (unsigned long) buf->size, (unsigned long) buf->used, + (unsigned long) len); + wpa_trace_show("wpabuf overflow"); + abort(); +} + + +int wpabuf_resize(struct wpabuf **_buf, size_t add_len) +{ + struct wpabuf *buf = *_buf; +#ifdef WPA_TRACE + struct wpabuf_trace *trace; +#endif /* WPA_TRACE */ + + if (buf == NULL) { + *_buf = wpabuf_alloc(add_len); + return *_buf == NULL ? -1 : 0; + } + +#ifdef WPA_TRACE + trace = wpabuf_get_trace(buf); + if (trace->magic != WPABUF_MAGIC) { + wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x", + trace->magic); + wpa_trace_show("wpabuf_resize invalid magic"); + abort(); + } +#endif /* WPA_TRACE */ + + if (buf->used + add_len > buf->size) { + unsigned char *nbuf; + if (buf->flags & WPABUF_FLAG_EXT_DATA) { + nbuf = os_realloc(buf->buf, buf->used + add_len); + if (nbuf == NULL) + return -1; + os_memset(nbuf + buf->used, 0, add_len); + buf->buf = nbuf; + } else { +#ifdef WPA_TRACE + nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf) + + buf->used + add_len); + if (nbuf == NULL) + return -1; + trace = (struct wpabuf_trace *) nbuf; + buf = (struct wpabuf *) (trace + 1); + os_memset(nbuf + sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf) + buf->used, 0, + add_len); +#else /* WPA_TRACE */ + nbuf = os_realloc(buf, sizeof(struct wpabuf) + + buf->used + add_len); + if (nbuf == NULL) + return -1; + buf = (struct wpabuf *) nbuf; + os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0, + add_len); +#endif /* WPA_TRACE */ + buf->buf = (u8 *) (buf + 1); + *_buf = buf; + } + buf->size = buf->used + add_len; + } + + return 0; +} + + +/** + * wpabuf_alloc - Allocate a wpabuf of the given size + * @len: Length for the allocated buffer + * Returns: Buffer to the allocated wpabuf or %NULL on failure + */ +struct wpabuf * wpabuf_alloc(size_t len) +{ +#ifdef WPA_TRACE + struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf) + len); + struct wpabuf *buf; + if (trace == NULL) + return NULL; + trace->magic = WPABUF_MAGIC; + buf = (struct wpabuf *) (trace + 1); +#else /* WPA_TRACE */ + struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf) + len); + if (buf == NULL) + return NULL; +#endif /* WPA_TRACE */ + + buf->size = len; + buf->buf = (u8 *) (buf + 1); + return buf; +} + + +struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len) +{ +#ifdef WPA_TRACE + struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf)); + struct wpabuf *buf; + if (trace == NULL) + return NULL; + trace->magic = WPABUF_MAGIC; + buf = (struct wpabuf *) (trace + 1); +#else /* WPA_TRACE */ + struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf)); + if (buf == NULL) + return NULL; +#endif /* WPA_TRACE */ + + buf->size = len; + buf->used = len; + buf->buf = data; + buf->flags |= WPABUF_FLAG_EXT_DATA; + + return buf; +} + + +struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len) +{ + struct wpabuf *buf = wpabuf_alloc(len); + if (buf) + wpabuf_put_data(buf, data, len); + return buf; +} + + +struct wpabuf * wpabuf_dup(const struct wpabuf *src) +{ + struct wpabuf *buf = wpabuf_alloc(wpabuf_len(src)); + if (buf) + wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src)); + return buf; +} + + +/** + * wpabuf_free - Free a wpabuf + * @buf: wpabuf buffer + */ +void wpabuf_free(struct wpabuf *buf) +{ +#ifdef WPA_TRACE + struct wpabuf_trace *trace; + if (buf == NULL) + return; + trace = wpabuf_get_trace(buf); + if (trace->magic != WPABUF_MAGIC) { + wpa_printf(MSG_ERROR, "wpabuf_free: invalid magic %x", + trace->magic); + wpa_trace_show("wpabuf_free magic mismatch"); + abort(); + } + if (buf->flags & WPABUF_FLAG_EXT_DATA) + os_free(buf->buf); + os_free(trace); +#else /* WPA_TRACE */ + if (buf == NULL) + return; + if (buf->flags & WPABUF_FLAG_EXT_DATA) + os_free(buf->buf); + os_free(buf); +#endif /* WPA_TRACE */ +} + + +void wpabuf_clear_free(struct wpabuf *buf) +{ + if (buf) { + os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf)); + wpabuf_free(buf); + } +} + + +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/freebsd/contrib/wpa/src/utils/wpabuf.h b/freebsd/contrib/wpa/src/utils/wpabuf.h new file mode 100644 index 00000000..c3ef1bae --- /dev/null +++ b/freebsd/contrib/wpa/src/utils/wpabuf.h @@ -0,0 +1,163 @@ +/* + * Dynamic data buffer + * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPABUF_H +#define WPABUF_H + +/* wpabuf::buf is a pointer to external data */ +#define WPABUF_FLAG_EXT_DATA BIT(0) + +/* + * Internal data structure for wpabuf. Please do not touch this directly from + * elsewhere. This is only defined in header file to allow inline functions + * from this file to access data. + */ +struct wpabuf { + size_t size; /* total size of the allocated buffer */ + size_t used; /* length of data in the buffer */ + u8 *buf; /* pointer to the head of the buffer */ + unsigned int flags; + /* optionally followed by the allocated buffer */ +}; + + +int wpabuf_resize(struct wpabuf **buf, size_t add_len); +struct wpabuf * wpabuf_alloc(size_t len); +struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len); +struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len); +struct wpabuf * wpabuf_dup(const struct wpabuf *src); +void wpabuf_free(struct wpabuf *buf); +void wpabuf_clear_free(struct wpabuf *buf); +void * wpabuf_put(struct wpabuf *buf, size_t len); +struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b); +struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len); +void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3); + + +/** + * wpabuf_size - Get the currently allocated size of a wpabuf buffer + * @buf: wpabuf buffer + * Returns: Currently allocated size of the buffer + */ +static inline size_t wpabuf_size(const struct wpabuf *buf) +{ + return buf->size; +} + +/** + * wpabuf_len - Get the current length of a wpabuf buffer data + * @buf: wpabuf buffer + * Returns: Currently used length of the buffer + */ +static inline size_t wpabuf_len(const struct wpabuf *buf) +{ + return buf->used; +} + +/** + * wpabuf_tailroom - Get size of available tail room in the end of the buffer + * @buf: wpabuf buffer + * Returns: Tail room (in bytes) of available space in the end of the buffer + */ +static inline size_t wpabuf_tailroom(const struct wpabuf *buf) +{ + return buf->size - buf->used; +} + +/** + * wpabuf_head - Get pointer to the head of the buffer data + * @buf: wpabuf buffer + * Returns: Pointer to the head of the buffer data + */ +static inline const void * wpabuf_head(const struct wpabuf *buf) +{ + return buf->buf; +} + +static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) +{ + return wpabuf_head(buf); +} + +/** + * wpabuf_mhead - Get modifiable pointer to the head of the buffer data + * @buf: wpabuf buffer + * Returns: Pointer to the head of the buffer data + */ +static inline void * wpabuf_mhead(struct wpabuf *buf) +{ + return buf->buf; +} + +static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf) +{ + return wpabuf_mhead(buf); +} + +static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data) +{ + u8 *pos = wpabuf_put(buf, 1); + *pos = data; +} + +static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data) +{ + u8 *pos = wpabuf_put(buf, 2); + WPA_PUT_LE16(pos, data); +} + +static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 4); + WPA_PUT_LE32(pos, data); +} + +static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data) +{ + u8 *pos = wpabuf_put(buf, 2); + WPA_PUT_BE16(pos, data); +} + +static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 3); + WPA_PUT_BE24(pos, data); +} + +static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 4); + WPA_PUT_BE32(pos, data); +} + +static inline void wpabuf_put_data(struct wpabuf *buf, const void *data, + size_t len) +{ + if (data) + os_memcpy(wpabuf_put(buf, len), data, len); +} + +static inline void wpabuf_put_buf(struct wpabuf *dst, + const struct wpabuf *src) +{ + wpabuf_put_data(dst, wpabuf_head(src), wpabuf_len(src)); +} + +static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len) +{ + buf->buf = (u8 *) data; + buf->flags = WPABUF_FLAG_EXT_DATA; + buf->size = buf->used = len; +} + +static inline void wpabuf_put_str(struct wpabuf *dst, const char *str) +{ + wpabuf_put_data(dst, str, os_strlen(str)); +} + +#endif /* WPABUF_H */ diff --git a/freebsd/contrib/wpa/src/wps/http.h b/freebsd/contrib/wpa/src/wps/http.h new file mode 100644 index 00000000..2fee3a8f --- /dev/null +++ b/freebsd/contrib/wpa/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/freebsd/contrib/wpa/src/wps/http_client.c b/freebsd/contrib/wpa/src/wps/http_client.c new file mode 100644 index 00000000..c4f26dc9 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/http_client.c @@ -0,0 +1,364 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * http_client - HTTP client + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README 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; + size_t send_len; + + send_len = wpabuf_len(c->req) - c->req_pos; + 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) send_len); + + res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, send_len, 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 < send_len) { + wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes " + "remaining", + res, (unsigned long) wpabuf_len(c->req), + (unsigned long) send_len - 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) + goto fail; + + if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) { + wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s", + strerror(errno)); + goto fail; + } + + if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) { + if (errno != EINPROGRESS) { + wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s", + strerror(errno)); + goto fail; + } + + /* + * 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) || + eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0, + http_client_timeout, c, NULL)) + goto fail; + + c->req = req; + + return c; + +fail: + http_client_free(c); + return NULL; +} + + +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/freebsd/contrib/wpa/src/wps/http_client.h b/freebsd/contrib/wpa/src/wps/http_client.h new file mode 100644 index 00000000..ddee2adb --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/http_client.h @@ -0,0 +1,40 @@ +/* + * http_client - HTTP client + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HTTP_CLIENT_H +#define HTTP_CLIENT_H + +struct http_client; + +enum http_client_event { + HTTP_CLIENT_FAILED, + HTTP_CLIENT_TIMEOUT, + HTTP_CLIENT_OK, + HTTP_CLIENT_INVALID_REPLY, +}; + +char * http_client_url_parse(const char *url, struct sockaddr_in *dst, + char **path); +struct http_client * http_client_addr(struct sockaddr_in *dst, + struct wpabuf *req, size_t max_response, + void (*cb)(void *ctx, + struct http_client *c, + enum http_client_event event), + void *cb_ctx); +struct http_client * http_client_url(const char *url, + struct wpabuf *req, size_t max_response, + void (*cb)(void *ctx, + struct http_client *c, + enum http_client_event event), + void *cb_ctx); +void http_client_free(struct http_client *c); +struct wpabuf * http_client_get_body(struct http_client *c); +char * http_client_get_hdr_line(struct http_client *c, const char *tag); +char * http_link_update(char *url, const char *base); + +#endif /* HTTP_CLIENT_H */ diff --git a/freebsd/contrib/wpa/src/wps/http_server.c b/freebsd/contrib/wpa/src/wps/http_server.c new file mode 100644 index 00000000..edef1b2c --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/http_server.c @@ -0,0 +1,316 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * http_server - HTTP server + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README 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; + int on = 1; + + srv = os_zalloc(sizeof(*srv)); + if (srv == NULL) + return NULL; + srv->cb = cb; + srv->cb_ctx = cb_ctx; + + srv->fd = socket(AF_INET, SOCK_STREAM, 0); + if (srv->fd < 0) + goto fail; + + if (setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) + { + wpa_printf(MSG_DEBUG, + "HTTP: setsockopt(SO_REUSEADDR) failed: %s", + strerror(errno)); + /* try to continue anyway */ + } + + 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 || + fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0 || + 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/freebsd/contrib/wpa/src/wps/http_server.h b/freebsd/contrib/wpa/src/wps/http_server.h new file mode 100644 index 00000000..4b2b749f --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/http_server.h @@ -0,0 +1,33 @@ +/* + * http_server - HTTP server + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HTTP_SERVER_H +#define HTTP_SERVER_H + +struct http_server; +struct http_request; + +void http_request_deinit(struct http_request *req); +void http_request_send(struct http_request *req, struct wpabuf *resp); +void http_request_send_and_deinit(struct http_request *req, + struct wpabuf *resp); +enum httpread_hdr_type http_request_get_type(struct http_request *req); +char * http_request_get_uri(struct http_request *req); +char * http_request_get_hdr(struct http_request *req); +char * http_request_get_data(struct http_request *req); +char * http_request_get_hdr_line(struct http_request *req, const char *tag); +struct sockaddr_in * http_request_get_cli_addr(struct http_request *req); + +struct http_server * http_server_init(struct in_addr *addr, int port, + void (*cb)(void *ctx, + struct http_request *req), + void *cb_ctx); +void http_server_deinit(struct http_server *srv); +int http_server_get_port(struct http_server *srv); + +#endif /* HTTP_SERVER_H */ diff --git a/freebsd/contrib/wpa/src/wps/httpread.c b/freebsd/contrib/wpa/src/wps/httpread.c new file mode 100644 index 00000000..74a1a4f5 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/httpread.c @@ -0,0 +1,850 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * httpread - Manage reading file(s) from HTTP/TCP socket + * Author: Ted Merrill + * Copyright 2008 Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * The files are buffered via internal callbacks from eloop, then presented to + * an application callback routine when completely read into memory. May also + * be used if no file is expected but just to get the header, including HTTP + * replies (e.g. HTTP/1.1 200 OK etc.). + * + * This does not attempt to be an optimally efficient implementation, but does + * attempt to be of reasonably small size and memory consumption; assuming that + * only small files are to be read. A maximum file size is provided by + * application and enforced. + * + * It is assumed that the application does not expect any of the following: + * -- transfer encoding other than chunked + * -- trailer fields + * It is assumed that, even if the other side requested that the connection be + * kept open, that we will close it (thus HTTP messages sent by application + * should have the connection closed field); this is allowed by HTTP/1.1 and + * simplifies things for us. + * + * Other limitations: + * -- HTTP header may not exceed a hard-coded size. + * + * Notes: + * This code would be massively simpler without some of the new features of + * HTTP/1.1, especially chunked data. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "httpread.h" + + +/* Tunable parameters */ +#define HTTPREAD_READBUF_SIZE 1024 /* read in chunks of this size */ +#define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */ +#define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */ + + +/* 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 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? */ +} + + +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) +{ + wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h); + if (!h) + return; + + eloop_cancel_timeout(httpread_timeout_handler, NULL, h); + eloop_unregister_sock(h->sd, EVENT_TYPE_READ); + os_free(h->body); + os_free(h->uri); + os_memset(h, 0, sizeof(*h)); /* aid debugging */ + 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->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); + if (h->content_length < 0 || h->content_length > h->max_bytes) { + wpa_printf(MSG_DEBUG, + "httpread: Unacceptable Content-Length %d", + h->content_length); + return -1; + } + 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++ = hex2byte(rawuri + 1); + rawuri += 3; + } else { + *uri++ = c; + rawuri++; + } + } + *uri = 0; /* null terminate */ + 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 */ + + /* read some at a time, then search for the interal + * boundaries between header and data and etc. + */ + wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h); + nread = read(h->sd, readbuf, sizeof(readbuf)); + if (nread < 0) { + wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno)); + goto bad; + } + wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread); + 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 + */ + 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) { + wpa_printf(MSG_DEBUG, + "httpread: Too long header"); + 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) { + wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)", + h); + goto got_file; + } + if (h->got_content_length && h->content_length == 0) { + 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) { + 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) { + wpa_printf(MSG_DEBUG, + "httpread: body_nbytes=%d >= max_bytes=%d", + 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_alloc_nbytes < h->body_alloc_nbytes || + new_alloc_nbytes > h->max_bytes + + HTTPREAD_BODYBUF_DELTA) { + wpa_printf(MSG_DEBUG, + "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)", + new_alloc_nbytes, + h->body_alloc_nbytes, + h->max_bytes); + goto bad; + } + if ((new_body = os_realloc(h->body, new_alloc_nbytes)) + == NULL) { + wpa_printf(MSG_DEBUG, + "httpread: Failed to reallocate buffer (len=%d)", + new_alloc_nbytes); + 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)) { + wpa_printf(MSG_DEBUG, + "httpread: Unexpected chunk header value (not a hex digit)"); + goto bad; + } + h->chunk_size = strtoul(cbp, NULL, 16); + if (h->chunk_size < 0 || + h->chunk_size > h->max_bytes) { + wpa_printf(MSG_DEBUG, + "httpread: Invalid chunk size %d", + h->chunk_size); + goto bad; + } + /* 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; + 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 { + wpa_printf(MSG_DEBUG, + "httpread: Invalid chunk end"); + 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; + 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 < 0) { + wpa_printf(MSG_DEBUG, + "httpread: Invalid ncopy=%d", ncopy); + goto bad; + } + 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. + */ + 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; + 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: + wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h); + return; + +got_file: + wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d", + h->body_nbytes, h->hdr_type); + wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body", + h->body, h->body_nbytes); + /* 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. + */ + eloop_unregister_sock(h->sd, EVENT_TYPE_READ); + /* The application can destroy us whenever they feel like... + * cancel timeout. + */ + eloop_cancel_timeout(httpread_timeout_handler, NULL, h); + (*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 && + eloop_register_timeout(timeout_seconds, 0, + httpread_timeout_handler, NULL, h)) { + /* No way to recover (from malloc failure) */ + goto fail; + } + if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler, + NULL, h)) { + /* No way to recover (from malloc failure) */ + goto fail; + } + 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/freebsd/contrib/wpa/src/wps/httpread.h b/freebsd/contrib/wpa/src/wps/httpread.h new file mode 100644 index 00000000..454b618b --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/httpread.h @@ -0,0 +1,117 @@ +/* + * httpread - Manage reading file(s) from HTTP/TCP socket + * Author: Ted Merrill + * Copyright 2008 Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HTTPREAD_H +#define HTTPREAD_H + +/* event types (passed to callback) */ +enum httpread_event { + HTTPREAD_EVENT_FILE_READY = 1, /* including reply ready */ + HTTPREAD_EVENT_TIMEOUT = 2, + HTTPREAD_EVENT_ERROR = 3 /* misc. error, esp malloc error */ +}; + + +/* header type detected + * available to callback via call to httpread_reply_code_get() + */ +enum httpread_hdr_type { + HTTPREAD_HDR_TYPE_UNKNOWN = 0, /* none of the following */ + HTTPREAD_HDR_TYPE_REPLY = 1, /* hdr begins w/ HTTP/ */ + HTTPREAD_HDR_TYPE_GET = 2, /* hdr begins with GET<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/freebsd/contrib/wpa/src/wps/upnp_xml.c b/freebsd/contrib/wpa/src/wps/upnp_xml.c new file mode 100644 index 00000000..af583ff6 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/upnp_xml.c @@ -0,0 +1,254 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * 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., & for ampersand(&) and < for left angle bracket (<). + * Five such escapes total (more can be defined but that does not apply to our + * case). Thus we can safely parse for angle brackets etc. + * + * XML describes tree structures of tagged data, with each element beginning + * with an opening tag <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 < and > so they will not trouble us. + */ +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, "<"); + continue; + } + if (c == '>') { + wpabuf_put_str(buf, ">"); + continue; + } + if (c == '&') { + wpabuf_put_str(buf, "&"); + continue; + } + if (c == '\'') { + wpabuf_put_str(buf, "'"); + continue; + } + if (c == '"') { + wpabuf_put_str(buf, """); + 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/freebsd/contrib/wpa/src/wps/upnp_xml.h b/freebsd/contrib/wpa/src/wps/upnp_xml.h new file mode 100644 index 00000000..616af3da --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/upnp_xml.h @@ -0,0 +1,25 @@ +/* + * 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); +int xml_next_tag(const char *in, const char **out, + const char **out_tagname, const char **end); +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/freebsd/contrib/wpa/src/wps/wps.c b/freebsd/contrib/wpa/src/wps/wps.c new file mode 100644 index 00000000..fb6d8f65 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps.c @@ -0,0 +1,664 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Wi-Fi Protected Setup + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/dh_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; +int wps_corrupt_pkhash = 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 = cfg->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; + wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password", + data->dev_password, data->dev_password_len); + } + +#ifdef CONFIG_WPS_NFC + if (cfg->pin == NULL && + cfg->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) + data->dev_pw_id = cfg->dev_pw_id; + + if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) { + /* Keep AP PIN as alternative Device Password */ + data->alt_dev_pw_id = data->dev_pw_id; + data->alt_dev_password = data->dev_password; + data->alt_dev_password_len = data->dev_password_len; + + data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id; + data->dev_password = + os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw)); + if (data->dev_password == NULL) { + os_free(data); + return NULL; + } + os_memcpy(data->dev_password, + wpabuf_head(cfg->wps->ap_nfc_dev_pw), + wpabuf_len(cfg->wps->ap_nfc_dev_pw)); + data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw); + wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password", + data->dev_password, data->dev_password_len); + } +#endif /* CONFIG_WPS_NFC */ + + data->pbc = cfg->pbc; + if (cfg->pbc) { + /* Use special PIN '00000000' for PBC */ + data->dev_pw_id = DEV_PW_PUSHBUTTON; + bin_clear_free(data->dev_password, data->dev_password_len); + data->dev_password = (u8 *) os_strdup("00000000"); + if (data->dev_password == NULL) { + os_free(data); + return NULL; + } + 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) { + bin_clear_free(data->dev_password, + data->dev_password_len); + 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; + data->pbc_in_m1 = cfg->pbc_in_m1; + + if (cfg->peer_pubkey_hash) { + os_memcpy(data->peer_pubkey_hash, cfg->peer_pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN); + data->peer_pubkey_hash_set = 1; + } + + return data; +} + + +/** + * wps_deinit - Deinitialize WPS Registration protocol data + * @data: WPS Registration protocol data from wps_init() + */ +void wps_deinit(struct wps_data *data) +{ +#ifdef CONFIG_WPS_NFC + if (data->registrar && data->nfc_pw_token) + wps_registrar_remove_nfc_pw_token(data->wps->registrar, + data->nfc_pw_token); +#endif /* CONFIG_WPS_NFC */ + + 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); + bin_clear_free(data->dev_password, data->dev_password_len); + bin_clear_free(data->alt_dev_password, data->alt_dev_password_len); + bin_clear_free(data->new_psk, data->new_psk_len); + wps_device_data_free(&data->peer_dev); + bin_clear_free(data->new_ap_settings, sizeof(*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: 2 if the specified address is explicit authorized, 1 if address is + * authorized (broadcast), 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) + return 2; + if (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; + int sel_a, sel_b; + + if (wps_a == NULL || wps_parse_msg(wps_a, &attr) < 0) + return 1; + sel_a = attr.selected_registrar && *attr.selected_registrar != 0; + + if (wps_b == NULL || wps_parse_msg(wps_b, &attr) < 0) + return -1; + sel_b = attr.selected_registrar && *attr.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_is_20 - Check whether WPS attributes claim support for WPS 2.0 + */ +int wps_is_20(const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + if (msg == NULL || wps_parse_msg(msg, &attr) < 0) + return 0; + return attr.version2 != NULL; +} + + +/** + * 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 + * @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for + * most other use cases) + * @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(u16 pw_id, 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; + + wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request"); + + ie = wpabuf_alloc(500); + if (ie == NULL) + return NULL; + + if (wps_build_version(ie) || + wps_build_req_type(ie, req_type) || + wps_build_config_methods(ie, dev->config_methods) || + wps_build_uuid_e(ie, uuid) || + wps_build_primary_dev_type(dev, ie) || + wps_build_rf_bands(dev, ie, 0) || + wps_build_assoc_state(NULL, ie) || + wps_build_config_error(ie, WPS_CFG_NO_ERROR) || + wps_build_dev_password_id(ie, pw_id) || + 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) || + 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; + } + + 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 (os_snprintf_error(end - pos, ret)) + 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 (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + if (attr.selected_registrar && *attr.selected_registrar) { + ret = os_snprintf(pos, end - pos, + "wps_selected_registrar=1\n"); + if (os_snprintf_error(end - pos, ret)) + 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 (os_snprintf_error(end - pos, ret)) + 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 (os_snprintf_error(end - pos, ret)) + 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 (os_snprintf_error(end - pos, ret)) + 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] == 0 || + is_ctrl_char(attr.dev_name[i])) + 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 (os_snprintf_error(end - pos, ret)) + 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 (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +const char * wps_ei_str(enum wps_error_indication ei) +{ + switch (ei) { + case WPS_EI_NO_ERROR: + return "No Error"; + case WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED: + return "TKIP Only Prohibited"; + case WPS_EI_SECURITY_WEP_PROHIBITED: + return "WEP Prohibited"; + case WPS_EI_AUTH_FAILURE: + return "Authentication Failure"; + default: + return "Unknown"; + } +} diff --git a/freebsd/contrib/wpa/src/wps/wps.h b/freebsd/contrib/wpa/src/wps/wps.h new file mode 100644 index 00000000..2c91d167 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps.h @@ -0,0 +1,1041 @@ +/* + * Wi-Fi Protected Setup + * Copyright (c) 2007-2013, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPS_H +#define WPS_H + +#include "common/ieee802_11_defs.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_parse_attr; + +/** + * 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[SSID_MAX_LEN]; + 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, WPS_RF_60GHZ 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; + u16 config_methods; + struct wpabuf *vendor_ext_m1; + struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; + + int p2p; +}; + +/** + * 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; + + /** + * pbc_in_m1 - Do not remove PushButton config method in M1 (AP) + * + * This can be used to enable a workaround to allow Windows 7 to use + * PBC with the AP. + */ + int pbc_in_m1; + + /** + * peer_pubkey_hash - Peer public key hash or %NULL if not known + */ + const u8 *peer_pubkey_hash; +}; + +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); +int wps_is_20(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(u16 pw_id, 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 + * @p2p_dev_addr: P2P Device Address of the Enrollee or all zeros if not + * @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 *p2p_dev_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 + * @dev_pw: Device Password (PIN) used during registration + * @dev_pw_len: Length of dev_pw in octets + * + * This callback is called whenever an Enrollee completes registration + * successfully. + */ + void (*reg_success_cb)(void *ctx, const u8 *mac_addr, + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len); + + /** + * 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; + + /** + * force_per_enrollee_psk - Force per-Enrollee random PSK + * + * This forces per-Enrollee random PSK to be generated even if a default + * PSK is set for a network. + */ + int force_per_enrollee_psk; +}; + + +/** + * 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_PBC_ACTIVE - PBC mode was activated + */ + WPS_EV_PBC_ACTIVE, + + /** + * WPS_EV_PBC_DISABLE - PBC mode was disabled + */ + WPS_EV_PBC_DISABLE, + + /** + * 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, + + /** + * WPS_EV_AP_PIN_SUCCESS - External Registrar used correct AP PIN + */ + WPS_EV_AP_PIN_SUCCESS +}; + +/** + * 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; + u8 peer_macaddr[ETH_ALEN]; + } fail; + + struct wps_event_success { + u8 peer_macaddr[ETH_ALEN]; + } success; + + struct wps_event_pwd_auth_fail { + int enrollee; + int part; + u8 peer_macaddr[ETH_ALEN]; + } 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[SSID_MAX_LEN]; + + /** + * ssid_len - Length of ssid in octets + */ + size_t ssid_len; + + /** + * dev - Own WPS device data + */ + struct wps_device_data dev; + + /** + * 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; + + /** + * encr_types - Current AP encryption type (WPS_ENCR_*) + */ + u16 ap_encr_type; + + /** + * ap_auth_type - Current AP authentication types (WPS_AUTH_*) + */ + u16 ap_auth_type; + + /** + * 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); + + /** + * rf_band_cb - Fetch currently used RF band + * @ctx: Higher layer context data (cb_ctx) + * Return: Current used RF band or 0 if not known + */ + int (*rf_band_cb)(void *ctx); + + /** + * 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; + + u16 ap_nfc_dev_pw_id; + struct wpabuf *ap_nfc_dh_pubkey; + struct wpabuf *ap_nfc_dh_privkey; + struct wpabuf *ap_nfc_dev_pw; +}; + +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_complete(struct wps_registrar *registrar, const u8 *uuid_e, + const u8 *dev_pw, size_t dev_pw_len); +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); +int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, + const u8 *pubkey_hash, u16 pw_id, + const u8 *dev_pw, size_t dev_pw_len, + int pk_hash_provided_oob); +int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, + const u8 *oob_dev_pw, + size_t oob_dev_pw_len); +void wps_registrar_flush(struct wps_registrar *reg); + +int wps_build_credential_wrap(struct wpabuf *msg, + const 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); +int wps_pin_str_valid(const char *pin); +void wps_free_pending_msgs(struct upnp_pending_message *msgs); + +struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band, + int channel); +int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr); +int wps_attr_text(struct wpabuf *data, char *buf, char *end); +const char * wps_ei_str(enum wps_error_indication ei); + +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, const u8 *addr); +const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr); +int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr, + const u8 *pin, size_t pin_len); +int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr, + const struct wps_credential *cred); +int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr, + const u8 *pin, size_t pin_len, + const struct wps_credential *cred); +struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps, + struct wps_credential *cred); +struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid, + const u8 *addr); +struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er, + struct wps_context *wps, const u8 *uuid, + const u8 *addr, struct wpabuf *pubkey); + +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); +struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, + const struct wpabuf *pubkey, + const struct wpabuf *dev_pw); +struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey, + struct wpabuf *dev_pw); +int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey); +struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, + struct wpabuf **privkey, + struct wpabuf **dev_pw); +struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx, + struct wpabuf *nfc_dh_pubkey); +struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx, + struct wpabuf *nfc_dh_pubkey, + const u8 *bssid, int freq); +struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx, + struct wpabuf *nfc_dh_pubkey); +struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx, + int nfc_dev_pw_id, + struct wpabuf *nfc_dh_pubkey, + struct wpabuf *nfc_dev_pw); + +/* ndef.c */ +struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf); +struct wpabuf * ndef_build_wifi(const struct wpabuf *buf); +struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf); +struct wpabuf * ndef_build_p2p(const struct wpabuf *buf); + +#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/freebsd/contrib/wpa/src/wps/wps_attr_build.c b/freebsd/contrib/wpa/src/wps/wps_attr_build.c new file mode 100644 index 00000000..742e082c --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_attr_build.c @@ -0,0 +1,487 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Wi-Fi Protected Setup - attribute building + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/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); + wps->dh_privkey = NULL; + if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey && + wps->wps->dh_ctx) { + wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys"); + if (wps->wps->dh_pubkey == NULL) { + wpa_printf(MSG_DEBUG, + "WPS: wps->wps->dh_pubkey == NULL"); + return -1; + } + 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); +#ifdef CONFIG_WPS_NFC + } else if ((wps->dev_pw_id >= 0x10 || + wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) && + (wps->wps->ap || + (wps->wps->ap_nfc_dh_pubkey && + wps->wps->ap_nfc_dev_pw_id == + DEV_PW_NFC_CONNECTION_HANDOVER && + wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)) && + (wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id || + wps->wps->ap_nfc_dh_pubkey)) { + wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys"); + if (wps->wps->ap_nfc_dh_privkey == NULL) { + wpa_printf(MSG_DEBUG, + "WPS: wps->wps->ap_nfc_dh_privkey == NULL"); + return -1; + } + if (wps->wps->ap_nfc_dh_pubkey == NULL) { + wpa_printf(MSG_DEBUG, + "WPS: wps->wps->ap_nfc_dh_pubkey == NULL"); + return -1; + } + wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey); + pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey); + wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey); +#endif /* CONFIG_WPS_NFC */ + } else { + wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys"); + 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) +{ + if (wpabuf_tailroom(msg) < 4 + WPS_UUID_LEN) + return -1; + 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. + */ + if (wpabuf_tailroom(msg) < 5) + return -1; + 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) +{ + u8 *len; + +#ifdef CONFIG_WPS_TESTING + if (WPS_VERSION == 0x10) + return 0; +#endif /* CONFIG_WPS_TESTING */ + + if (wpabuf_tailroom(msg) < + 7 + 3 + (req_to_enroll ? 3 : 0) + + (auth_macs ? 2 + auth_macs_count * ETH_ALEN : 0)) + return -1; + 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); + +#ifdef CONFIG_WPS_TESTING + if (WPS_VERSION > 0x20) { + if (wpabuf_tailroom(msg) < 5) + return -1; + 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; + /* WPA/WPA2-Enterprise enrollment not supported through WPS */ + auth_types &= ~WPS_AUTH_WPA; + auth_types &= ~WPS_AUTH_WPA2; + auth_types &= ~WPS_AUTH_SHARED; + 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; + encr_types &= ~WPS_ENCR_WEP; + 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_pw(struct wpabuf *msg, u16 dev_pw_id, + const struct wpabuf *pubkey, const u8 *dev_pw, + size_t dev_pw_len) +{ + size_t hash_len; + const u8 *addr[1]; + u8 pubkey_hash[WPS_HASH_LEN]; + + wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password (dev_pw_id=%u)", + dev_pw_id); + addr[0] = wpabuf_head(pubkey); + hash_len = wpabuf_len(pubkey); + sha256_vector(1, addr, &hash_len, pubkey_hash); +#ifdef CONFIG_WPS_TESTING + if (wps_corrupt_pkhash) { + wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash", + pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); + wpa_printf(MSG_INFO, "WPS: Testing - corrupt public key hash"); + pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN - 2]++; + } +#endif /* CONFIG_WPS_TESTING */ + + wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD); + wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len); + wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash", + pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); + wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); + wpabuf_put_be16(msg, dev_pw_id); + if (dev_pw) { + wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password", + dev_pw, dev_pw_len); + wpabuf_put_data(msg, dev_pw, dev_pw_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; +} + + +int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr) +{ + wpa_printf(MSG_DEBUG, "WPS: * MAC Address (" MACSTR ")", + MAC2STR(addr)); + wpabuf_put_be16(msg, ATTR_MAC_ADDR); + wpabuf_put_be16(msg, ETH_ALEN); + wpabuf_put_data(msg, addr, ETH_ALEN); + return 0; +} + + +int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands) +{ + wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", rf_bands); + wpabuf_put_be16(msg, ATTR_RF_BANDS); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, rf_bands); + return 0; +} + + +int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel) +{ + wpa_printf(MSG_DEBUG, "WPS: * AP Channel (%u)", ap_channel); + wpabuf_put_be16(msg, ATTR_AP_CHANNEL); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, ap_channel); + return 0; +} diff --git a/freebsd/contrib/wpa/src/wps/wps_attr_parse.c b/freebsd/contrib/wpa/src/wps/wps_attr_parse.c new file mode 100644 index 00000000..748fe0ac --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_attr_parse.c @@ -0,0 +1,665 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Wi-Fi Protected Setup - attribute parsing + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wps_defs.h" +#include "wps_attr_parse.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; + case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u", + len); + return -1; + } + attr->registrar_configuration_methods = 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_PUBKEY_HASH_LEN + 2 || + len > WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN || + (len < WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_MIN_LEN && + WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) != + DEV_PW_NFC_CONNECTION_HANDOVER)) { + wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device " + "Password length %u", len); + return -1; + } + attr->oob_dev_password = pos; + attr->oob_dev_password_len = len; + 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_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; + if (len > WPS_MANUFACTURER_MAX_LEN) + attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN; + else + attr->manufacturer_len = len; + break; + case ATTR_MODEL_NAME: + attr->model_name = pos; + if (len > WPS_MODEL_NAME_MAX_LEN) + attr->model_name_len = WPS_MODEL_NAME_MAX_LEN; + else + attr->model_name_len = len; + break; + case ATTR_MODEL_NUMBER: + attr->model_number = pos; + if (len > WPS_MODEL_NUMBER_MAX_LEN) + attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN; + else + attr->model_number_len = len; + break; + case ATTR_SERIAL_NUMBER: + attr->serial_number = pos; + if (len > WPS_SERIAL_NUMBER_MAX_LEN) + attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN; + else + attr->serial_number_len = len; + break; + case ATTR_DEV_NAME: + if (len > WPS_DEV_NAME_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore too long Device Name (len=%u)", + len); + break; + } + attr->dev_name = pos; + attr->dev_name_len = len; + break; + case ATTR_PUBLIC_KEY: + /* + * The Public Key attribute is supposed to be exactly 192 bytes + * in length. Allow couple of bytes shorter one to try to + * interoperate with implementations that do not use proper + * zero-padding. + */ + if (len < 190 || len > 192) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore Public Key with unexpected length %u", + len); + break; + } + 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: + if (len > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "WPS: Ignore too long SSID (len=%u)", len); + break; + } + attr->ssid = pos; + attr->ssid_len = len; + break; + case ATTR_NETWORK_KEY: + attr->network_key = pos; + attr->network_key_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; + case ATTR_AP_CHANNEL: + if (len != 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel " + "length %u", len); + return -1; + } + attr->ap_channel = pos; + break; + default: + wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x " + "len=%u", type, len); + break; + } + + return 0; +} + + +int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr) +{ + const u8 *pos, *end; + u16 type, len; +#ifdef WPS_WORKAROUNDS + u16 prev_type = 0; +#endif /* WPS_WORKAROUNDS */ + + 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; + +#ifdef WPS_WORKAROUNDS + prev_type = type; +#endif /* WPS_WORKAROUNDS */ + pos += len; + } + + return 0; +} diff --git a/freebsd/contrib/wpa/src/wps/wps_attr_parse.h b/freebsd/contrib/wpa/src/wps/wps_attr_parse.h new file mode 100644 index 00000000..8188fe91 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_attr_parse.h @@ -0,0 +1,104 @@ +/* + * Wi-Fi Protected Setup - attribute parsing + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPS_ATTR_PARSE_H +#define WPS_ATTR_PARSE_H + +#include "wps.h" + +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 *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 *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) */ + const u8 *ap_channel; /* 2 octets */ + const u8 *registrar_configuration_methods; /* 2 octets */ + + /* variable length fields */ + const u8 *manufacturer; + const u8 *model_name; + const u8 *model_number; + const u8 *serial_number; + const u8 *dev_name; + const u8 *public_key; + const u8 *encr_settings; + const u8 *ssid; /* <= 32 octets */ + const u8 *network_key; /* <= 64 octets */ + const u8 *authorized_macs; /* <= 30 octets */ + const u8 *sec_dev_type_list; /* <= 128 octets */ + const u8 *oob_dev_password; /* 38..54 octets */ + u16 manufacturer_len; + u16 model_name_len; + u16 model_number_len; + u16 serial_number_len; + u16 dev_name_len; + u16 public_key_len; + u16 encr_settings_len; + u16 ssid_len; + u16 network_key_len; + u16 authorized_macs_len; + u16 sec_dev_type_list_len; + u16 oob_dev_password_len; + + /* attributes that can occur multiple times */ +#define MAX_CRED_COUNT 10 +#define MAX_REQ_DEV_TYPE_COUNT 10 + + unsigned int num_cred; + unsigned int num_req_dev_type; + unsigned int num_vendor_ext; + + u16 cred_len[MAX_CRED_COUNT]; + u16 vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT]; + + const u8 *cred[MAX_CRED_COUNT]; + const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; + const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT]; +}; + +int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); + +#endif /* WPS_ATTR_PARSE_H */ diff --git a/freebsd/contrib/wpa/src/wps/wps_attr_process.c b/freebsd/contrib/wpa/src/wps/wps_attr_process.c new file mode 100644 index 00000000..329649df --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_attr_process.c @@ -0,0 +1,274 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Wi-Fi Protected Setup - attribute processing + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/sha256.h" +#include "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_const(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_const(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_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)) + 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/freebsd/contrib/wpa/src/wps/wps_common.c b/freebsd/contrib/wpa/src/wps/wps_common.c new file mode 100644 index 00000000..b6c83892 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_common.c @@ -0,0 +1,911 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Wi-Fi Protected Setup - common functionality + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/defs.h" +#include "common/ieee802_11_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); +} + + +int wps_pin_str_valid(const char *pin) +{ + const char *p; + size_t len; + + p = pin; + while (*p >= '0' && *p <= '9') + p++; + if (*p != '\0') + return 0; + + len = p - pin; + return len == 4 || len == 8; +} + + +void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, + u16 config_error, u16 error_indication, const u8 *mac_addr) +{ + 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; + os_memcpy(data.fail.peer_macaddr, mac_addr, ETH_ALEN); + wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data); +} + + +void wps_success_event(struct wps_context *wps, const u8 *mac_addr) +{ + union wps_event_data data; + + if (wps->event_cb == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.success.peer_macaddr, mac_addr, ETH_ALEN); + wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, &data); +} + + +void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part, + const u8 *mac_addr) +{ + 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; + os_memcpy(data.pwd_auth_fail.peer_macaddr, mac_addr, ETH_ALEN); + 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); +} + + +void wps_pbc_active_event(struct wps_context *wps) +{ + if (wps->event_cb == NULL) + return; + + wps->event_cb(wps->cb_ctx, WPS_EV_PBC_ACTIVE, NULL); +} + + +void wps_pbc_disable_event(struct wps_context *wps) +{ + if (wps->event_cb == NULL) + return; + + wps->event_cb(wps->cb_ctx, WPS_EV_PBC_DISABLE, NULL); +} + + +#ifdef CONFIG_WPS_OOB + +struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band, + int channel) +{ + 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_cred(&data, plain) || + (rf_band && wps_build_rf_bands_attr(plain, rf_band)) || + (channel && wps_build_ap_channel(plain, channel)) || + wps_build_mac_addr(plain, wps->dev.mac_addr) || + wps_build_wfa_ext(plain, 0, NULL, 0)) { + os_free(data.new_psk); + wpabuf_free(plain); + return NULL; + } + + if (wps->wps_state == WPS_STATE_NOT_CONFIGURED && data.new_psk && + wps->ap) { + struct wps_credential cred; + + wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based " + "on credential token generation"); + + os_memset(&cred, 0, sizeof(cred)); + os_memcpy(cred.ssid, wps->ssid, wps->ssid_len); + cred.ssid_len = 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, data.new_psk, data.new_psk_len); + cred.key_len = data.new_psk_len; + + wps->wps_state = WPS_STATE_CONFIGURED; + wpa_hexdump_ascii_key(MSG_DEBUG, + "WPS: Generated random passphrase", + data.new_psk, data.new_psk_len); + if (wps->cred_cb) + wps->cred_cb(wps->cb_ctx, &cred); + } + + os_free(data.new_psk); + + return plain; +} + + +struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, + const struct wpabuf *pubkey, + const struct wpabuf *dev_pw) +{ + struct wpabuf *data; + + data = wpabuf_alloc(200); + if (data == NULL) + return NULL; + + if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey, + wpabuf_head(dev_pw), wpabuf_len(dev_pw)) || + wps_build_wfa_ext(data, 0, NULL, 0)) { + wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password " + "token"); + wpabuf_free(data); + return NULL; + } + + return data; +} + + +int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr) +{ + struct wpabuf msg; + size_t i; + + 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; +} + + +#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 (os_snprintf_error(buf_len, ret)) + 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 || str[0] == '\0') { + /* Default to enabling methods based on build configuration */ + methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + methods |= WPS_CONFIG_VIRT_DISPLAY; +#ifdef CONFIG_WPS_NFC + methods |= WPS_CONFIG_NFC_INTERFACE; +#endif /* CONFIG_WPS_NFC */ +#ifdef CONFIG_P2P + methods |= WPS_CONFIG_P2PS; +#endif /* CONFIG_P2P */ + } else { + 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; + 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; + if (os_strstr(str, "p2ps")) + methods |= WPS_CONFIG_P2PS; + } + + 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; +} + + +#ifdef CONFIG_WPS_NFC + +struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey, + struct wpabuf *dev_pw) +{ + struct wpabuf *ret; + + if (pubkey == NULL || dev_pw == NULL) + return NULL; + + ret = wps_build_nfc_pw_token(id, pubkey, dev_pw); + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} + + +int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey) +{ + struct wpabuf *priv = NULL, *pub = NULL; + void *dh_ctx; + + dh_ctx = dh5_init(&priv, &pub); + if (dh_ctx == NULL) + return -1; + pub = wpabuf_zeropad(pub, 192); + if (pub == NULL) { + wpabuf_free(priv); + return -1; + } + wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub); + dh5_free(dh_ctx); + + wpabuf_free(*pubkey); + *pubkey = pub; + wpabuf_free(*privkey); + *privkey = priv; + + return 0; +} + + +struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, + struct wpabuf **privkey, + struct wpabuf **dev_pw) +{ + struct wpabuf *pw; + u16 val; + + pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN); + if (pw == NULL) + return NULL; + + if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN), + WPS_OOB_DEVICE_PASSWORD_LEN) || + random_get_bytes((u8 *) &val, sizeof(val))) { + wpabuf_free(pw); + return NULL; + } + + if (wps_nfc_gen_dh(pubkey, privkey) < 0) { + wpabuf_free(pw); + return NULL; + } + + *id = 0x10 + val % 0xfff0; + wpabuf_free(*dev_pw); + *dev_pw = pw; + + return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw); +} + + +struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx, + struct wpabuf *nfc_dh_pubkey) +{ + struct wpabuf *msg; + void *len; + + if (ctx == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection " + "handover request"); + + if (nfc_dh_pubkey == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password " + "configured"); + return NULL; + } + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return msg; + len = wpabuf_put(msg, 2); + + if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER, + nfc_dh_pubkey, NULL, 0) || + wps_build_uuid_e(msg, ctx->uuid) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + + WPA_PUT_BE16(len, wpabuf_len(msg) - 2); + + return msg; +} + + +static int wps_build_ssid(struct wpabuf *msg, struct wps_context *wps) +{ + wpa_printf(MSG_DEBUG, "WPS: * SSID"); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID in Connection Handover Select", + wps->ssid, wps->ssid_len); + wpabuf_put_be16(msg, ATTR_SSID); + wpabuf_put_be16(msg, wps->ssid_len); + wpabuf_put_data(msg, wps->ssid, wps->ssid_len); + return 0; +} + + +static int wps_build_ap_freq(struct wpabuf *msg, int freq) +{ + enum hostapd_hw_mode mode; + u8 channel, rf_band; + u16 ap_channel; + + if (freq <= 0) + return 0; + + mode = ieee80211_freq_to_chan(freq, &channel); + if (mode == NUM_HOSTAPD_MODES) + return 0; /* Unknown channel */ + + if (mode == HOSTAPD_MODE_IEEE80211G || mode == HOSTAPD_MODE_IEEE80211B) + rf_band = WPS_RF_24GHZ; + else if (mode == HOSTAPD_MODE_IEEE80211A) + rf_band = WPS_RF_50GHZ; + else if (mode == HOSTAPD_MODE_IEEE80211AD) + rf_band = WPS_RF_60GHZ; + else + return 0; /* Unknown band */ + ap_channel = channel; + + if (wps_build_rf_bands_attr(msg, rf_band) || + wps_build_ap_channel(msg, ap_channel)) + return -1; + + return 0; +} + + +struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx, + struct wpabuf *nfc_dh_pubkey, + const u8 *bssid, int freq) +{ + struct wpabuf *msg; + void *len; + + if (ctx == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection " + "handover select"); + + if (nfc_dh_pubkey == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password " + "configured"); + return NULL; + } + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return msg; + len = wpabuf_put(msg, 2); + + if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER, + nfc_dh_pubkey, NULL, 0) || + wps_build_ssid(msg, ctx) || + wps_build_ap_freq(msg, freq) || + (bssid && wps_build_mac_addr(msg, bssid)) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + + WPA_PUT_BE16(len, wpabuf_len(msg) - 2); + + return msg; +} + + +struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx, + struct wpabuf *nfc_dh_pubkey) +{ + struct wpabuf *msg; + + if (ctx == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection " + "handover request (P2P)"); + + if (nfc_dh_pubkey == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No NFC DH Public Key configured"); + return NULL; + } + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return msg; + + if (wps_build_manufacturer(&ctx->dev, msg) || + wps_build_model_name(&ctx->dev, msg) || + wps_build_model_number(&ctx->dev, msg) || + wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER, + nfc_dh_pubkey, NULL, 0) || + wps_build_rf_bands(&ctx->dev, msg, 0) || + wps_build_serial_number(&ctx->dev, msg) || + wps_build_uuid_e(msg, ctx->uuid) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx, + int nfc_dev_pw_id, + struct wpabuf *nfc_dh_pubkey, + struct wpabuf *nfc_dev_pw) +{ + struct wpabuf *msg; + const u8 *dev_pw; + size_t dev_pw_len; + + if (ctx == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection " + "handover select (P2P)"); + + if (nfc_dh_pubkey == NULL || + (nfc_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && + nfc_dev_pw == NULL)) { + wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password " + "configured"); + return NULL; + } + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return msg; + + if (nfc_dev_pw) { + dev_pw = wpabuf_head(nfc_dev_pw); + dev_pw_len = wpabuf_len(nfc_dev_pw); + } else { + dev_pw = NULL; + dev_pw_len = 0; + } + + if (wps_build_manufacturer(&ctx->dev, msg) || + wps_build_model_name(&ctx->dev, msg) || + wps_build_model_number(&ctx->dev, msg) || + wps_build_oob_dev_pw(msg, nfc_dev_pw_id, nfc_dh_pubkey, + dev_pw, dev_pw_len) || + wps_build_rf_bands(&ctx->dev, msg, 0) || + wps_build_serial_number(&ctx->dev, msg) || + wps_build_uuid_e(msg, ctx->uuid) || + wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + +#endif /* CONFIG_WPS_NFC */ diff --git a/freebsd/contrib/wpa/src/wps/wps_defs.h b/freebsd/contrib/wpa/src/wps/wps_defs.h new file mode 100644 index 00000000..a23b979d --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_defs.h @@ -0,0 +1,379 @@ +/* + * Wi-Fi Protected Setup - message definitions + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README 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; +extern int wps_corrupt_pkhash; +#define WPS_VERSION wps_version_number + +#else /* CONFIG_WPS_TESTING */ + +#define WPS_VERSION 0x20 + +#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_MIN_LEN 16 +#define WPS_OOB_DEVICE_PASSWORD_LEN 32 +#define WPS_OOB_PUBKEY_HASH_LEN 20 +#define WPS_DEV_NAME_MAX_LEN 32 +#define WPS_MANUFACTURER_MAX_LEN 64 +#define WPS_MODEL_NAME_MAX_LEN 32 +#define WPS_MODEL_NUMBER_MAX_LEN 32 +#define WPS_SERIAL_NUMBER_MAX_LEN 32 + +/* 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, + WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05 +}; + +/* 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, + DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007, + DEV_PW_P2PS_DEFAULT = 0x0008 +}; + +/* 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 /* deprecated */ +#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 /* deprecated */ +#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, + WPS_CFG_60G_CHAN_NOT_SUPPORTED = 19, + WPS_CFG_PUBLIC_KEY_HASH_MISMATCH = 20 +}; + +/* 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, + WPS_EI_AUTH_FAILURE, + NUM_WPS_EI_VALUES +}; + +/* RF Bands */ +#define WPS_RF_24GHZ 0x01 +#define WPS_RF_50GHZ 0x02 +#define WPS_RF_60GHZ 0x04 + +/* 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 +#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280 +#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480 +#define WPS_CONFIG_P2PS 0x1000 +#define WPS_CONFIG_VIRT_DISPLAY 0x2008 +#define WPS_CONFIG_PHY_DISPLAY 0x4008 + +/* 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, + WPS_DEV_AUDIO = 11, +}; + +enum wps_dev_subcateg { + WPS_DEV_COMPUTER_PC = 1, + WPS_DEV_COMPUTER_SERVER = 2, + WPS_DEV_COMPUTER_MEDIA_CENTER = 3, + WPS_DEV_COMPUTER_ULTRA_MOBILE = 4, + WPS_DEV_COMPUTER_NOTEBOOK = 5, + WPS_DEV_COMPUTER_DESKTOP = 6, + WPS_DEV_COMPUTER_MID = 7, + WPS_DEV_COMPUTER_NETBOOK = 8, + WPS_DEV_COMPUTER_TABLET = 9, + WPS_DEV_INPUT_KEYBOARD = 1, + WPS_DEV_INPUT_MOUSE = 2, + WPS_DEV_INPUT_JOYSTICK = 3, + WPS_DEV_INPUT_TRACKBALL = 4, + WPS_DEV_INPUT_GAMING = 5, + WPS_DEV_INPUT_REMOTE = 6, + WPS_DEV_INPUT_TOUCHSCREEN = 7, + WPS_DEV_INPUT_BIOMETRIC_READER = 8, + WPS_DEV_INPUT_BARCODE_READER = 9, + WPS_DEV_PRINTER_PRINTER = 1, + WPS_DEV_PRINTER_SCANNER = 2, + WPS_DEV_PRINTER_FAX = 3, + WPS_DEV_PRINTER_COPIER = 4, + WPS_DEV_PRINTER_ALL_IN_ONE = 5, + WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1, + WPS_DEV_CAMERA_VIDEO = 2, + WPS_DEV_CAMERA_WEB = 3, + WPS_DEV_CAMERA_SECURITY = 4, + 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_NETWORK_INFRA_GATEWAY = 4, + WPS_DEV_NETWORK_INFRA_BRIDGE = 5, + WPS_DEV_DISPLAY_TV = 1, + WPS_DEV_DISPLAY_PICTURE_FRAME = 2, + WPS_DEV_DISPLAY_PROJECTOR = 3, + WPS_DEV_DISPLAY_MONITOR = 4, + WPS_DEV_MULTIMEDIA_DAR = 1, + WPS_DEV_MULTIMEDIA_PVR = 2, + WPS_DEV_MULTIMEDIA_MCX = 3, + WPS_DEV_MULTIMEDIA_SET_TOP_BOX = 4, + WPS_DEV_MULTIMEDIA_MEDIA_SERVER = 5, + WPS_DEV_MULTIMEDIA_PORTABLE_VIDEO_PLAYER = 6, + WPS_DEV_GAMING_XBOX = 1, + WPS_DEV_GAMING_XBOX360 = 2, + WPS_DEV_GAMING_PLAYSTATION = 3, + WPS_DEV_GAMING_GAME_CONSOLE = 4, + WPS_DEV_GAMING_PORTABLE_DEVICE = 5, + WPS_DEV_PHONE_WINDOWS_MOBILE = 1, + WPS_DEV_PHONE_SINGLE_MODE = 2, + WPS_DEV_PHONE_DUAL_MODE = 3, + WPS_DEV_PHONE_SP_SINGLE_MODE = 4, + WPS_DEV_PHONE_SP_DUAL_MODE = 5, + WPS_DEV_AUDIO_TUNER_RECV = 1, + WPS_DEV_AUDIO_SPEAKERS = 2, + WPS_DEV_AUDIO_PMP = 3, + WPS_DEV_AUDIO_HEADSET = 4, + WPS_DEV_AUDIO_HEADPHONES = 5, + WPS_DEV_AUDIO_MICROPHONE = 6, + WPS_DEV_AUDIO_HOME_THEATRE = 7, +}; + + +/* 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/freebsd/contrib/wpa/src/wps/wps_dev_attr.c b/freebsd/contrib/wpa/src/wps/wps_dev_attr.c new file mode 100644 index 00000000..1cc6cee8 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_dev_attr.c @@ -0,0 +1,421 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Wi-Fi Protected Setup - device attributes + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wps_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; +} + + +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_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg) +{ + if (dev->vendor_ext_m1 != NULL) { + wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension M1", + wpabuf_head_u8(dev->vendor_ext_m1), + wpabuf_len(dev->vendor_ext_m1)); + wpabuf_put_be16(msg, ATTR_VENDOR_EXT); + wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1)); + wpabuf_put_buf(msg, dev->vendor_ext_m1); + } + return 0; +} + + +int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg, + u8 rf_band) +{ + return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands); +} + + +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 = dup_binstr(str, str_len); + if (dev->manufacturer == NULL) + return -1; + + 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 = dup_binstr(str, str_len); + if (dev->model_name == NULL) + return -1; + + 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 = dup_binstr(str, str_len); + if (dev->model_number == NULL) + return -1; + + 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 = dup_binstr(str, str_len); + if (dev->serial_number == NULL) + return -1; + + 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 = dup_binstr(str, str_len); + if (dev->device_name == NULL) + return -1; + + 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_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/freebsd/contrib/wpa/src/wps/wps_dev_attr.h b/freebsd/contrib/wpa/src/wps/wps_dev_attr.h new file mode 100644 index 00000000..c9034add --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_dev_attr.h @@ -0,0 +1,39 @@ +/* + * Wi-Fi Protected Setup - device attributes + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README 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_serial_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_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg, + u8 rf_band); +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_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/freebsd/contrib/wpa/src/wps/wps_enrollee.c b/freebsd/contrib/wpa/src/wps/wps_enrollee.c new file mode 100644 index 00000000..e2cfa571 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_enrollee.c @@ -0,0 +1,1510 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Wi-Fi Protected Setup - Enrollee + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/crypto.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "wps_i.h" +#include "wps_dev_attr.h" + + +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 && !wps->pbc_in_m1 && + (wps->dev_password_len != 0 || + (config_methods & WPS_CONFIG_DISPLAY))) { + /* + * These are the methods that the AP supports as an Enrollee + * for adding external Registrars, so remove PushButton. + * + * As a workaround for Windows 7 mechanism for probing WPS + * capabilities from M1, leave PushButton option if no PIN + * method is available or if WPS configuration enables PBC + * workaround. + */ + config_methods &= ~WPS_CONFIG_PUSHBUTTON; + config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); + } + + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_M1) || + wps_build_uuid_e(msg, wps->uuid_e) || + wps_build_mac_addr(msg, wps->mac_addr_e) || + 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->wps->rf_band_cb(wps->wps->cb_ctx)) || + 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) || + wps_build_vendor_ext_m1(&wps->wps->dev, msg)) { + wpabuf_free(msg); + return NULL; + } + + wps->state = RECV_M2; + return msg; +} + + +static struct wpabuf * wps_build_m3(struct wps_data *wps) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "WPS: Building Message M3"); + + if (wps->dev_password == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Device Password available"); + return NULL; + } + wps_derive_psk(wps, wps->dev_password, wps->dev_password_len); + + if (wps->wps->ap && random_pool_ready() != 1) { + wpa_printf(MSG_INFO, + "WPS: Not enough entropy in random pool to proceed - do not allow AP PIN to be used"); + return NULL; + } + + 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) +{ + u16 auth_type = wps->wps->ap_auth_type; + + /* + * Work around issues with Windows 7 WPS implementation not liking + * multiple Authentication Type bits in M7 AP Settings attribute by + * showing only the most secure option from current configuration. + */ + if (auth_type & WPS_AUTH_WPA2PSK) + auth_type = WPS_AUTH_WPA2PSK; + else if (auth_type & WPS_AUTH_WPAPSK) + auth_type = WPS_AUTH_WPAPSK; + else if (auth_type & WPS_AUTH_OPEN) + auth_type = WPS_AUTH_OPEN; + + wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", auth_type); + wpabuf_put_be16(msg, ATTR_AUTH_TYPE); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, auth_type); + return 0; +} + + +static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg) +{ + u16 encr_type = wps->wps->ap_encr_type; + + /* + * Work around issues with Windows 7 WPS implementation not liking + * multiple Encryption Type bits in M7 AP Settings attribute by + * showing only the most secure option from current configuration. + */ + if (wps->wps->ap_auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) { + if (encr_type & WPS_ENCR_AES) + encr_type = WPS_ENCR_AES; + else if (encr_type & WPS_ENCR_TKIP) + encr_type = WPS_ENCR_TKIP; + } + + wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", encr_type); + wpabuf_put_be16(msg, ATTR_ENCR_TYPE); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, encr_type); + return 0; +} + + +static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg) +{ + if ((wps->wps->ap_auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) && + wps->wps->network_key_len == 0) { + char hex[65]; + u8 psk[32]; + /* Generate a random per-device PSK */ + if (random_pool_ready() != 1 || + random_get_bytes(psk, sizeof(psk)) < 0) { + wpa_printf(MSG_INFO, + "WPS: Could not generate random PSK"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK", + psk, sizeof(psk)); + wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%u)", + (unsigned int) wps->new_psk_len * 2); + wpa_snprintf_hex(hex, sizeof(hex), psk, sizeof(psk)); + wpabuf_put_be16(msg, ATTR_NETWORK_KEY); + wpabuf_put_be16(msg, sizeof(psk) * 2); + wpabuf_put_data(msg, hex, sizeof(psk) * 2); + if (wps->wps->registrar) { + wps_cb_new_psk(wps->wps->registrar, + wps->peer_dev.mac_addr, + wps->p2p_dev_addr, psk, sizeof(psk)); + } + return 0; + } + + wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%u)", + (unsigned int) wps->wps->network_key_len); + 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) +{ + const u8 *start, *end; + int ret; + + 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; + } + + wpa_printf(MSG_DEBUG, "WPS: * AP Settings based on current configuration"); + start = wpabuf_put(plain, 0); + ret = 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); + end = wpabuf_put(plain, 0); + + wpa_hexdump_key(MSG_DEBUG, "WPS: Plaintext AP Settings", + start, end - start); + + return ret; +} + + +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->peer_dev.mac_addr); + 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; + } + + if (wps->peer_pubkey_hash_set) { + u8 hash[WPS_HASH_LEN]; + sha256_vector(1, &pk, &pk_len, hash); + if (os_memcmp_const(hash, wps->peer_pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN) != 0) { + wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch"); + wpa_hexdump(MSG_DEBUG, "WPS: Received public key", + pk, pk_len); + wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key " + "hash", hash, WPS_OOB_PUBKEY_HASH_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash", + wps->peer_pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN); + wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH; + return -1; + } + } + + 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_const(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, wps->peer_dev.mac_addr); + 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_const(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, wps->peer_dev.mac_addr); + 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; + int ret = 0; + + 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 */ + } + + 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; + } + + if (wps->wps->cred_cb) { + wps->cred.cred_attr = cred - 4; + wps->cred.cred_attr_len = cred_len + 4; + ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); + wps->cred.cred_attr = NULL; + wps->cred.cred_attr_len = 0; + } + + return ret; +} + + +static int wps_process_creds(struct wps_data *wps, const u8 *cred[], + u16 cred_len[], unsigned int 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; + int ret = 0; + + 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 */ + } + + 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; + } + +#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 */ + + 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; + } + + if (wps->wps->cred_cb) { + cred.cred_attr = wpabuf_head(attrs); + cred.cred_attr_len = wpabuf_len(attrs); + ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred); + } + + return ret; +} + + +static int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id) +{ + u16 id; + + if (dev_pw_id == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Device Password ID"); + return -1; + } + + id = WPA_GET_BE16(dev_pw_id); + if (wps->dev_pw_id == id) { + wpa_printf(MSG_DEBUG, "WPS: Device Password ID %u", id); + return 0; + } + +#ifdef CONFIG_P2P + if ((id == DEV_PW_DEFAULT && + wps->dev_pw_id == DEV_PW_REGISTRAR_SPECIFIED) || + (id == DEV_PW_REGISTRAR_SPECIFIED && + wps->dev_pw_id == DEV_PW_DEFAULT)) { + /* + * Common P2P use cases indicate whether the PIN is from the + * client or GO using Device Password Id in M1/M2 in a way that + * does not look fully compliant with WSC specification. Anyway, + * this is deployed and needs to be allowed, so ignore changes + * between Registrar-Specified and Default PIN. + */ + wpa_printf(MSG_DEBUG, "WPS: Allow PIN Device Password ID " + "change"); + return 0; + } +#endif /* CONFIG_P2P */ + + wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password " + "ID from %u to %u", wps->dev_pw_id, id); + + if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) { + wpa_printf(MSG_DEBUG, + "WPS: Workaround - ignore PBC-to-PIN change"); + return 0; + } + + if (wps->alt_dev_password && wps->alt_dev_pw_id == id) { + wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password"); + bin_clear_free(wps->dev_password, wps->dev_password_len); + wps->dev_pw_id = wps->alt_dev_pw_id; + wps->dev_password = wps->alt_dev_password; + wps->dev_password_len = wps->alt_dev_password_len; + wps->alt_dev_password = NULL; + wps->alt_dev_password_len = 0; + return 0; + } + + return -1; +} + + +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_process_dev_pw_id(wps, attr->dev_password_id)) { + 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; + } + +#ifdef CONFIG_WPS_NFC + if (wps->peer_pubkey_hash_set) { + struct wpabuf *decrypted; + struct wps_parse_attr eattr; + + decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, + attr->encr_settings_len); + if (decrypted == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt " + "Encrypted Settings attribute"); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + + wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted " + "Settings attribute"); + if (wps_parse_msg(decrypted, &eattr) < 0 || + wps_process_key_wrap_auth(wps, decrypted, + eattr.key_wrap_auth) || + wps_process_creds(wps, eattr.cred, eattr.cred_len, + eattr.num_cred, 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; + } +#endif /* CONFIG_WPS_NFC */ + + 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 != 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_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 != 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_r_snonce2(wps, eattr.r_snonce2)) { + wpabuf_free(decrypted); + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wpabuf_free(decrypted); + + if (wps->wps->ap) + wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS, + NULL); + + 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 != 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_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, + wps->peer_dev.mac_addr); + 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, + wps->peer_dev.mac_addr); + 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, + wps->peer_dev.mac_addr); + 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->peer_dev.mac_addr); + 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, wps->peer_dev.mac_addr); + break; + case RECV_M6: + wps_fail_event(wps->wps, WPS_M5, config_error, + wps->error_indication, wps->peer_dev.mac_addr); + break; + case RECV_M8: + wps_fail_event(wps->wps, WPS_M7, config_error, + wps->error_indication, wps->peer_dev.mac_addr); + 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/freebsd/contrib/wpa/src/wps/wps_er.h b/freebsd/contrib/wpa/src/wps/wps_er.h new file mode 100644 index 00000000..4b48ff6a --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_er.h @@ -0,0 +1,112 @@ +/* + * Wi-Fi Protected Setup - External Registrar + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README 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]; + int forced_ifname; + 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/freebsd/contrib/wpa/src/wps/wps_i.h b/freebsd/contrib/wpa/src/wps/wps_i.h new file mode 100644 index 00000000..f7154f87 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_i.h @@ -0,0 +1,218 @@ +/* + * Wi-Fi Protected Setup - internal definitions + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPS_I_H +#define WPS_I_H + +#include "wps.h" +#include "wps_attr_parse.h" + +struct wps_nfc_pw_token; + +/** + * 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; + u8 *alt_dev_password; + size_t alt_dev_password_len; + u16 alt_dev_pw_id; + + u8 peer_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; + int peer_pubkey_hash_set; + + /** + * 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 */ + int pbc_in_m1; + + struct wps_nfc_pw_token *nfc_pw_token; +}; + + +/* 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, const u8 *mac_addr); +void wps_success_event(struct wps_context *wps, const u8 *mac_addr); +void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part, + const u8 *mac_addr); +void wps_pbc_overlap_event(struct wps_context *wps); +void wps_pbc_timeout_event(struct wps_context *wps); +void wps_pbc_active_event(struct wps_context *wps); +void wps_pbc_disable_event(struct wps_context *wps); + +struct wpabuf * wps_build_wsc_ack(struct wps_data *wps); +struct wpabuf * wps_build_wsc_nack(struct wps_data *wps); + +/* 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_pw(struct wpabuf *msg, u16 dev_pw_id, + const struct wpabuf *pubkey, const u8 *dev_pw, + size_t dev_pw_len); +struct wpabuf * wps_ie_encapsulate(struct wpabuf *data); +int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr); +int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands); +int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel); + +/* 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, + u16 dev_pw_id); +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); +void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg, + struct wps_nfc_pw_token *token); +int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr, + const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len); + +#endif /* WPS_I_H */ diff --git a/freebsd/contrib/wpa/src/wps/wps_registrar.c b/freebsd/contrib/wpa/src/wps/wps_registrar.c new file mode 100644 index 00000000..261b4557 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_registrar.c @@ -0,0 +1,3672 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Wi-Fi Protected Setup - Registrar + * Copyright (c) 2008-2013, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/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 */ + +#ifdef CONFIG_WPS_NFC + +struct wps_nfc_pw_token { + struct dl_list list; + u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; + unsigned int peer_pk_hash_known:1; + u16 pw_id; + u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1]; + size_t dev_pw_len; + int pk_hash_provided_oob; /* whether own PK hash was provided OOB */ +}; + + +static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token) +{ + dl_list_del(&token->list); + bin_clear_free(token, sizeof(*token)); +} + + +static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id) +{ + struct wps_nfc_pw_token *token, *prev; + dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token, + list) { + if (pw_id == 0 || pw_id == token->pw_id) + wps_remove_nfc_pw_token(token); + } +} + + +static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens, + u16 pw_id) +{ + struct wps_nfc_pw_token *token; + dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) { + if (pw_id == token->pw_id) + return token; + } + return NULL; +} + +#else /* CONFIG_WPS_NFC */ + +#define wps_free_nfc_pw_tokens(t, p) do { } while (0) + +#endif /* CONFIG_WPS_NFC */ + + +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_reltime expiration; + u8 enrollee_addr[ETH_ALEN]; +}; + + +static void wps_free_pin(struct wps_uuid_pin *pin) +{ + bin_clear_free(pin->pin, pin->pin_len); + 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_reltime 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 *p2p_dev_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, const u8 *dev_pw, + size_t dev_pw_len); + 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 dl_list nfc_pw_tokens; + 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; + int force_per_enrollee_psk; + + 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]; + + u8 pbc_ignore_uuid[WPS_UUID_LEN]; +#ifdef WPS_WORKAROUNDS + struct os_reltime pbc_ignore_start; +#endif /* WPS_WORKAROUNDS */ +}; + + +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_remove_pin(struct wps_registrar *reg, + struct wps_uuid_pin *pin); + + +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_reltime now; + + os_get_reltime(&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 (os_reltime_expired(&now, &pbc->timestamp, + 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, + const u8 *p2p_dev_addr) +{ + 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 || + (p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) && + os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) == + 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_reltime now; + + os_get_reltime(&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 (os_reltime_expired(&now, &pbc->timestamp, + 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; + if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) == + WPS_CONFIG_VIRT_PUSHBUTTON) + *methods |= WPS_CONFIG_VIRT_PUSHBUTTON; + if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) == + 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; + } +} + + +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; + methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); + 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; + methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); + wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods); + wpabuf_put_be16(msg, ATTR_CONFIG_METHODS); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, methods); + return 0; +} + + +static int wps_build_config_methods_r(struct wps_registrar *reg, + struct wpabuf *msg) +{ + return wps_build_config_methods(msg, reg->wps->config_methods); +} + + +const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count) +{ + *count = 0; + + while (*count < WPS_MAX_AUTHORIZED_MACS) { + if (is_zero_ether_addr(reg->authorized_macs_union[*count])) + break; + (*count)++; + } + + 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(®->pins); + dl_list_init(®->nfc_pw_tokens); + 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; + reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk; + + if (wps_set_ie(reg)) { + wps_registrar_deinit(reg); + return NULL; + } + + return reg; +} + + +void wps_registrar_flush(struct wps_registrar *reg) +{ + if (reg == NULL) + return; + wps_free_pins(®->pins); + wps_free_nfc_pw_tokens(®->nfc_pw_tokens, 0); + wps_free_pbc_sessions(reg->pbc_sessions); + reg->pbc_sessions = NULL; + wps_free_devices(reg->devices); + reg->devices = NULL; +#ifdef WPS_WORKAROUNDS + reg->pbc_ignore_start.sec = 0; +#endif /* WPS_WORKAROUNDS */ +} + + +/** + * 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_registrar_flush(reg); + wpabuf_free(reg->extra_cred); + os_free(reg); +} + + +static void wps_registrar_invalidate_unused(struct wps_registrar *reg) +{ + struct wps_uuid_pin *pin; + + dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { + if (pin->wildcard_uuid == 1 && !(pin->flags & PIN_LOCKED)) { + wpa_printf(MSG_DEBUG, "WPS: Invalidate previously " + "configured wildcard PIN"); + wps_registrar_remove_pin(reg, pin); + break; + } + } +} + + +/** + * 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_reltime(&p->expiration); + p->expiration.sec += timeout; + } + + if (p->wildcard_uuid) + wps_registrar_invalidate_unused(reg); + + dl_list_add(®->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, 0); + 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, 0); +} + + +static void wps_registrar_expire_pins(struct wps_registrar *reg) +{ + struct wps_uuid_pin *pin, *prev; + struct os_reltime now; + + os_get_reltime(&now); + dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) + { + if ((pin->flags & PIN_EXPIRES) && + os_reltime_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() + * @dev_pw: PIN to search for or %NULL to match any + * @dev_pw_len: Length of dev_pw in octets + * Returns: 0 on success, -1 if not wildcard PIN is enabled + */ +static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg, + const u8 *dev_pw, + size_t dev_pw_len) +{ + struct wps_uuid_pin *pin, *prev; + + dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) + { + if (dev_pw && pin->pin && + (dev_pw_len != pin->pin_len || + os_memcmp_const(dev_pw, pin->pin, dev_pw_len) != 0)) + continue; /* different PIN */ + 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, ®->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, ®->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, ®->pins, struct wps_uuid_pin, list) { + if (pin->wildcard_uuid == 1 || + pin->wildcard_uuid == 2) { + wpa_printf(MSG_DEBUG, "WPS: Found a wildcard " + "PIN. Assigned it for this UUID-E"); + pin->wildcard_uuid++; + 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, ®->pins, struct wps_uuid_pin, list) { + if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { + if (pin->wildcard_uuid == 3) { + 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, 0); +} + + +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 activated 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, 0); + + wps_pbc_active_event(reg->wps); + 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); + wps_pbc_disable_event(reg->wps); +} + + +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, 0); +} + + +void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e, + const u8 *dev_pw, size_t dev_pw_len) +{ + if (registrar->pbc) { + wps_registrar_remove_pbc_session(registrar, + uuid_e, NULL); + wps_registrar_pbc_completed(registrar); +#ifdef WPS_WORKAROUNDS + os_get_reltime(®istrar->pbc_ignore_start); +#endif /* WPS_WORKAROUNDS */ + os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN); + } else { + wps_registrar_pin_completed(registrar); + } + + if (dev_pw && + wps_registrar_invalidate_wildcard_pin(registrar, dev_pw, + dev_pw_len) == 0) { + wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN", + dev_pw, dev_pw_len); + } +} + + +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); + eloop_cancel_timeout(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, NULL, 0); + 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; + int skip_add = 0; + + 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); + +#ifdef WPS_WORKAROUNDS + if (reg->pbc_ignore_start.sec && + os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) { + struct os_reltime now, dur; + os_get_reltime(&now); + os_reltime_sub(&now, ®->pbc_ignore_start, &dur); + if (dur.sec >= 0 && dur.sec < 5) { + wpa_printf(MSG_DEBUG, "WPS: Ignore PBC activation " + "based on Probe Request from the Enrollee " + "that just completed PBC provisioning"); + skip_add = 1; + } else + reg->pbc_ignore_start.sec = 0; + } +#endif /* WPS_WORKAROUNDS */ + + if (!skip_add) + 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); + } +} + + +int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr, + const u8 *p2p_dev_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, p2p_dev_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, const u8 *dev_pw, + size_t dev_pw_len) +{ + if (reg->reg_success_cb == NULL) + return; + + reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len); +} + + +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; + methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); + 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(®->wps->dev, beacon, 0)) || + wps_build_wfa_ext(beacon, 0, auth_macs, count) || + wps_build_vendor_ext(®->wps->dev, beacon)) { + wpabuf_free(beacon); + wpabuf_free(probe); + return -1; + } + +#ifdef CONFIG_P2P + if (wps_build_dev_name(®->wps->dev, beacon) || + wps_build_primary_dev_type(®->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(®->wps->dev, probe) || + wps_build_probe_config_methods(reg, probe) || + (reg->dualband && wps_build_rf_bands(®->wps->dev, probe, 0)) || + wps_build_wfa_ext(probe, 0, auth_macs, count) || + wps_build_vendor_ext(®->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; + + bin_clear_free(wps->dev_password, wps->dev_password_len); + wps->dev_password = NULL; + + if (wps->pbc) { + wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC"); + pin = (const u8 *) "00000000"; + pin_len = 8; +#ifdef CONFIG_WPS_NFC + } else if (wps->nfc_pw_token) { + if (wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) + { + wpa_printf(MSG_DEBUG, "WPS: Using NFC connection " + "handover and abbreviated WPS handshake " + "without Device Password"); + return 0; + } + wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC " + "Password Token"); + pin = wps->nfc_pw_token->dev_pw; + pin_len = wps->nfc_pw_token->dev_pw_len; + } else if (wps->dev_pw_id >= 0x10 && + wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id && + wps->wps->ap_nfc_dev_pw) { + wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from own NFC Password Token"); + pin = wpabuf_head(wps->wps->ap_nfc_dev_pw); + pin_len = wpabuf_len(wps->wps->ap_nfc_dev_pw); +#endif /* CONFIG_WPS_NFC */ + } else { + pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e, + &pin_len); + if (pin && wps->dev_pw_id >= 0x10) { + wpa_printf(MSG_DEBUG, "WPS: No match for OOB Device " + "Password ID, but PIN found"); + /* + * See whether Enrollee is willing to use PIN instead. + */ + wps->dev_pw_id = DEV_PW_DEFAULT; + } + } + if (pin == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Device Password available for " + "the Enrollee (context %p registrar %p)", + wps->wps, wps->wps->registrar); + 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_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_mac_addr(msg, cred->mac_addr)) + return -1; + return 0; +} + + +int wps_build_credential_wrap(struct wpabuf *msg, + const struct wps_credential *cred) +{ + struct wpabuf *wbuf; + wbuf = wpabuf_alloc(200); + if (wbuf == NULL) + return -1; + if (wps_build_credential(wbuf, cred)) { + wpabuf_free(wbuf); + return -1; + } + wpabuf_put_be16(msg, ATTR_CRED); + wpabuf_put_be16(msg, wpabuf_len(wbuf)); + wpabuf_put_buf(msg, wbuf); + wpabuf_free(wbuf); + 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 { + 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_NONE) + wps->encr_type = WPS_ENCR_NONE; +#ifdef CONFIG_TESTING_OPTIONS + else if (wps->encr_type & WPS_ENCR_WEP) + wps->encr_type = WPS_ENCR_WEP; +#endif /* CONFIG_TESTING_OPTIONS */ + 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_pool_ready() != 1 || + random_get_bytes(r, sizeof(r)) < 0) { + wpa_printf(MSG_INFO, + "WPS: Could not generate random PSK"); + 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->wps->registrar->force_per_enrollee_psk && + 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->registrar->force_per_enrollee_psk && + 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_pool_ready() != 1 || + random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) { + wpa_printf(MSG_INFO, + "WPS: Could not generate random PSK"); + 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_ap_cred(struct wps_data *wps) +{ + struct wpabuf *msg, *plain; + + msg = wpabuf_alloc(1000); + if (msg == NULL) + return NULL; + + plain = wpabuf_alloc(200); + if (plain == NULL) { + wpabuf_free(msg); + return NULL; + } + + if (wps_build_ap_settings(wps, plain)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + + wpabuf_put_be16(msg, ATTR_CRED); + wpabuf_put_be16(msg, wpabuf_len(plain)); + wpabuf_put_buf(msg, plain); + wpabuf_free(plain); + + return msg; +} + + +static struct wpabuf * wps_build_m2(struct wps_data *wps) +{ + struct wpabuf *msg; + int config_in_m2 = 0; + + 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->wps->rf_band_cb(wps->wps->cb_ctx)) || + 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)) { + wpabuf_free(msg); + return NULL; + } + +#ifdef CONFIG_WPS_NFC + if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob && + wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) { + /* + * Use abbreviated handshake since public key hash allowed + * Enrollee to validate our public key similarly to how Enrollee + * public key was validated. There is no need to validate Device + * Password in this case. + */ + struct wpabuf *plain = wpabuf_alloc(500); + if (plain == NULL || + wps_build_cred(wps, plain) || + wps_build_key_wrap_auth(wps, plain) || + wps_build_encr_settings(wps, msg, plain)) { + wpabuf_free(msg); + wpabuf_free(plain); + return NULL; + } + wpabuf_free(plain); + config_in_m2 = 1; + } +#endif /* CONFIG_WPS_NFC */ + + if (wps_build_authenticator(wps, msg)) { + wpabuf_free(msg); + return NULL; + } + + wps->int_reg = 1; + wps->state = config_in_m2 ? RECV_DONE : 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->wps->rf_band_cb(wps->wps->cb_ctx)) || + 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_const(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, wps->mac_addr_e); + 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_const(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, wps->mac_addr_e); + 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); + + /* + * In case wildcard PIN is used and WPS handshake succeeds in the first + * attempt, wps_registrar_unlock_pin() would not free the PIN, so make + * sure the PIN gets invalidated here. + */ + wps_registrar_invalidate_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; + } + + 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_P2PS_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 && +#ifdef CONFIG_WPS_NFC + wps->dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && +#endif /* CONFIG_WPS_NFC */ + (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_NFC + if (wps->dev_pw_id >= 0x10 || + wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) { + struct wps_nfc_pw_token *token; + const u8 *addr[1]; + u8 hash[WPS_HASH_LEN]; + + wpa_printf(MSG_DEBUG, "WPS: Searching for NFC token match for id=%d (ctx %p registrar %p)", + wps->dev_pw_id, wps->wps, wps->wps->registrar); + token = wps_get_nfc_pw_token( + &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id); + if (token && token->peer_pk_hash_known) { + size_t len; + + wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " + "Password Token"); + dl_list_del(&token->list); + wps->nfc_pw_token = token; + + addr[0] = attr->public_key; + len = attr->public_key_len; + sha256_vector(1, addr, &len, hash); + if (os_memcmp_const(hash, + wps->nfc_pw_token->pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN) != 0) { + wpa_printf(MSG_ERROR, "WPS: Public Key hash " + "mismatch"); + wps->state = SEND_M2D; + wps->config_error = + WPS_CFG_PUBLIC_KEY_HASH_MISMATCH; + return WPS_CONTINUE; + } + } else if (token) { + wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " + "Password Token (no peer PK hash)"); + wps->nfc_pw_token = token; + } else if (wps->dev_pw_id >= 0x10 && + wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id && + wps->wps->ap_nfc_dev_pw) { + wpa_printf(MSG_DEBUG, "WPS: Found match with own NFC Password Token"); + } + } +#endif /* CONFIG_WPS_NFC */ + + 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->mac_addr_e); + 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) +{ + struct wpabuf *msg; + + 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); + + msg = wps_build_ap_cred(wps); + if (msg == NULL) + return -1; + wps->cred.cred_attr = wpabuf_head(msg); + wps->cred.cred_attr_len = wpabuf_len(msg); + + if (wps->ap_settings_cb) { + wps->ap_settings_cb(wps->ap_settings_cb_ctx, + &wps->cred); + wpabuf_free(msg); + return 1; + } + wps_sta_cred_cb(wps); + + wps->cred.cred_attr = NULL; + wps->cred.cred_attr_len = 0; + wpabuf_free(msg); + + 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, wps->mac_addr_e); + 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, wps->mac_addr_e); + 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, wps->mac_addr_e); + 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, wps->mac_addr_e); + break; + case RECV_M5: + wps_fail_event(wps->wps, WPS_M4, config_error, + wps->error_indication, wps->mac_addr_e); + break; + case RECV_M7: + wps_fail_event(wps->wps, WPS_M6, config_error, + wps->error_indication, wps->mac_addr_e); + break; + case RECV_DONE: + wps_fail_event(wps->wps, WPS_M8, config_error, + wps->error_indication, wps->mac_addr_e); + 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; + if (wps->wps->rf_band_cb(wps->wps->cb_ctx) == WPS_RF_60GHZ) { + cred.auth_type = WPS_AUTH_WPA2PSK; + cred.encr_type = WPS_ENCR_AES; + } else { + 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->p2p_dev_addr, 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, + wps->dev_password, wps->dev_password_len); + + if (wps->pbc) { + wps_registrar_remove_pbc_session(wps->wps->registrar, + wps->uuid_e, + wps->p2p_dev_addr); + wps_registrar_pbc_completed(wps->wps->registrar); +#ifdef WPS_WORKAROUNDS + os_get_reltime(&wps->wps->registrar->pbc_ignore_start); +#endif /* WPS_WORKAROUNDS */ + os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e, + WPS_UUID_LEN); + } 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, wps->mac_addr_e); + + 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, wps->mac_addr_e); + } + 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, 0); +} + + +#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, ®->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, + u16 dev_pw_id) +{ + 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; + methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON); + if (reg->pbc) { + reg->sel_reg_dev_password_id_override = + DEV_PW_PUSHBUTTON; + wps_set_pushbutton(&methods, reg->wps->config_methods); + } else if (dev_pw_id) + reg->sel_reg_dev_password_id_override = dev_pw_id; + 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 (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + return len; +} + + +int wps_registrar_config_ap(struct wps_registrar *reg, + struct wps_credential *cred) +{ + wpa_printf(MSG_DEBUG, "WPS: encr_type=0x%x", 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; + } + + if (reg->wps->cred_cb) + return reg->wps->cred_cb(reg->wps->cb_ctx, cred); + + return -1; +} + + +#ifdef CONFIG_WPS_NFC + +int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, + const u8 *pubkey_hash, u16 pw_id, + const u8 *dev_pw, size_t dev_pw_len, + int pk_hash_provided_oob) +{ + struct wps_nfc_pw_token *token; + + if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN) + return -1; + + if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER && + (pubkey_hash == NULL || !pk_hash_provided_oob)) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token " + "addition - missing public key hash"); + return -1; + } + + wps_free_nfc_pw_tokens(®->nfc_pw_tokens, pw_id); + + token = os_zalloc(sizeof(*token)); + if (token == NULL) + return -1; + + token->peer_pk_hash_known = pubkey_hash != NULL; + if (pubkey_hash) + os_memcpy(token->pubkey_hash, pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN); + token->pw_id = pw_id; + token->pk_hash_provided_oob = pk_hash_provided_oob; + if (dev_pw) { + wpa_snprintf_hex_uppercase((char *) token->dev_pw, + sizeof(token->dev_pw), + dev_pw, dev_pw_len); + token->dev_pw_len = dev_pw_len * 2; + } + + dl_list_add(®->nfc_pw_tokens, &token->list); + + reg->selected_registrar = 1; + reg->pbc = 0; + wps_registrar_add_authorized_mac(reg, + (u8 *) "\xff\xff\xff\xff\xff\xff"); + wps_registrar_selected_registrar_changed(reg, pw_id); + 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); + + wpa_printf(MSG_DEBUG, "WPS: Added NFC Device Password %u to Registrar", + pw_id); + + return 0; +} + + +int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, + const u8 *oob_dev_pw, + size_t oob_dev_pw_len) +{ + const u8 *pos, *hash, *dev_pw; + u16 id; + size_t dev_pw_len; + + if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 || + oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN) + return -1; + + hash = oob_dev_pw; + pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN; + id = WPA_GET_BE16(pos); + dev_pw = pos + 2; + dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw; + + wpa_printf(MSG_DEBUG, "WPS: Add NFC Password Token for Password ID %u", + id); + + wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash", + hash, WPS_OOB_PUBKEY_HASH_LEN); + wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len); + + return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw, + dev_pw_len, 0); +} + + +void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg, + struct wps_nfc_pw_token *token) +{ + wps_registrar_remove_authorized_mac(reg, + (u8 *) "\xff\xff\xff\xff\xff\xff"); + wps_registrar_selected_registrar_changed(reg, 0); + + /* + * Free the NFC password token if it was used only for a single protocol + * run. The static handover case uses the same password token multiple + * times, so do not free that case here. + */ + if (token->peer_pk_hash_known) + os_free(token); +} + +#endif /* CONFIG_WPS_NFC */ diff --git a/freebsd/contrib/wpa/src/wps/wps_upnp.c b/freebsd/contrib/wpa/src/wps/wps_upnp.c new file mode 100644 index 00000000..d66ca357 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_upnp.c @@ -0,0 +1,1214 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * 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 no one 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 <time.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); + if (date == NULL) + return; + 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 int uuid_make(u8 uuid[UUID_LEN]) +{ + if (os_get_random(uuid, UUID_LEN) < 0) + return -1; + + /* 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; + + return 0; +} + + +/* + * 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 *host; + char *delim; + char *path; + int port = 80; /* port to send to (default is port 80) */ + struct addrinfo hints; + struct addrinfo *result = NULL; + struct addrinfo *rp; + int rerr; + size_t host_len, path_len; + + /* url MUST begin with http: */ + if (url_len < 7 || os_strncasecmp(url, "http://", 7)) + goto fail; + url += 7; + url_len -= 7; + + /* Make a copy of the string to allow modification during parsing */ + scratch_mem = dup_binstr(url, url_len); + if (scratch_mem == NULL) + goto fail; + wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem); + host = scratch_mem; + path = os_strchr(host, '/'); + if (path) + *path++ = '\0'; /* null terminate host */ + + /* Process and remove optional port component */ + delim = os_strchr(host, ':'); + if (delim) { + *delim = '\0'; /* null terminate host name for now */ + if (isdigit(delim[1])) + port = atol(delim + 1); + } + + /* + * 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(host, 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), host); + goto fail; + } + + if (delim) + *delim = ':'; /* Restore port */ + + host_len = os_strlen(host); + path_len = path ? os_strlen(path) : 0; + alloc_len = host_len + 1 + 1 + path_len + 1; + + 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) + break; + mem = (char *) (a + 1); + a->domain_and_port = mem; + os_memcpy(mem, host, host_len); + mem += host_len + 1; + a->path = mem; + if (path == NULL || path[0] != '/') + *mem++ = '/'; + if (path) + os_memcpy(mem, path, path_len); + 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; + } +} + + +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_reltime now; + + if (dl_list_empty(&sm->subscriptions)) { + /* optimize */ + return; + } + + if (os_get_reltime(&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) +{ + struct upnp_wps_device_interface *iface; + wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s); + subscr_addr_free_all(s); + event_delete_all(s); + dl_list_for_each(iface, &s->sm->interfaces, + struct upnp_wps_device_interface, list) + upnp_er_remove_notification(iface->wps->registrar, 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); + if (wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } + 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; + char str[80]; + + /* 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); + if (s) { + 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; + if (uuid_make(s->uuid) < 0) { + subscription_destroy(s); + return NULL; + } + 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; + } + uuid_bin2str(s->uuid, str, sizeof(str)); + wpa_printf(MSG_DEBUG, + "WPS UPnP: Subscription %p (SID %s) started with %s", + s, str, 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); + ssdp_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; + + 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/freebsd/contrib/wpa/src/wps/wps_upnp.h b/freebsd/contrib/wpa/src/wps/wps_upnp.h new file mode 100644 index 00000000..87b7ab14 --- /dev/null +++ b/freebsd/contrib/wpa/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/freebsd/contrib/wpa/src/wps/wps_upnp_ap.c b/freebsd/contrib/wpa/src/wps/wps_upnp_ap.c new file mode 100644 index 00000000..e705d445 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_upnp_ap.c @@ -0,0 +1,85 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * Wi-Fi Protected Setup - UPnP AP functionality + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "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; + struct wps_registrar *reg = timeout_ctx; + wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out"); + s->selected_registrar = 0; + wps_registrar_selected_registrar_changed(reg, 0); +} + + +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 || + wps_parse_msg(msg, &attr) < 0) + return -1; + + s->reg = reg; + eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg); + + 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) { + wpa_printf(MSG_DEBUG, "WPS: Add broadcast " + "AuthorizedMACs for WPS 1.0 ER"); + os_memset(s->authorized_macs, 0xff, ETH_ALEN); + } + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, + upnp_er_set_selected_timeout, s, reg); + } + + wps_registrar_selected_registrar_changed(reg, 0); + + return 0; +} + + +void upnp_er_remove_notification(struct wps_registrar *reg, + struct subscription *s) +{ + s->selected_registrar = 0; + eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg); + if (reg) + wps_registrar_selected_registrar_changed(reg, 0); +} diff --git a/freebsd/contrib/wpa/src/wps/wps_upnp_event.c b/freebsd/contrib/wpa/src/wps/wps_upnp_event.c new file mode 100644 index 00000000..92a603b0 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_upnp_event.c @@ -0,0 +1,423 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * 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) || + s->current_event || + 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/freebsd/contrib/wpa/src/wps/wps_upnp_i.h b/freebsd/contrib/wpa/src/wps/wps_upnp_i.h new file mode 100644 index 00000000..f289fe68 --- /dev/null +++ b/freebsd/contrib/wpa/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 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, const char *forced_ifname); +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 wps_registrar *reg, + struct subscription *s); + +#endif /* WPS_UPNP_I_H */ diff --git a/freebsd/contrib/wpa/src/wps/wps_upnp_ssdp.c b/freebsd/contrib/wpa/src/wps/wps_upnp_ssdp.c new file mode 100644 index 00000000..ed972494 --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_upnp_ssdp.c @@ -0,0 +1,950 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * 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-2013, 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> +#ifdef __linux__ +#include <net/if.h> +#endif /* __linux__ */ + +#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; +} + + +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); + if (!iface) + return NULL; + uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string)); + msg = wpabuf_alloc(800); /* more than big enough */ + if (msg == NULL) + return NULL; + 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; +} + + +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; + if (os_get_random((void *) &r, sizeof(r)) < 0) + r = 32768; + 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 */ + 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)) { + 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); + if (!iface) + continue; + 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 || + fcntl(sd, F_SETFL, O_NONBLOCK) != 0 || + 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)) || + 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, const char *forced_ifname) +{ + 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 (forced_ifname) { +#ifdef __linux__ + struct ifreq req; + os_memset(&req, 0, sizeof(req)); + os_strlcpy(req.ifr_name, forced_ifname, sizeof(req.ifr_name)); + if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &req, + sizeof(req)) < 0) { + wpa_printf(MSG_INFO, "WPS UPnP: Failed to bind " + "multicast socket to ifname %s: %s", + forced_ifname, strerror(errno)); + close(sd); + return -1; + } +#endif /* __linux__ */ + } + +#if 0 /* maybe ok if we sometimes block on writes */ + if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) { + close(sd); + 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)); + close(sd); + 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)); + close(sd); + 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)); + close(sd); + 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, NULL); + if (sm->multicast_sd < 0) + return -1; + return 0; +} diff --git a/freebsd/contrib/wpa/src/wps/wps_upnp_web.c b/freebsd/contrib/wpa/src/wps/wps_upnp_web.c new file mode 100644 index 00000000..8c3bbd1b --- /dev/null +++ b/freebsd/contrib/wpa/src/wps/wps_upnp_web.c @@ -0,0 +1,1352 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * 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_interface *iface, + struct upnp_wps_device_sm *sm, + struct wpabuf *buf) +{ + const char *s; + char uuid_string[80]; + + wpabuf_put_str(buf, wps_device_xml_prefix); + + /* + * Add required fields with default values if not configured. Add + * optional and recommended fields only if configured. + */ + s = 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); + if (iface == NULL) { + http_request_deinit(hreq); + return; + } + + /* + * It is not required that filenames be case insensitive but it is + * allowed and cannot hurt here. + */ + 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(iface, 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); + + wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo"); + + if (!iface || iface->ctx->ap_pin == NULL) + return HTTP_INTERNAL_SERVER_ERROR; + + peer = &iface->peer; + + /* + * 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); + if (!iface) + return HTTP_INTERNAL_SERVER_ERROR; + + /* + * 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'); + + while (end) { + /* 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 renewal. + **/ + 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 = dup_binstr(h, len); + if (callback_urls == NULL) { + ret = HTTP_INTERNAL_SERVER_ERROR; + goto error; + } + if (len > 0 && callback_urls[len - 1] == '\r') + callback_urls[len - 1] = '\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'); + + while (end) { + /* 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 + 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; + } + + match = "NT:"; + match_len = os_strlen(match); + if (os_strncasecmp(h, match, match_len) == 0) { + ret = HTTP_BAD_REQUEST; + goto send_msg; + } + + match = "CALLBACK:"; + match_len = os_strlen(match); + if (os_strncasecmp(h, match, match_len) == 0) { + ret = HTTP_BAD_REQUEST; + goto send_msg; + } + } + + if (got_uuid) { + char str[80]; + + uuid_bin2str(uuid, str, sizeof(str)); + + 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 (SID %s) %s", + s, str, (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: Could not find matching subscription to unsubscribe (SID %s)", + str); + ret = HTTP_PRECONDITION_FAILED; + goto send_msg; + } + } 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; +} |