diff options
| author | Dmitry Shmidt <dimitrysh@google.com> | 2013-07-11 10:46:32 -0700 |
|---|---|---|
| committer | Dmitry Shmidt <dimitrysh@google.com> | 2013-07-11 11:25:16 -0700 |
| commit | 34af306c42b7ccf956508e7cd23f0ba90606e360 (patch) | |
| tree | 7d4c227be85be82637fa5f13f618488cc356f337 /src | |
| parent | 8bae4138a0356709720a96f3e50b4d734e532c12 (diff) | |
| download | android_external_wpa_supplicant_8-34af306c42b7ccf956508e7cd23f0ba90606e360.tar.gz android_external_wpa_supplicant_8-34af306c42b7ccf956508e7cd23f0ba90606e360.tar.bz2 android_external_wpa_supplicant_8-34af306c42b7ccf956508e7cd23f0ba90606e360.zip | |
Accumulative patch from commit 1075b2957169d8f9d6dddd7679339c751dc9515b
1075b29 P2P: Report group formation failure on error to start GO mode
b62b29e Do not block on ctrl_iface monitor events
eb7ddbf WPS: Stop SSDP service before freeing the pending entries
98cbc0a Remove forgotten Xcode defines
0b9d3b2 Interworking: Relax 3GPP info PLMN matching for MNC
c7a67a7 WPS: Disconnect when removing existing WPS network block
fe65847 EAP-EKE: Add server implementation
7e7610d EAP-EKE: Add peer implementation
489202d EAP-SAKE: Use configured server identity
a607b42 EAP-PSK: Use configured server identity
15b042b EAP-MSCHAPv2: Use configured server identity
162865b EAP-IKEv2 server: Use configured server identity
8f89d82 EAP-GPSK server: Use configured server identity
67fe933 Add server identity configuration for EAP server
06aeff5 dbus: Register the AutoScan method call at the right place
78f79fe P2P: Do not add ctrl interface for P2P_DEVICE (p2p-dev-*)
1c42b42 P2P: Fix TDLS and l2_packet init without P2P Device interface
9e6a321 Fix non-P2P build after the P2P_DEVICE changes
d53d259 Fix build with older OpenSSL versions
54d4ba4 nl80211: Silence a compiler warning with older gcc versions
2e5ba4b P2P: Derive group interface name bit more sensibly
c68f620 P2P: Create P2P Device interface if supported
851b0c5 nl80211: Do not indicate P2P_DEVICE support by default
bb4028f P2P: Ignore p2p_no_group_iface when driver advertizes P2P_DEVICE support
7940c79 nl80211: Use wdev id when cancelling wait for frame using P2P_DEVICE
f608081 nl80211: Verify P2P GO/client address with all interface addresses
5fbcb45 nl80211: Fix determining phy name for P2P Device
27ce1d6 nl80211: Fix nl80211_get_wiphy_index() for P2P Device
080585c Add support for OCSP stapling to validate server certificate
72950ed P2P: Remove a call to wpas_p2p_deinit_global()
ab7a1ad nl80211: Fix P2P group interface creating using P2P Device
fa93de4 nl80211: Use wdev_id in nl80211_create_iface_once()
fdc554b nl80211: Use wdev id to obtain P2P Device scan results
597b94f nl80211: Add .get_mac_addr() callback for P2P Device
8e12685 nl80211: Rework setting interface mode
91724d6 nl80211: Introduce i802_set_iface_flags()
eb4582f nl80211: Remove P2P Device interface upon .deinit()
f632e48 nl80211: Fix P2P Device interface initialization
e472e1b nl80211: Handle creation of P2P Device interface
01517c8 nl80211: Allow Android P2P functionality
6bae92e nl80211: Add support for P2P Device in add interface
d6dcfcd nl80211: Add a handler to create_interface
d3aaef8 nl80211: Hold wdev identification for P2P Device
7aad838 nl80211: Identify if nl80211 is capable of P2P Device abstraction
6a71413 nl80211: Rename is_p2p_interface
8393e1a nl80211: Print interface name on set_key()
80ebfd9 VLAN: Avoid access to non-existing interfaces
4345fe9 bridge: Track inter-BSS usage
459eee9 bridge: Use safe default bridge interface
2aaeedf bridge: Give bridge name in per-bss configuration
8a901d7 D-Bus: Emit signal when a station is authorized or deauthorized
9578329 Add AVG_RSSI report in signal_poll
2cc8d8f Add bandwidth and center freq info to signal_poll
1e0e943 Remove 802.11b rates only in case of P2P group operation
2090a0b nl80211: Add prints for kernel events
8743676 TDLS: Validate ext_supp_rates in copy_supp_rates
85b4eac P2P: Do not reply to 802.11b-only Probe Request frames as GO
ec7b97a Interworking: Add support for using eap_proxy offload
4331263 Fix session timeout after ANQP dummy STA entry with SME-in-driver
56cb4e1 wpadebug: Add option to ignore SSL errors
aa20e1a Remove CONFIG_NO_WPA2 build parameter
5d5c4ee Remove compiler warnings with CONFIG_NO_SCAN_PROCESSING
9aaa695 Remove compiler warnings if TDLS is enabled without WPA2
84ae1d4 Fix WNM build without WPA2
c33d5eb Fix build without WPA2 or EAP
1aef400 IBSS RSN: Implement disconnect() callback using sta_deauth()
Change-Id: I4593be5b1478f6532da917423b1d2afa95fb8020
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
Diffstat (limited to 'src')
48 files changed, 3612 insertions, 297 deletions
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 7ab86fca..fbc1ee05 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -440,6 +440,7 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->server_cert); os_free(conf->private_key); os_free(conf->private_key_passwd); + os_free(conf->ocsp_stapling_response); os_free(conf->dh_file); os_free(conf->pac_opaque_encr_key); os_free(conf->eap_fast_a_id); @@ -531,6 +532,8 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) wpabuf_free(conf->vendor_elements); os_free(conf->sae_groups); + + os_free(conf->server_id); } @@ -606,11 +609,23 @@ int hostapd_rate_found(int *list, int rate) } -const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) +int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id) { struct hostapd_vlan *v = vlan; while (v) { if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD) + return 1; + v = v->next; + } + return 0; +} + + +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) +{ + struct hostapd_vlan *v = vlan; + while (v) { + if (v->vlan_id == vlan_id) return v->ifname; v = v->next; } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 7c9ea907..a744ba62 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -180,6 +180,7 @@ struct hostapd_nai_realm_data { 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; @@ -294,6 +295,7 @@ struct hostapd_bss_config { char *private_key; char *private_key_passwd; int check_crl; + char *ocsp_stapling_response; char *dh_file; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; @@ -373,6 +375,7 @@ struct hostapd_bss_config { 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) @@ -546,6 +549,7 @@ int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, const u8 *addr, const u8 *prev_psk); int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); +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 * diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index d66d97e4..68ad4dc5 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -111,6 +111,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) srv.eap_req_id_text = conf->eap_req_id_text; srv.eap_req_id_text_len = conf->eap_req_id_text_len; srv.pwd_group = conf->pwd_group; + srv.server_id = conf->server_id ? conf->server_id : "hostapd"; #ifdef CONFIG_RADIUS_TEST srv.dump_msk_file = conf->dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ @@ -148,6 +149,8 @@ int authsrv_init(struct hostapd_data *hapd) params.private_key = hapd->conf->private_key; params.private_key_passwd = hapd->conf->private_key_passwd; params.dh_file = hapd->conf->dh_file; + params.ocsp_stapling_response = + hapd->conf->ocsp_stapling_response; if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 0ef307d3..2f4ba23a 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -485,6 +485,17 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_GROUP_OWNER) && + supp_rates_11b_only(&elems)) { + /* Indicates support for 11b rates only */ + wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from " + MACSTR " with only 802.11b rates", + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_P2P */ + /* TODO: verify that supp_rates contains at least one matching rate * with AP configuration */ diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 07fd11db..fa4b5e4b 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -85,6 +85,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta = ap_get_sta(hapd, addr); if (sta) { + ap_sta_no_session_timeout(hapd, sta); accounting_sta_stop(hapd, sta); /* diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 55f6dd84..75d9c66a 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -25,6 +25,7 @@ enum wps_event; union wps_event_data; struct hostapd_iface; +struct hostapd_dynamic_iface; struct hapd_interfaces { int (*reload_config)(struct hostapd_iface *iface); @@ -37,11 +38,13 @@ struct hapd_interfaces { int (*driver_init)(struct hostapd_iface *iface); size_t count; + size_t count_dynamic; int global_ctrl_sock; char *global_iface_path; char *global_iface_name; gid_t ctrl_iface_group; struct hostapd_iface **iface; + struct hostapd_dynamic_iface **dynamic_iface; }; @@ -275,6 +278,16 @@ struct hostapd_iface { void (*scan_cb)(struct hostapd_iface *iface); }; +/** + * struct hostapd_dynamic_iface - hostapd per dynamically allocated + * or added interface data structure + */ +struct hostapd_dynamic_iface { + char parent[IFNAMSIZ + 1]; + char iface[IFNAMSIZ + 1]; + unsigned int usage; +}; + /* hostapd.c */ int hostapd_for_each_interface(struct hapd_interfaces *interfaces, int (*cb)(struct hostapd_iface *iface, diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 2e570c09..5503af15 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -650,8 +650,7 @@ static void handle_auth(struct hostapd_data *hapd, } if (vlan_id > 0) { - if (hostapd_get_vlan_id_ifname(hapd->conf->vlan, - vlan_id) == NULL) { + if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Invalid VLAN ID " "%d received from RADIUS server", diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index a28c0f85..3554e8b5 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1438,8 +1438,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, sta->vlan_id = radius_msg_get_vlanid(msg); } if (sta->vlan_id > 0 && - hostapd_get_vlan_id_ifname(hapd->conf->vlan, - sta->vlan_id)) { + hostapd_vlan_id_valid(hapd->conf->vlan, sta->vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, @@ -1829,6 +1828,13 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.fragment_size = hapd->conf->fragment_size; conf.pwd_group = hapd->conf->pwd_group; conf.pbc_in_m1 = hapd->conf->pbc_in_m1; + if (hapd->conf->server_id) { + conf.server_id = (const u8 *) hapd->conf->server_id; + conf.server_id_len = os_strlen(hapd->conf->server_id); + } else { + conf.server_id = (const u8 *) "hostapd"; + conf.server_id_len = 7; + } os_memset(&cb, 0, sizeof(cb)); cb.eapol_send = ieee802_1x_eapol_send; diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index 6390e8bd..70affda2 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -480,6 +480,123 @@ static int vlan_set_name_type(unsigned int name_type) #endif /* CONFIG_VLAN_NETLINK */ +/** + * Increase the usage counter for given parent/ifname combination. + * If create is set, then this iface is added to the global list. + * Returns + * -1 on error + * 0 if iface is not in list + * 1 if iface is in list (was there or has been added) + */ +static int hapd_get_dynamic_iface(const char *parent, const char *ifname, + int create, struct hostapd_data *hapd) +{ + size_t i; + struct hostapd_dynamic_iface *j = NULL, **tmp; + struct hapd_interfaces *hapd_global = hapd->iface->interfaces; + + if (!parent) + parent = ""; + + for (i = 0; i < hapd_global->count_dynamic; i++) { + j = hapd_global->dynamic_iface[i]; + if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 && + os_strncmp(j->parent, parent, sizeof(j->parent)) == 0) + break; + } + if (i < hapd_global->count_dynamic) { + j->usage++; + return 1; + } + + /* new entry required */ + if (!create) + return 0; + + j = os_zalloc(sizeof(*j)); + if (!j) + return -1; + os_strlcpy(j->iface, ifname, sizeof(j->iface)); + os_strlcpy(j->parent, parent, sizeof(j->parent)); + + tmp = os_realloc_array(hapd_global->dynamic_iface, i + 1, + sizeof(*hapd_global->dynamic_iface)); + if (!tmp) { + wpa_printf(MSG_ERROR, "VLAN: Failed to allocate memory in %s", + __func__); + return -1; + } + hapd_global->count_dynamic++; + hapd_global->dynamic_iface = tmp; + hapd_global->dynamic_iface[i] = j; + + return 1; +} + + +/** + * Decrease the usage counter for given ifname. + * Returns + * -1 on error or if iface was not found + * 0 if iface was found and is still present + * 1 if iface was removed from global list + */ +static int hapd_put_dynamic_iface(const char *parent, const char *ifname, + struct hostapd_data *hapd) +{ + size_t i; + struct hostapd_dynamic_iface *j = NULL, **tmp; + struct hapd_interfaces *hapd_glob = hapd->iface->interfaces; + + if (!parent) + parent = ""; + + for (i = 0; i < hapd_glob->count_dynamic; i++) { + j = hapd_glob->dynamic_iface[i]; + if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 && + os_strncmp(j->parent, parent, sizeof(j->parent)) == 0) + break; + } + + if (i == hapd_glob->count_dynamic) { + /* + * Interface not in global list. This can happen if alloc in + * _get_ failed. + */ + return -1; + } + + if (j->usage > 0) { + j->usage--; + return 0; + } + + os_free(j); + for (; i < hapd_glob->count_dynamic - 1; i++) + hapd_glob->dynamic_iface[i] = hapd_glob->dynamic_iface[i + 1]; + hapd_glob->dynamic_iface[hapd_glob->count_dynamic - 1] = NULL; + hapd_glob->count_dynamic--; + + if (hapd_glob->count_dynamic == 0) { + os_free(hapd_glob->dynamic_iface); + hapd_glob->dynamic_iface = NULL; + return 1; + } + + tmp = os_realloc_array(hapd_glob->dynamic_iface, + hapd_glob->count_dynamic, + sizeof(*hapd_glob->dynamic_iface)); + if (!tmp) { + wpa_printf(MSG_ERROR, "VLAN: Failed to release memory in %s", + __func__); + return -1; + } + hapd_glob->dynamic_iface = tmp; + + return 1; +} + + static void vlan_newlink(char *ifname, struct hostapd_data *hapd) { char vlan_ifname[IFNAMSIZ]; @@ -487,16 +604,29 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) struct hostapd_vlan *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; int vlan_naming = hapd->conf->ssid.vlan_naming; + int ret; wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); while (vlan) { if (os_strcmp(ifname, vlan->ifname) == 0) { - os_snprintf(br_name, sizeof(br_name), "brvlan%d", - vlan->vlan_id); + if (hapd->conf->vlan_bridge[0]) { + os_snprintf(br_name, sizeof(br_name), "%s%d", + hapd->conf->vlan_bridge, + vlan->vlan_id); + } else if (tagged_interface) { + os_snprintf(br_name, sizeof(br_name), + "br%s.%d", tagged_interface, + vlan->vlan_id); + } else { + os_snprintf(br_name, sizeof(br_name), + "brvlan%d", vlan->vlan_id); + } - if (!br_addbr(br_name)) + ret = br_addbr(br_name); + if (hapd_get_dynamic_iface(NULL, br_name, ret == 0, + hapd)) vlan->clean |= DVLAN_CLEAN_BR; ifconfig_up(br_name); @@ -514,17 +644,24 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) "vlan%d", vlan->vlan_id); ifconfig_up(tagged_interface); - if (!vlan_add(tagged_interface, vlan->vlan_id, - vlan_ifname)) + ret = vlan_add(tagged_interface, vlan->vlan_id, + vlan_ifname); + if (hapd_get_dynamic_iface(NULL, vlan_ifname, + ret == 0, hapd)) vlan->clean |= DVLAN_CLEAN_VLAN; - if (!br_addif(br_name, vlan_ifname)) + ret = br_addif(br_name, vlan_ifname); + if (hapd_get_dynamic_iface(br_name, + vlan_ifname, + ret == 0, hapd)) vlan->clean |= DVLAN_CLEAN_VLAN_PORT; ifconfig_up(vlan_ifname); } - if (!br_addif(br_name, ifname)) + ret = br_addif(br_name, ifname); + if (hapd_get_dynamic_iface(br_name, ifname, ret == 0, + hapd)) vlan->clean |= DVLAN_CLEAN_WLAN_PORT; ifconfig_up(ifname); @@ -550,10 +687,21 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) while (vlan) { if (os_strcmp(ifname, vlan->ifname) == 0) { - os_snprintf(br_name, sizeof(br_name), "brvlan%d", - vlan->vlan_id); + if (hapd->conf->vlan_bridge[0]) { + os_snprintf(br_name, sizeof(br_name), "%s%d", + hapd->conf->vlan_bridge, + vlan->vlan_id); + } else if (tagged_interface) { + os_snprintf(br_name, sizeof(br_name), + "br%s.%d", tagged_interface, + vlan->vlan_id); + } else { + os_snprintf(br_name, sizeof(br_name), + "brvlan%d", vlan->vlan_id); + } - if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) + if ((vlan->clean & DVLAN_CLEAN_WLAN_PORT) && + hapd_put_dynamic_iface(br_name, vlan->ifname, hapd)) br_delif(br_name, vlan->ifname); if (tagged_interface) { @@ -567,15 +715,20 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vlan->vlan_id); - if (vlan->clean & DVLAN_CLEAN_VLAN_PORT) + if ((vlan->clean & DVLAN_CLEAN_VLAN_PORT) && + hapd_put_dynamic_iface(br_name, vlan_ifname, + hapd)) br_delif(br_name, vlan_ifname); ifconfig_down(vlan_ifname); - if (vlan->clean & DVLAN_CLEAN_VLAN) + if ((vlan->clean & DVLAN_CLEAN_VLAN) && + hapd_put_dynamic_iface(NULL, vlan_ifname, + hapd)) vlan_rem(vlan_ifname); } if ((vlan->clean & DVLAN_CLEAN_BR) && + hapd_put_dynamic_iface(NULL, br_name, hapd) && br_getnumports(br_name) == 0) { ifconfig_down(br_name); br_delbr(br_name); diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 407b066c..aab8ac61 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -512,3 +512,36 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) return mode; } + + +static int is_11b(u8 rate) +{ + return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; +} + + +int supp_rates_11b_only(struct ieee802_11_elems *elems) +{ + int num_11b = 0, num_others = 0; + int i; + + if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL) + return 0; + + for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) { + if (is_11b(elems->supp_rates[i])) + num_11b++; + else + num_others++; + } + + for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len; + i++) { + if (is_11b(elems->ext_supp_rates[i])) + num_11b++; + else + num_others++; + } + + return num_11b > 0 && num_others == 0; +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 7f59cf25..68c6b96f 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -101,4 +101,6 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], const char *name, const char *val); enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel); +int supp_rates_11b_only(struct ieee802_11_elems *elems); + #endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index a8cf6be8..c3afbfd3 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -335,7 +335,6 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, #endif /* CONFIG_IEEE80211R */ -#ifndef CONFIG_NO_WPA2 static int rsn_selector_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) @@ -384,7 +383,6 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) #endif /* CONFIG_SAE */ return 0; } -#endif /* CONFIG_NO_WPA2 */ /** @@ -397,7 +395,6 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data) { -#ifndef CONFIG_NO_WPA2 const struct rsn_ie_hdr *hdr; const u8 *pos; int left; @@ -551,9 +548,6 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, } return 0; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } diff --git a/src/crypto/tls.h b/src/crypto/tls.h index b61e4393..2fdaa026 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -1,6 +1,6 @@ /* * SSL/TLS interface definition - * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> + * 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. @@ -82,6 +82,8 @@ struct tls_config { #define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0) #define TLS_CONN_DISABLE_TIME_CHECKS BIT(1) #define TLS_CONN_DISABLE_SESSION_TICKET BIT(2) +#define TLS_CONN_REQUEST_OCSP BIT(3) +#define TLS_CONN_REQUIRE_OCSP BIT(4) /** * struct tls_connection_params - Parameters for TLS connection @@ -117,6 +119,8 @@ struct tls_config { * @cert_id: the certificate's id when using engine * @ca_cert_id: the CA certificate's id when using engine * @flags: Parameter options (TLS_CONN_*) + * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response + * or %NULL if OCSP is not enabled * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -153,6 +157,7 @@ struct tls_connection_params { const char *ca_cert_id; unsigned int flags; + const char *ocsp_stapling_response; }; diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index fec8e385..90acf84b 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -1,6 +1,6 @@ /* * SSL/TLS interface functions for OpenSSL - * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> + * 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. @@ -65,6 +65,13 @@ static BIO * BIO_from_keystore(const char *key) } #endif /* ANDROID */ +#ifdef SSL_set_tlsext_status_type +#ifndef OPENSSL_NO_TLSEXT +#define HAVE_OCSP +#include <openssl/ocsp.h> +#endif /* OPENSSL_NO_TLSEXT */ +#endif /* SSL_set_tlsext_status_type */ + static int tls_openssl_ref_count = 0; struct tls_context { @@ -72,6 +79,7 @@ struct tls_context { union tls_event_data *data); void *cb_ctx; int cert_in_cb; + char *ocsp_stapling_response; }; static struct tls_context *tls_global = NULL; @@ -102,6 +110,9 @@ struct tls_connection { u8 srv_cert_hash[32]; unsigned int flags; + + X509 *peer_cert; + X509 *peer_issuer; }; @@ -841,6 +852,8 @@ void tls_deinit(void *ssl_ctx) ERR_remove_state(0); ERR_free_strings(); EVP_cleanup(); + os_free(tls_global->ocsp_stapling_response); + tls_global->ocsp_stapling_response = NULL; os_free(tls_global); tls_global = NULL; } @@ -1267,6 +1280,12 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) conn = SSL_get_app_data(ssl); if (conn == NULL) return 0; + + if (depth == 0) + conn->peer_cert = err_cert; + else if (depth == 1) + conn->peer_issuer = err_cert; + context = conn->context; match = conn->subject_match; altmatch = conn->altsubject_match; @@ -2768,11 +2787,187 @@ int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) } +#ifdef HAVE_OCSP + +static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + extern int wpa_debug_level; + BIO *out; + size_t rlen; + char *txt; + int res; + + if (wpa_debug_level > MSG_DEBUG) + return; + + out = BIO_new(BIO_s_mem()); + if (!out) + return; + + OCSP_RESPONSE_print(out, rsp, 0); + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (!txt) { + BIO_free(out); + return; + } + + res = BIO_read(out, txt, rlen); + if (res > 0) { + txt[res] = '\0'; + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP Response\n%s", txt); + } + os_free(txt); + BIO_free(out); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static int ocsp_resp_cb(SSL *s, void *arg) +{ + struct tls_connection *conn = arg; + const unsigned char *p; + int len, status, reason; + OCSP_RESPONSE *rsp; + OCSP_BASICRESP *basic; + OCSP_CERTID *id; + ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update; + + len = SSL_get_tlsext_status_ocsp_resp(s, &p); + if (!p) { + wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received"); + return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; + } + + wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len); + + rsp = d2i_OCSP_RESPONSE(NULL, &p, len); + if (!rsp) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response"); + return 0; + } + + ocsp_debug_print_resp(rsp); + + status = OCSP_response_status(rsp); + if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)", + status, OCSP_response_status_str(status)); + return 0; + } + + basic = OCSP_response_get1_basic(rsp); + if (!basic) { + wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse"); + return 0; + } + + status = OCSP_basic_verify(basic, NULL, SSL_CTX_get_cert_store(s->ctx), + 0); + if (status <= 0) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: OCSP response failed verification"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded"); + + if (!conn->peer_cert || !conn->peer_issuer) { + wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate or issue certificate not available for OCSP status check"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); + if (!id) { + wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, + &this_update, &next_update)) { + wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", + (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" : + " (OCSP not required)"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; + } + + if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: OCSP status times invalid"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s", + OCSP_cert_status_str(status)); + + if (status == V_OCSP_CERTSTATUS_GOOD) + return 1; + if (status == V_OCSP_CERTSTATUS_REVOKED) + return 0; + if (conn->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required"); + return 0; + } + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue"); + return 1; +} + + +static int ocsp_status_cb(SSL *s, void *arg) +{ + char *tmp; + char *resp; + size_t len; + + if (tls_global->ocsp_stapling_response == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - no response configured"); + return SSL_TLSEXT_ERR_OK; + } + + resp = os_readfile(tls_global->ocsp_stapling_response, &len); + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - could not read response file"); + /* TODO: Build OCSPResponse with responseStatus = internalError + */ + return SSL_TLSEXT_ERR_OK; + } + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - send cached response"); + tmp = OPENSSL_malloc(len); + if (tmp == NULL) { + os_free(resp); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + os_memcpy(tmp, resp, len); + os_free(resp); + SSL_set_tlsext_status_ocsp_resp(s, tmp, len); + + return SSL_TLSEXT_ERR_OK; +} + +#endif /* HAVE_OCSP */ + + int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { int ret; unsigned long err; + SSL_CTX *ssl_ctx = tls_ctx; if (conn == NULL) return -1; @@ -2836,10 +3031,20 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_set_options(conn->ssl, SSL_OP_NO_TICKET); +#ifdef SSL_clear_options else SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET); +#endif /* SSL_clear_options */ #endif /* SSL_OP_NO_TICKET */ +#ifdef HAVE_OCSP + if (params->flags & TLS_CONN_REQUEST_OCSP) { + SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp); + SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); + SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn); + } +#endif /* HAVE_OCSP */ + conn->flags = params->flags; tls_get_errors(tls_ctx); @@ -2878,10 +3083,23 @@ int tls_global_set_params(void *tls_ctx, #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); +#ifdef SSL_CTX_clear_options else SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET); +#endif /* SSL_clear_options */ #endif /* SSL_OP_NO_TICKET */ +#ifdef HAVE_OCSP + SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_status_cb); + SSL_CTX_set_tlsext_status_arg(ssl_ctx, ssl_ctx); + os_free(tls_global->ocsp_stapling_response); + if (params->ocsp_stapling_response) + tls_global->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + else + tls_global->ocsp_stapling_response = NULL; +#endif /* HAVE_OCSP */ + return 0; } diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 0f92b189..0604fefb 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -830,7 +830,7 @@ struct wpa_driver_capa { * it cannot be used for P2P group operations or non-P2P purposes. */ #define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400 -/* This interface is P2P capable (P2P Device, GO, or P2P Client */ +/* This interface is P2P capable (P2P GO or P2P Client) */ #define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800 /* Driver supports concurrent operations on multiple channels */ #define WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT 0x00001000 @@ -874,6 +874,8 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_IBSS 0x08000000 /* Driver supports radar detection */ #define WPA_DRIVER_FLAGS_RADAR 0x10000000 +/* Driver supports a dedicated interface for P2P Device */ +#define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE 0x20000000 unsigned int flags; int max_scan_ssids; @@ -1013,7 +1015,13 @@ enum wpa_driver_if_type { * 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_GROUP, + + /** + * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the + * abstracted P2P Device function in the driver + */ + WPA_IF_P2P_DEVICE }; struct wpa_init_params { @@ -1090,6 +1098,17 @@ enum wnm_oper { 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 */ @@ -1097,8 +1116,12 @@ struct wpa_signal_info { u32 frequency; int above_threshold; int current_signal; + int avg_signal; int current_noise; int current_txrate; + enum chan_width chanwidth; + int center_frq1; + int center_frq2; }; /** diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index cbc80e3b..bcd0a94d 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -157,6 +157,8 @@ static void nl_destroy_handles(struct nl_handle **handle) 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; @@ -183,12 +185,14 @@ struct i802_bss { struct wpa_driver_nl80211_data *drv; struct i802_bss *next; int ifindex; + u64 wdev_id; char ifname[IFNAMSIZ + 1]; char brname[IFNAMSIZ]; unsigned int beacon_set:1; unsigned int added_if_into_bridge:1; unsigned int added_bridge:1; unsigned int in_deinit:1; + unsigned int wdev_id_set:1; u8 addr[ETH_ALEN]; @@ -249,6 +253,7 @@ struct wpa_driver_nl80211_data { unsigned int retry_auth:1; unsigned int use_monitor:1; unsigned int ignore_next_local_disconnect:1; + unsigned int allow_p2p_device:1; u64 remain_on_chan_cookie; u64 send_action_cookie; @@ -357,6 +362,117 @@ static int wpa_driver_nl80211_authenticate_retry( struct wpa_driver_nl80211_data *drv); +static const char * nl80211_command_to_string(enum nl80211_commands cmd) +{ +#define C2S(x) case x: return #x; + switch (cmd) { + C2S(NL80211_CMD_UNSPEC) + C2S(NL80211_CMD_GET_WIPHY) + C2S(NL80211_CMD_SET_WIPHY) + C2S(NL80211_CMD_NEW_WIPHY) + C2S(NL80211_CMD_DEL_WIPHY) + C2S(NL80211_CMD_GET_INTERFACE) + C2S(NL80211_CMD_SET_INTERFACE) + C2S(NL80211_CMD_NEW_INTERFACE) + C2S(NL80211_CMD_DEL_INTERFACE) + C2S(NL80211_CMD_GET_KEY) + C2S(NL80211_CMD_SET_KEY) + C2S(NL80211_CMD_NEW_KEY) + C2S(NL80211_CMD_DEL_KEY) + C2S(NL80211_CMD_GET_BEACON) + C2S(NL80211_CMD_SET_BEACON) + C2S(NL80211_CMD_START_AP) + C2S(NL80211_CMD_STOP_AP) + C2S(NL80211_CMD_GET_STATION) + C2S(NL80211_CMD_SET_STATION) + C2S(NL80211_CMD_NEW_STATION) + C2S(NL80211_CMD_DEL_STATION) + C2S(NL80211_CMD_GET_MPATH) + C2S(NL80211_CMD_SET_MPATH) + C2S(NL80211_CMD_NEW_MPATH) + C2S(NL80211_CMD_DEL_MPATH) + C2S(NL80211_CMD_SET_BSS) + C2S(NL80211_CMD_SET_REG) + C2S(NL80211_CMD_REQ_SET_REG) + C2S(NL80211_CMD_GET_MESH_CONFIG) + C2S(NL80211_CMD_SET_MESH_CONFIG) + C2S(NL80211_CMD_SET_MGMT_EXTRA_IE) + C2S(NL80211_CMD_GET_REG) + C2S(NL80211_CMD_GET_SCAN) + C2S(NL80211_CMD_TRIGGER_SCAN) + C2S(NL80211_CMD_NEW_SCAN_RESULTS) + C2S(NL80211_CMD_SCAN_ABORTED) + C2S(NL80211_CMD_REG_CHANGE) + C2S(NL80211_CMD_AUTHENTICATE) + C2S(NL80211_CMD_ASSOCIATE) + C2S(NL80211_CMD_DEAUTHENTICATE) + C2S(NL80211_CMD_DISASSOCIATE) + C2S(NL80211_CMD_MICHAEL_MIC_FAILURE) + C2S(NL80211_CMD_REG_BEACON_HINT) + C2S(NL80211_CMD_JOIN_IBSS) + C2S(NL80211_CMD_LEAVE_IBSS) + C2S(NL80211_CMD_TESTMODE) + C2S(NL80211_CMD_CONNECT) + C2S(NL80211_CMD_ROAM) + C2S(NL80211_CMD_DISCONNECT) + C2S(NL80211_CMD_SET_WIPHY_NETNS) + C2S(NL80211_CMD_GET_SURVEY) + C2S(NL80211_CMD_NEW_SURVEY_RESULTS) + C2S(NL80211_CMD_SET_PMKSA) + C2S(NL80211_CMD_DEL_PMKSA) + C2S(NL80211_CMD_FLUSH_PMKSA) + C2S(NL80211_CMD_REMAIN_ON_CHANNEL) + C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL) + C2S(NL80211_CMD_SET_TX_BITRATE_MASK) + C2S(NL80211_CMD_REGISTER_FRAME) + C2S(NL80211_CMD_FRAME) + C2S(NL80211_CMD_FRAME_TX_STATUS) + C2S(NL80211_CMD_SET_POWER_SAVE) + C2S(NL80211_CMD_GET_POWER_SAVE) + C2S(NL80211_CMD_SET_CQM) + C2S(NL80211_CMD_NOTIFY_CQM) + C2S(NL80211_CMD_SET_CHANNEL) + C2S(NL80211_CMD_SET_WDS_PEER) + C2S(NL80211_CMD_FRAME_WAIT_CANCEL) + C2S(NL80211_CMD_JOIN_MESH) + C2S(NL80211_CMD_LEAVE_MESH) + C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE) + C2S(NL80211_CMD_UNPROT_DISASSOCIATE) + C2S(NL80211_CMD_NEW_PEER_CANDIDATE) + C2S(NL80211_CMD_GET_WOWLAN) + C2S(NL80211_CMD_SET_WOWLAN) + C2S(NL80211_CMD_START_SCHED_SCAN) + C2S(NL80211_CMD_STOP_SCHED_SCAN) + C2S(NL80211_CMD_SCHED_SCAN_RESULTS) + C2S(NL80211_CMD_SCHED_SCAN_STOPPED) + C2S(NL80211_CMD_SET_REKEY_OFFLOAD) + C2S(NL80211_CMD_PMKSA_CANDIDATE) + C2S(NL80211_CMD_TDLS_OPER) + C2S(NL80211_CMD_TDLS_MGMT) + C2S(NL80211_CMD_UNEXPECTED_FRAME) + C2S(NL80211_CMD_PROBE_CLIENT) + C2S(NL80211_CMD_REGISTER_BEACONS) + C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME) + C2S(NL80211_CMD_SET_NOACK_MAP) + C2S(NL80211_CMD_CH_SWITCH_NOTIFY) + C2S(NL80211_CMD_START_P2P_DEVICE) + C2S(NL80211_CMD_STOP_P2P_DEVICE) + C2S(NL80211_CMD_CONN_FAILED) + C2S(NL80211_CMD_SET_MCAST_RATE) + C2S(NL80211_CMD_SET_MAC_ACL) + C2S(NL80211_CMD_RADAR_DETECT) + C2S(NL80211_CMD_GET_PROTOCOL_FEATURES) + C2S(NL80211_CMD_UPDATE_FT_IES) + C2S(NL80211_CMD_FT_EVENT) + C2S(NL80211_CMD_CRIT_PROTOCOL_START) + C2S(NL80211_CMD_CRIT_PROTOCOL_STOP) + default: + return "NL80211_CMD_UNKNOWN"; + } +#undef C2S +} + + static int is_ap_interface(enum nl80211_iftype nlmode) { return (nlmode == NL80211_IFTYPE_AP || @@ -371,7 +487,7 @@ static int is_sta_interface(enum nl80211_iftype nlmode) } -static int is_p2p_interface(enum nl80211_iftype nlmode) +static int is_p2p_net_interface(enum nl80211_iftype nlmode) { return (nlmode == NL80211_IFTYPE_P2P_CLIENT || nlmode == NL80211_IFTYPE_P2P_GO); @@ -491,6 +607,19 @@ struct family_data { }; +static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss) +{ + if (bss->wdev_id_set) + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); + else + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + return 0; + +nla_put_failure: + return -1; +} + + static int family_handler(struct nl_msg *msg, void *arg) { struct family_data *res = arg; @@ -557,6 +686,8 @@ static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, struct wiphy_idx_data { int wiphy_idx; + enum nl80211_iftype nlmode; + u8 *macaddr; }; @@ -572,6 +703,13 @@ static int netdev_info_handler(struct nl_msg *msg, void *arg) if (tb[NL80211_ATTR_WIPHY]) info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + if (tb[NL80211_ATTR_IFTYPE]) + info->nlmode = nla_get_u32(tb[NL80211_ATTR_IFTYPE]); + + if (tb[NL80211_ATTR_MAC] && info->macaddr) + os_memcpy(info->macaddr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + return NL_SKIP; } @@ -581,15 +719,17 @@ static int nl80211_get_wiphy_index(struct i802_bss *bss) struct nl_msg *msg; struct wiphy_idx_data data = { .wiphy_idx = -1, + .macaddr = NULL, }; msg = nlmsg_alloc(); if (!msg) - return -1; + return NL80211_IFTYPE_UNSPECIFIED; nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) return data.wiphy_idx; @@ -600,6 +740,57 @@ nla_put_failure: } +static enum nl80211_iftype nl80211_get_ifmode(struct i802_bss *bss) +{ + struct nl_msg *msg; + struct wiphy_idx_data data = { + .nlmode = NL80211_IFTYPE_UNSPECIFIED, + .macaddr = NULL, + }; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); + + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + + if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) + return data.nlmode; + msg = NULL; +nla_put_failure: + nlmsg_free(msg); + return NL80211_IFTYPE_UNSPECIFIED; +} + + +#ifndef HOSTAPD +static int nl80211_get_macaddr(struct i802_bss *bss) +{ + struct nl_msg *msg; + struct wiphy_idx_data data = { + .macaddr = bss->addr, + }; + + msg = nlmsg_alloc(); + if (!msg) + return NL80211_IFTYPE_UNSPECIFIED; + + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + + return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data); + +nla_put_failure: + nlmsg_free(msg); + return NL80211_IFTYPE_UNSPECIFIED; +} +#endif /* HOSTAPD */ + + static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv, struct nl80211_wiphy_data *w) { @@ -1532,22 +1723,26 @@ static void mlme_event(struct i802_bss *bss, } if (frame == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: MLME event %d without frame " - "data", cmd); + wpa_printf(MSG_DEBUG, + "nl80211: MLME event %d (%s) without frame data", + cmd, nl80211_command_to_string(cmd)); return; } data = nla_data(frame); len = nla_len(frame); if (len < 4 + 2 * ETH_ALEN) { - wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d on %s(" MACSTR - ") - too short", - cmd, bss->ifname, MAC2STR(bss->addr)); + wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" + MACSTR ") - too short", + cmd, nl80211_command_to_string(cmd), bss->ifname, + MAC2STR(bss->addr)); return; } - wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d on %s(" MACSTR ") A1=" - MACSTR " A2=" MACSTR, cmd, bss->ifname, MAC2STR(bss->addr), - MAC2STR(data + 4), MAC2STR(data + 4 + ETH_ALEN)); + wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR + ") A1=" MACSTR " A2=" MACSTR, cmd, + nl80211_command_to_string(cmd), bss->ifname, + MAC2STR(bss->addr), MAC2STR(data + 4), + MAC2STR(data + 4 + ETH_ALEN)); if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) && os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 && os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) { @@ -1780,6 +1975,7 @@ static int get_link_signal(struct nl_msg *msg, void *arg) struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, + [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 }, }; struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { @@ -1802,6 +1998,12 @@ static int get_link_signal(struct nl_msg *msg, void *arg) sig_change->current_signal = (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); + if (sinfo[NL80211_STA_INFO_SIGNAL_AVG]) + sig_change->avg_signal = + (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]); + else + sig_change->avg_signal = 0; + if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, sinfo[NL80211_STA_INFO_TX_BITRATE], @@ -2341,6 +2543,9 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, { struct wpa_driver_nl80211_data *drv = bss->drv; + wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", + cmd, nl80211_command_to_string(cmd), bss->ifname); + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && (cmd == NL80211_CMD_NEW_SCAN_RESULTS || cmd == NL80211_CMD_SCAN_ABORTED)) { @@ -2484,19 +2689,31 @@ static int process_drv_event(struct nl_msg *msg, void *arg) nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); - if (tb[NL80211_ATTR_IFINDEX]) + if (tb[NL80211_ATTR_IFINDEX]) { ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - for (bss = &drv->first_bss; bss; bss = bss->next) { - if (ifidx == -1 || ifidx == bss->ifindex) { - do_process_drv_event(bss, gnlh->cmd, tb); - return NL_SKIP; + for (bss = &drv->first_bss; bss; bss = bss->next) + if (ifidx == -1 || ifidx == bss->ifindex) { + do_process_drv_event(bss, gnlh->cmd, tb); + return NL_SKIP; + } + wpa_printf(MSG_DEBUG, + "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d)", + gnlh->cmd, ifidx); + } else if (tb[NL80211_ATTR_WDEV]) { + u64 wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + wpa_printf(MSG_DEBUG, "nl80211: Process event on P2P device"); + for (bss = &drv->first_bss; bss; bss = bss->next) { + if (bss->wdev_id_set && wdev_id == bss->wdev_id) { + do_process_drv_event(bss, gnlh->cmd, tb); + return NL_SKIP; + } } + wpa_printf(MSG_DEBUG, + "nl80211: Ignored event (cmd=%d) for foreign interface (wdev 0x%llx)", + gnlh->cmd, (long long unsigned int) wdev_id); } - wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d) for foreign " - "interface (ifindex %d)", gnlh->cmd, ifidx); - return NL_SKIP; } @@ -2509,17 +2726,26 @@ static int process_global_event(struct nl_msg *msg, void *arg) struct wpa_driver_nl80211_data *drv, *tmp; int ifidx = -1; struct i802_bss *bss; + u64 wdev_id = 0; + int wdev_id_set = 0; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_IFINDEX]) ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + else if (tb[NL80211_ATTR_WDEV]) { + wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + wdev_id_set = 1; + } dl_list_for_each_safe(drv, tmp, &global->interfaces, struct wpa_driver_nl80211_data, list) { for (bss = &drv->first_bss; bss; bss = bss->next) { - if (ifidx == -1 || ifidx == bss->ifindex) { + if ((ifidx == -1 && !wdev_id_set) || + ifidx == bss->ifindex || + (wdev_id_set && bss->wdev_id_set && + wdev_id == bss->wdev_id)) { do_process_drv_event(bss, gnlh->cmd, tb); return NL_SKIP; } @@ -2539,6 +2765,10 @@ static int process_bss_event(struct nl_msg *msg, void *arg) nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); + wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s", + gnlh->cmd, nl80211_command_to_string(gnlh->cmd), + bss->ifname); + switch (gnlh->cmd) { case NL80211_CMD_FRAME: case NL80211_CMD_FRAME_TX_STATUS: @@ -2699,6 +2929,10 @@ static void wiphy_info_supported_iftypes(struct wiphy_info_data *info, case NL80211_IFTYPE_ADHOC: info->capa->flags |= WPA_DRIVER_FLAGS_IBSS; break; + case NL80211_IFTYPE_P2P_DEVICE: + info->capa->flags |= + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; + break; case NL80211_IFTYPE_P2P_GO: info->p2p_go_supported = 1; break; @@ -2896,6 +3130,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); + if (tb[NL80211_ATTR_WIPHY_NAME]) + os_strncpy(drv->phyname, + nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]), + sizeof(drv->phyname)); if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) capa->max_scan_ssids = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); @@ -2992,7 +3230,8 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex); + if (nl80211_set_iface_id(msg, &drv->first_bss) < 0) + goto nla_put_failure; if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info)) return -1; @@ -3265,39 +3504,6 @@ static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) } -static void nl80211_get_phy_name(struct wpa_driver_nl80211_data *drv) -{ - /* Find phy (radio) to which this interface belongs */ - char buf[90], *pos; - int f, rv; - - drv->phyname[0] = '\0'; - snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name", - drv->first_bss.ifname); - f = open(buf, O_RDONLY); - if (f < 0) { - wpa_printf(MSG_DEBUG, "Could not open file %s: %s", - buf, strerror(errno)); - return; - } - - rv = read(f, drv->phyname, sizeof(drv->phyname) - 1); - close(f); - if (rv < 0) { - wpa_printf(MSG_DEBUG, "Could not read file %s: %s", - buf, strerror(errno)); - return; - } - - drv->phyname[rv] = '\0'; - pos = os_strchr(drv->phyname, '\n'); - if (pos) - *pos = '\0'; - wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s", - drv->first_bss.ifname, drv->phyname); -} - - static void wpa_driver_nl80211_handle_eapol_tx_status(int sock, void *eloop_ctx, void *handle) @@ -3419,8 +3625,6 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, if (nl80211_init_bss(bss)) goto failed; - nl80211_get_phy_name(drv); - rcfg = os_zalloc(sizeof(*rcfg)); if (rcfg == NULL) goto failed; @@ -3491,7 +3695,9 @@ static int nl80211_register_frame(struct i802_bss *bss, nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type); NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); @@ -3728,27 +3934,124 @@ static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx) } +static void nl80211_del_p2pdev(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE); + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + + wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s", + bss->ifname, (long long unsigned int) bss->wdev_id, + strerror(ret)); + +nla_put_failure: + nlmsg_free(msg); +} + + +static int nl80211_set_p2pdev(struct i802_bss *bss, int start) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + if (start) + nl80211_cmd(drv, msg, 0, NL80211_CMD_START_P2P_DEVICE); + else + nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_P2P_DEVICE); + + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + + wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s", + start ? "Start" : "Stop", + bss->ifname, (long long unsigned int) bss->wdev_id, + strerror(ret)); + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int i802_set_iface_flags(struct i802_bss *bss, int up) +{ + enum nl80211_iftype nlmode; + + nlmode = nl80211_get_ifmode(bss); + if (nlmode != NL80211_IFTYPE_P2P_DEVICE) { + return linux_set_iface_flags(bss->drv->global->ioctl_sock, + bss->ifname, up); + } + + /* P2P Device has start/stop which is equivalent */ + return nl80211_set_p2pdev(bss, up); +} + + static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) { +#ifndef HOSTAPD + enum nl80211_iftype nlmode = NL80211_IFTYPE_STATION; +#endif /* HOSTAPD */ struct i802_bss *bss = &drv->first_bss; int send_rfkill_event = 0; + int dynamic_if; drv->ifindex = if_nametoindex(bss->ifname); - drv->first_bss.ifindex = drv->ifindex; + bss->ifindex = drv->ifindex; + bss->wdev_id = drv->global->if_add_wdevid; + bss->wdev_id_set = drv->global->if_add_wdevid_set; + + dynamic_if = drv->ifindex == drv->global->if_add_ifindex; + dynamic_if = dynamic_if || drv->global->if_add_wdevid_set; + drv->global->if_add_wdevid_set = 0; + + if (wpa_driver_nl80211_capa(drv)) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s", + bss->ifname, drv->phyname); #ifndef HOSTAPD + if (dynamic_if) + nlmode = nl80211_get_ifmode(bss); + /* * Make sure the interface starts up in station mode unless this is a * dynamically added interface (e.g., P2P) that was already configured * with proper iftype. */ - if (drv->ifindex != drv->global->if_add_ifindex && - wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Could not configure driver to " - "use managed mode"); + if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not configure driver to use managed mode"); return -1; } + drv->nlmode = nlmode; + + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) { + int ret = nl80211_set_p2pdev(bss, 1); + if (ret < 0) + wpa_printf(MSG_ERROR, "nl80211: Could not start P2P device"); + nl80211_get_macaddr(bss); + return ret; + } if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) { if (rfkill_is_blocked(drv->rfkill)) { @@ -3768,9 +4071,6 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) 1, IF_OPER_DORMANT); #endif /* HOSTAPD */ - if (wpa_driver_nl80211_capa(drv)) - return -1; - if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, bss->addr)) return -1; @@ -3867,10 +4167,13 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - (void) linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); - wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION); - nl80211_mgmt_unsubscribe(bss, "deinit"); - + (void) i802_set_iface_flags(bss, 0); + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) { + wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION); + } else { + nl80211_mgmt_unsubscribe(bss, "deinit"); + nl80211_del_p2pdev(bss); + } nl_cb_put(drv->nl_cb); nl80211_destroy_bss(&drv->first_bss); @@ -3911,7 +4214,7 @@ static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) static struct nl_msg * nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd, - struct wpa_driver_scan_params *params) + struct wpa_driver_scan_params *params, u64 *wdev_id) { struct nl_msg *msg; size_t i; @@ -3922,8 +4225,10 @@ nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd, nl80211_cmd(drv, msg, 0, cmd); - if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) < 0) - goto fail; + if (!wdev_id) + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + else + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, *wdev_id); if (params->num_ssids) { struct nlattr *ssids; @@ -3972,6 +4277,7 @@ nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd, return msg; fail: +nla_put_failure: nlmsg_free(msg); return NULL; } @@ -3993,7 +4299,8 @@ static int wpa_driver_nl80211_scan(struct i802_bss *bss, wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); drv->scan_for_auth = 0; - msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params); + msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params, + bss->wdev_id_set ? &bss->wdev_id : NULL); if (!msg) return -1; @@ -4096,7 +4403,8 @@ static int wpa_driver_nl80211_sched_scan(void *priv, return android_pno_start(bss, params); #endif /* ANDROID */ - msg = nl80211_scan_common(drv, NL80211_CMD_START_SCHED_SCAN, params); + msg = nl80211_scan_common(drv, NL80211_CMD_START_SCHED_SCAN, params, + bss->wdev_id_set ? &bss->wdev_id : NULL); if (!msg) goto nla_put_failure; @@ -4499,7 +4807,8 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) goto nla_put_failure; nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (nl80211_set_iface_id(msg, &drv->first_bss) < 0) + goto nla_put_failure; arg.drv = drv; arg.res = res; @@ -4570,13 +4879,18 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, const u8 *key, size_t key_len) { struct wpa_driver_nl80211_data *drv = bss->drv; - int ifindex = if_nametoindex(ifname); + int ifindex; struct nl_msg *msg; int ret; - wpa_printf(MSG_DEBUG, "%s: ifindex=%d alg=%d addr=%p key_idx=%d " + /* Ignore for P2P Device */ + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + return 0; + + ifindex = if_nametoindex(ifname); + wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d " "set_tx=%d seq_len=%lu key_len=%lu", - __func__, ifindex, alg, addr, key_idx, set_tx, + __func__, ifindex, ifname, alg, addr, key_idx, set_tx, (unsigned long) seq_len, (unsigned long) key_len); #ifdef CONFIG_TDLS if (key_idx == -1) @@ -5785,7 +6099,8 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, mgmt = (struct ieee80211_mgmt *) data; fc = le_to_host16(mgmt->frame_control); - if (is_sta_interface(drv->nlmode) && + if ((is_sta_interface(drv->nlmode) || + drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) && WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { /* @@ -6406,6 +6721,8 @@ static const char * nl80211_iftype_str(enum nl80211_iftype mode) return "P2P_CLIENT"; case NL80211_IFTYPE_P2P_GO: return "P2P_GO"; + case NL80211_IFTYPE_P2P_DEVICE: + return "P2P_DEVICE"; default: return "unknown"; } @@ -6415,7 +6732,9 @@ static const char * nl80211_iftype_str(enum nl80211_iftype mode) static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, const char *ifname, enum nl80211_iftype iftype, - const u8 *addr, int wds) + const u8 *addr, int wds, + int (*handler)(struct nl_msg *, void *), + void *arg) { struct nl_msg *msg; int ifidx; @@ -6429,7 +6748,8 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, return -1; nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (nl80211_set_iface_id(msg, &drv->first_bss) < 0) + goto nla_put_failure; NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); @@ -6447,7 +6767,7 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds); } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, handler, arg); msg = NULL; if (ret) { nla_put_failure: @@ -6457,6 +6777,9 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, return ret; } + if (iftype == NL80211_IFTYPE_P2P_DEVICE) + return 0; + ifidx = if_nametoindex(ifname); wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d", ifname, ifidx); @@ -6479,11 +6802,14 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, const char *ifname, enum nl80211_iftype iftype, - const u8 *addr, int wds) + const u8 *addr, int wds, + int (*handler)(struct nl_msg *, void *), + void *arg) { int ret; - ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds); + ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler, + arg); /* if error occurred and interface exists already */ if (ret == -ENFILE && if_nametoindex(ifname)) { @@ -6494,10 +6820,10 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, /* Try to create the interface again */ ret = nl80211_create_iface_once(drv, ifname, iftype, addr, - wds); + wds, handler, arg); } - if (ret >= 0 && is_p2p_interface(iftype)) + if (ret >= 0 && is_p2p_net_interface(iftype)) nl80211_disable_11b_rates(drv, ret, 1); return ret; @@ -6844,7 +7170,7 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) drv->monitor_ifidx = nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, - 0); + 0, NULL, NULL); if (drv->monitor_ifidx == -EOPNOTSUPP) { /* @@ -7707,7 +8033,8 @@ static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv, return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + if (nl80211_set_iface_id(msg, &drv->first_bss) < 0) + goto nla_put_failure; NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode); ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -7732,6 +8059,9 @@ static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, int res; res = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (res && nlmode == nl80211_get_ifmode(bss)) + res = 0; + if (res == 0) { drv->nlmode = nlmode; ret = 0; @@ -7755,8 +8085,7 @@ static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting " "interface down"); for (i = 0; i < 10; i++) { - res = linux_set_iface_flags(drv->global->ioctl_sock, - bss->ifname, 0); + res = i802_set_iface_flags(bss, 0); if (res == -EACCES || res == -ENODEV) break; if (res == 0) { @@ -7765,8 +8094,7 @@ static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, ret = nl80211_set_mode(drv, drv->ifindex, nlmode); if (ret == -EACCES) break; - res = linux_set_iface_flags(drv->global->ioctl_sock, - bss->ifname, 1); + res = i802_set_iface_flags(bss, 1); if (res && !ret) ret = -1; else if (ret != -EBUSY) @@ -7791,7 +8119,7 @@ done: return ret; } - if (is_p2p_interface(nlmode)) + if (is_p2p_net_interface(nlmode)) nl80211_disable_11b_rates(drv, drv->ifindex, 1); else if (drv->disabled_11b_rates) nl80211_disable_11b_rates(drv, drv->ifindex, 0); @@ -7830,6 +8158,13 @@ static int wpa_driver_nl80211_get_capa(void *priv, capa->extended_capa_mask = drv->extended_capa_mask; capa->extended_capa_len = drv->extended_capa_len; } + + if ((capa->flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && + !drv->allow_p2p_device) { + wpa_printf(MSG_DEBUG, "nl80211: Do not indicate P2P_DEVICE support (p2p_device=1 driver param not specified)"); + capa->flags &= ~WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; + } + return 0; } @@ -8368,7 +8703,7 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, if (!if_nametoindex(name)) { if (nl80211_create_iface(drv, name, NL80211_IFTYPE_AP_VLAN, - bss->addr, 1) < 0) + bss->addr, 1, NULL, NULL) < 0) return -1; if (bridge_ifname && linux_br_add_if(drv->global->ioctl_sock, @@ -8584,6 +8919,8 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( return NL80211_IFTYPE_AP; case WPA_IF_P2P_GO: return NL80211_IFTYPE_P2P_GO; + case WPA_IF_P2P_DEVICE: + return NL80211_IFTYPE_P2P_DEVICE; } return -1; } @@ -8630,12 +8967,40 @@ static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv, #endif /* CONFIG_P2P */ +struct wdev_info { + u64 wdev_id; + int wdev_id_set; + u8 macaddr[ETH_ALEN]; +}; + +static int nl80211_wdev_handler(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct wdev_info *wi = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (tb[NL80211_ATTR_WDEV]) { + wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + wi->wdev_id_set = 1; + } + + if (tb[NL80211_ATTR_MAC]) + os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + + return NL_SKIP; +} + + static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, const char *bridge) { + enum nl80211_iftype nlmode; struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ifidx; @@ -8651,21 +9016,46 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, if (addr) os_memcpy(if_addr, addr, ETH_ALEN); - ifidx = nl80211_create_iface(drv, ifname, - wpa_driver_nl80211_if_type(type), addr, - 0); - if (ifidx < 0) { + nlmode = wpa_driver_nl80211_if_type(type); + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) { + struct wdev_info p2pdev_info; + + os_memset(&p2pdev_info, 0, sizeof(p2pdev_info)); + ifidx = nl80211_create_iface(drv, ifname, nlmode, addr, + 0, nl80211_wdev_handler, + &p2pdev_info); + if (!p2pdev_info.wdev_id_set || ifidx != 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s", + ifname); + return -1; + } + + drv->global->if_add_wdevid = p2pdev_info.wdev_id; + drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set; + if (!is_zero_ether_addr(p2pdev_info.macaddr)) + os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created", + ifname, + (long long unsigned int) p2pdev_info.wdev_id); + } else { + ifidx = nl80211_create_iface(drv, ifname, nlmode, addr, + 0, NULL, NULL); + if (ifidx < 0) { #ifdef HOSTAPD - os_free(new_bss); + os_free(new_bss); #endif /* HOSTAPD */ - return -1; + return -1; + } } - if (!addr && - linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - if_addr) < 0) { - nl80211_remove_iface(drv, ifidx); - return -1; + if (!addr) { + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + os_memcpy(if_addr, bss->addr, ETH_ALEN); + else if (linux_get_ifhwaddr(drv->global->ioctl_sock, + bss->ifname, if_addr) < 0) { + nl80211_remove_iface(drv, ifidx); + return -1; + } } #ifdef CONFIG_P2P @@ -8673,16 +9063,14 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || type == WPA_IF_P2P_GO)) { /* Enforce unique P2P Interface Address */ - u8 new_addr[ETH_ALEN], own_addr[ETH_ALEN]; + u8 new_addr[ETH_ALEN]; - if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - own_addr) < 0 || - linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, + if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, new_addr) < 0) { nl80211_remove_iface(drv, ifidx); return -1; } - if (os_memcmp(own_addr, new_addr, ETH_ALEN) == 0) { + if (nl80211_addr_in_use(drv->global, new_addr)) { wpa_printf(MSG_DEBUG, "nl80211: Allocate new address " "for P2P group interface"); if (nl80211_p2p_interface_addr(drv, new_addr) < 0) { @@ -8830,7 +9218,9 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, freq, wait, no_cck, no_ack, offchanok); nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); if (wait) NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait); @@ -8923,7 +9313,8 @@ static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) (long long unsigned int) drv->send_action_cookie); nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie); ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -8952,7 +9343,9 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); @@ -8999,7 +9392,9 @@ static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie); ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -9154,11 +9549,9 @@ static int wpa_driver_nl80211_deinit_p2p_cli(void *priv) static void wpa_driver_nl80211_resume(void *priv) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on " - "resume event"); - } + + if (i802_set_iface_flags(bss, 1)) + wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event"); } @@ -9245,6 +9638,75 @@ nla_put_failure: } +/* Converts nl80211_chan_width to a common format */ +static enum chan_width convert2width(int width) +{ + switch (width) { + case NL80211_CHAN_WIDTH_20_NOHT: + return CHAN_WIDTH_20_NOHT; + case NL80211_CHAN_WIDTH_20: + return CHAN_WIDTH_20; + case NL80211_CHAN_WIDTH_40: + return CHAN_WIDTH_40; + case NL80211_CHAN_WIDTH_80: + return CHAN_WIDTH_80; + case NL80211_CHAN_WIDTH_80P80: + return CHAN_WIDTH_80P80; + case NL80211_CHAN_WIDTH_160: + return CHAN_WIDTH_160; + } + return CHAN_WIDTH_UNKNOWN; +} + + +static int get_channel_width(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wpa_signal_info *sig_change = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + sig_change->center_frq1 = -1; + sig_change->center_frq2 = -1; + sig_change->chanwidth = CHAN_WIDTH_UNKNOWN; + + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) { + sig_change->chanwidth = convert2width( + nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH])); + if (tb[NL80211_ATTR_CENTER_FREQ1]) + sig_change->center_frq1 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + if (tb[NL80211_ATTR_CENTER_FREQ2]) + sig_change->center_frq2 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + } + + return NL_SKIP; +} + + +static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_INTERFACE); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, get_channel_width, sig); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) { struct i802_bss *bss = priv; @@ -9256,6 +9718,10 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) if (res != 0) return res; + res = nl80211_get_channel_width(drv, si); + if (res != 0) + return res; + return nl80211_get_link_noise(drv, si); } @@ -9330,6 +9796,13 @@ static int nl80211_set_param(void *priv, const char *param) drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; } + + if (os_strstr(param, "p2p_device=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + drv->allow_p2p_device = 1; + } + #ifdef ANDROID_P2P if(os_strstr(param, "use_multi_chan_concurrent=1")) { struct i802_bss *bss = priv; @@ -10013,6 +10486,18 @@ nla_put_failure: } +const u8 * wpa_driver_nl80211_get_macaddr(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) + return NULL; + + return bss->addr; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -10092,6 +10577,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .tdls_oper = nl80211_tdls_oper, #endif /* CONFIG_TDLS */ .update_ft_ies = wpa_driver_nl80211_update_ft_ies, + .get_mac_addr = wpa_driver_nl80211_get_macaddr, #ifdef ANDROID_P2P .set_noa = wpa_driver_set_p2p_noa, .get_noa = wpa_driver_get_p2p_noa, diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h index 0d247c4d..f5890bec 100644 --- a/src/eap_common/eap_defs.h +++ b/src/eap_common/eap_defs.h @@ -63,6 +63,7 @@ typedef enum { 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; diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c new file mode 100644 index 00000000..a62ac8e0 --- /dev/null +++ b/src/eap_common/eap_eke_common.c @@ -0,0 +1,768 @@ +/* + * EAP server/peer: EAP-EKE shared routines + * Copyright (c) 2011-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/aes.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/dh_groups.h" +#include "crypto/random.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "eap_common/eap_defs.h" +#include "eap_eke_common.h" + + +static int eap_eke_dh_len(u8 group) +{ + switch (group) { + case EAP_EKE_DHGROUP_EKE_2: + return 128; + case EAP_EKE_DHGROUP_EKE_5: + return 192; + case EAP_EKE_DHGROUP_EKE_14: + return 256; + case EAP_EKE_DHGROUP_EKE_15: + return 384; + case EAP_EKE_DHGROUP_EKE_16: + return 512; + } + + return -1; +} + + +static int eap_eke_dhcomp_len(u8 dhgroup, u8 encr) +{ + int dhlen; + + dhlen = eap_eke_dh_len(dhgroup); + if (dhlen < 0) + return -1; + if (encr != EAP_EKE_ENCR_AES128_CBC) + return -1; + return AES_BLOCK_SIZE + dhlen; +} + + +static const struct dh_group * eap_eke_dh_group(u8 group) +{ + switch (group) { + case EAP_EKE_DHGROUP_EKE_2: + return dh_groups_get(2); + case EAP_EKE_DHGROUP_EKE_5: + return dh_groups_get(5); + case EAP_EKE_DHGROUP_EKE_14: + return dh_groups_get(14); + case EAP_EKE_DHGROUP_EKE_15: + return dh_groups_get(15); + case EAP_EKE_DHGROUP_EKE_16: + return dh_groups_get(16); + } + + return NULL; +} + + +static int eap_eke_dh_generator(u8 group) +{ + switch (group) { + case EAP_EKE_DHGROUP_EKE_2: + return 5; + case EAP_EKE_DHGROUP_EKE_5: + return 31; + case EAP_EKE_DHGROUP_EKE_14: + return 11; + case EAP_EKE_DHGROUP_EKE_15: + return 5; + case EAP_EKE_DHGROUP_EKE_16: + return 5; + } + + return -1; +} + + +static int eap_eke_pnonce_len(u8 mac) +{ + int mac_len; + + if (mac == EAP_EKE_MAC_HMAC_SHA1) + mac_len = SHA1_MAC_LEN; + else if (mac == EAP_EKE_MAC_HMAC_SHA2_256) + mac_len = SHA256_MAC_LEN; + else + return -1; + + return AES_BLOCK_SIZE + 16 + mac_len; +} + + +static int eap_eke_pnonce_ps_len(u8 mac) +{ + int mac_len; + + if (mac == EAP_EKE_MAC_HMAC_SHA1) + mac_len = SHA1_MAC_LEN; + else if (mac == EAP_EKE_MAC_HMAC_SHA2_256) + mac_len = SHA256_MAC_LEN; + else + return -1; + + return AES_BLOCK_SIZE + 2 * 16 + mac_len; +} + + +static int eap_eke_prf_len(u8 prf) +{ + if (prf == EAP_EKE_PRF_HMAC_SHA1) + return 20; + if (prf == EAP_EKE_PRF_HMAC_SHA2_256) + return 32; + return -1; +} + + +static int eap_eke_nonce_len(u8 prf) +{ + int prf_len; + + prf_len = eap_eke_prf_len(prf); + if (prf_len < 0) + return -1; + + if (prf_len > 2 * 16) + return (prf_len + 1) / 2; + + return 16; +} + + +static int eap_eke_auth_len(u8 prf) +{ + switch (prf) { + case EAP_EKE_PRF_HMAC_SHA1: + return SHA1_MAC_LEN; + case EAP_EKE_PRF_HMAC_SHA2_256: + return SHA256_MAC_LEN; + } + + return -1; +} + + +int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub) +{ + int generator; + u8 gen; + const struct dh_group *dh; + size_t pub_len, i; + + generator = eap_eke_dh_generator(group); + if (generator < 0 || generator > 255) + return -1; + gen = generator; + + dh = eap_eke_dh_group(group); + if (dh == NULL) + return -1; + + /* x = random number 2 .. p-1 */ + if (random_get_bytes(ret_priv, dh->prime_len)) + return -1; + if (os_memcmp(ret_priv, dh->prime, dh->prime_len) > 0) { + /* Make sure private value is smaller than prime */ + ret_priv[0] = 0; + } + for (i = 0; i < dh->prime_len - 1; i++) { + if (ret_priv[i]) + break; + } + if (i == dh->prime_len - 1 && (ret_priv[i] == 0 || ret_priv[i] == 1)) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: DH private value", + ret_priv, dh->prime_len); + + /* y = g ^ x (mod p) */ + pub_len = dh->prime_len; + if (crypto_mod_exp(&gen, 1, ret_priv, dh->prime_len, + dh->prime, dh->prime_len, ret_pub, &pub_len) < 0) + return -1; + if (pub_len < dh->prime_len) { + size_t pad = dh->prime_len - pub_len; + os_memmove(ret_pub + pad, ret_pub, pub_len); + os_memset(ret_pub, 0, pad); + } + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DH public value", + ret_pub, dh->prime_len); + + return 0; +} + + +static int eap_eke_prf(u8 prf, const u8 *key, size_t key_len, const u8 *data, + size_t data_len, const u8 *data2, size_t data2_len, + u8 *res) +{ + const u8 *addr[2]; + size_t len[2]; + size_t num_elem = 1; + + addr[0] = data; + len[0] = data_len; + if (data2) { + num_elem++; + addr[1] = data2; + len[1] = data2_len; + } + + if (prf == EAP_EKE_PRF_HMAC_SHA1) + return hmac_sha1_vector(key, key_len, num_elem, addr, len, res); + if (prf == EAP_EKE_PRF_HMAC_SHA2_256) + return hmac_sha256_vector(key, key_len, num_elem, addr, len, + res); + return -1; +} + + +static int eap_eke_prf_hmac_sha1(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *res, size_t len) +{ + u8 hash[SHA1_MAC_LEN]; + u8 idx; + const u8 *addr[3]; + size_t vlen[3]; + int ret; + + idx = 0; + addr[0] = hash; + vlen[0] = SHA1_MAC_LEN; + addr[1] = data; + vlen[1] = data_len; + addr[2] = &idx; + vlen[2] = 1; + + while (len > 0) { + idx++; + if (idx == 1) + ret = hmac_sha1_vector(key, key_len, 2, &addr[1], + &vlen[1], hash); + else + ret = hmac_sha1_vector(key, key_len, 3, addr, vlen, + hash); + if (ret < 0) + return -1; + if (len > SHA1_MAC_LEN) { + os_memcpy(res, hash, SHA1_MAC_LEN); + res += SHA1_MAC_LEN; + len -= SHA1_MAC_LEN; + } else { + os_memcpy(res, hash, len); + len = 0; + } + } + + return 0; +} + + +static int eap_eke_prf_hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *res, size_t len) +{ + u8 hash[SHA256_MAC_LEN]; + u8 idx; + const u8 *addr[3]; + size_t vlen[3]; + int ret; + + idx = 0; + addr[0] = hash; + vlen[0] = SHA256_MAC_LEN; + addr[1] = data; + vlen[1] = data_len; + addr[2] = &idx; + vlen[2] = 1; + + while (len > 0) { + idx++; + if (idx == 1) + ret = hmac_sha256_vector(key, key_len, 2, &addr[1], + &vlen[1], hash); + else + ret = hmac_sha256_vector(key, key_len, 3, addr, vlen, + hash); + if (ret < 0) + return -1; + if (len > SHA256_MAC_LEN) { + os_memcpy(res, hash, SHA256_MAC_LEN); + res += SHA256_MAC_LEN; + len -= SHA256_MAC_LEN; + } else { + os_memcpy(res, hash, len); + len = 0; + } + } + + return 0; +} + + +static int eap_eke_prfplus(u8 prf, const u8 *key, size_t key_len, + const u8 *data, size_t data_len, u8 *res, size_t len) +{ + if (prf == EAP_EKE_PRF_HMAC_SHA1) + return eap_eke_prf_hmac_sha1(key, key_len, data, data_len, res, + len); + if (prf == EAP_EKE_PRF_HMAC_SHA2_256) + return eap_eke_prf_hmac_sha256(key, key_len, data, data_len, + res, len); + return -1; +} + + +int eap_eke_derive_key(struct eap_eke_session *sess, + const u8 *password, size_t password_len, + const u8 *id_s, size_t id_s_len, const u8 *id_p, + size_t id_p_len, u8 *key) +{ + u8 zeros[EAP_EKE_MAX_HASH_LEN]; + u8 temp[EAP_EKE_MAX_HASH_LEN]; + size_t key_len = 16; /* Only AES-128-CBC is used here */ + u8 *id; + + /* temp = prf(0+, password) */ + os_memset(zeros, 0, sess->prf_len); + if (eap_eke_prf(sess->prf, zeros, sess->prf_len, + password, password_len, NULL, 0, temp) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: temp = prf(0+, password)", + temp, sess->prf_len); + + /* key = prf+(temp, ID_S | ID_P) */ + id = os_malloc(id_s_len + id_p_len); + if (id == NULL) + return -1; + os_memcpy(id, id_s, id_s_len); + os_memcpy(id + id_s_len, id_p, id_p_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: ID_S | ID_P", + id, id_s_len + id_p_len); + if (eap_eke_prfplus(sess->prf, temp, sess->prf_len, + id, id_s_len + id_p_len, key, key_len) < 0) { + os_free(id); + return -1; + } + os_free(id); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: key = prf+(temp, ID_S | ID_P)", + key, key_len); + + return 0; +} + + +int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub, + u8 *ret_dhcomp) +{ + u8 pub[EAP_EKE_MAX_DH_LEN]; + int dh_len; + u8 iv[AES_BLOCK_SIZE]; + + dh_len = eap_eke_dh_len(sess->dhgroup); + if (dh_len < 0) + return -1; + + /* + * DHComponent = Encr(key, y) + * + * All defined DH groups use primes that have length devisible by 16, so + * no need to do extra padding for y (= pub). + */ + if (sess->encr != EAP_EKE_ENCR_AES128_CBC) + return -1; + if (random_get_bytes(iv, AES_BLOCK_SIZE)) + return -1; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Encr(key, y)", + iv, AES_BLOCK_SIZE); + os_memcpy(pub, dhpub, dh_len); + if (aes_128_cbc_encrypt(key, iv, pub, dh_len) < 0) + return -1; + os_memcpy(ret_dhcomp, iv, AES_BLOCK_SIZE); + os_memcpy(ret_dhcomp + AES_BLOCK_SIZE, pub, dh_len); + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent = Encr(key, y)", + ret_dhcomp, AES_BLOCK_SIZE + dh_len); + + return 0; +} + + +int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, + const u8 *dhpriv, const u8 *peer_dhcomp) +{ + u8 zeros[EAP_EKE_MAX_HASH_LEN]; + u8 peer_pub[EAP_EKE_MAX_DH_LEN]; + u8 modexp[EAP_EKE_MAX_DH_LEN]; + size_t len; + const struct dh_group *dh; + + if (sess->encr != EAP_EKE_ENCR_AES128_CBC) + return -1; + + dh = eap_eke_dh_group(sess->dhgroup); + if (dh == NULL) + return -1; + + /* Decrypt peer DHComponent */ + os_memcpy(peer_pub, peer_dhcomp + AES_BLOCK_SIZE, dh->prime_len); + if (aes_128_cbc_decrypt(key, peer_dhcomp, peer_pub, dh->prime_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt DHComponent"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted peer DH pubkey", + peer_pub, dh->prime_len); + + /* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */ + len = dh->prime_len; + if (crypto_mod_exp(peer_pub, dh->prime_len, dhpriv, dh->prime_len, + dh->prime, dh->prime_len, modexp, &len) < 0) + return -1; + if (len < dh->prime_len) { + size_t pad = dh->prime_len - len; + os_memmove(modexp + pad, modexp, len); + os_memset(modexp, 0, pad); + } + + os_memset(zeros, 0, sess->auth_len); + if (eap_eke_prf(sess->prf, zeros, sess->auth_len, modexp, dh->prime_len, + NULL, 0, sess->shared_secret) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: SharedSecret", + sess->shared_secret, sess->auth_len); + + return 0; +} + + +int eap_eke_derive_ke_ki(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len) +{ + u8 buf[EAP_EKE_MAX_KE_LEN + EAP_EKE_MAX_KI_LEN]; + size_t ke_len, ki_len; + u8 *data; + size_t data_len; + const char *label = "EAP-EKE Keys"; + size_t label_len; + + /* + * Ke | Ki = prf+(SharedSecret, "EAP-EKE Keys" | ID_S | ID_P) + * Ke = encryption key + * Ki = integrity protection key + * Length of each key depends on the selected algorithms. + */ + + if (sess->encr == EAP_EKE_ENCR_AES128_CBC) + ke_len = 16; + else + return -1; + + if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) + ki_len = 20; + else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) + ki_len = 32; + else + return -1; + + label_len = os_strlen(label); + data_len = label_len + id_s_len + id_p_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + os_memcpy(data, label, label_len); + os_memcpy(data + label_len, id_s, id_s_len); + os_memcpy(data + label_len + id_s_len, id_p, id_p_len); + if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, + data, data_len, buf, ke_len + ki_len) < 0) { + os_free(data); + return -1; + } + + os_memcpy(sess->ke, buf, ke_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ke", sess->ke, ke_len); + os_memcpy(sess->ki, buf + ke_len, ki_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ki", sess->ki, ki_len); + + os_free(data); + return 0; +} + + +int eap_eke_derive_ka(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s) +{ + u8 *data, *pos; + size_t data_len; + const char *label = "EAP-EKE Ka"; + size_t label_len; + + /* + * Ka = prf+(SharedSecret, "EAP-EKE Ka" | ID_S | ID_P | Nonce_P | + * Nonce_S) + * Ka = authentication key + * Length of the key depends on the selected algorithms. + */ + + label_len = os_strlen(label); + data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + os_memcpy(pos, label, label_len); + pos += label_len; + os_memcpy(pos, id_s, id_s_len); + pos += id_s_len; + os_memcpy(pos, id_p, id_p_len); + pos += id_p_len; + os_memcpy(pos, nonce_p, sess->nonce_len); + pos += sess->nonce_len; + os_memcpy(pos, nonce_s, sess->nonce_len); + if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, + data, data_len, sess->ka, sess->prf_len) < 0) { + os_free(data); + return -1; + } + os_free(data); + + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka", sess->ka, sess->prf_len); + + return 0; +} + + +int eap_eke_derive_msk(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s, + u8 *msk, u8 *emsk) +{ + u8 *data, *pos; + size_t data_len; + const char *label = "EAP-EKE Exported Keys"; + size_t label_len; + u8 buf[EAP_MSK_LEN + EAP_EMSK_LEN]; + + /* + * MSK | EMSK = prf+(SharedSecret, "EAP-EKE Exported Keys" | ID_S | + * ID_P | Nonce_P | Nonce_S) + */ + + label_len = os_strlen(label); + data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + os_memcpy(pos, label, label_len); + pos += label_len; + os_memcpy(pos, id_s, id_s_len); + pos += id_s_len; + os_memcpy(pos, id_p, id_p_len); + pos += id_p_len; + os_memcpy(pos, nonce_p, sess->nonce_len); + pos += sess->nonce_len; + os_memcpy(pos, nonce_s, sess->nonce_len); + if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, + data, data_len, buf, EAP_MSK_LEN + EAP_EMSK_LEN) < + 0) { + os_free(data); + return -1; + } + os_free(data); + + os_memcpy(msk, buf, EAP_MSK_LEN); + os_memcpy(emsk, buf + EAP_MSK_LEN, EAP_EMSK_LEN); + os_memset(buf, 0, sizeof(buf)); + + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: MSK", msk, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: EMSK", msk, EAP_EMSK_LEN); + + return 0; +} + + +static int eap_eke_mac(u8 mac, const u8 *key, const u8 *data, size_t data_len, + u8 *res) +{ + if (mac == EAP_EKE_MAC_HMAC_SHA1) + return hmac_sha1(key, SHA1_MAC_LEN, data, data_len, res); + if (mac == EAP_EKE_MAC_HMAC_SHA2_256) + return hmac_sha256(key, SHA256_MAC_LEN, data, data_len, res); + return -1; +} + + +int eap_eke_prot(struct eap_eke_session *sess, + const u8 *data, size_t data_len, + u8 *prot, size_t *prot_len) +{ + size_t block_size, icv_len, pad; + u8 *pos, *iv, *e; + + if (sess->encr == EAP_EKE_ENCR_AES128_CBC) + block_size = AES_BLOCK_SIZE; + else + return -1; + + if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) + icv_len = SHA1_MAC_LEN; + else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) + icv_len = SHA256_MAC_LEN; + else + return -1; + + pad = data_len % block_size; + if (pad) + pad = block_size - pad; + + if (*prot_len < block_size + data_len + pad + icv_len) { + wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for Prot() data"); + } + pos = prot; + + if (random_get_bytes(pos, block_size)) + return -1; + iv = pos; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Prot()", iv, block_size); + pos += block_size; + + e = pos; + os_memcpy(pos, data, data_len); + pos += data_len; + if (pad) { + if (random_get_bytes(pos, pad)) + return -1; + pos += pad; + } + + if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0) + return -1; + + if (eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0) + return -1; + pos += icv_len; + + *prot_len = pos - prot; + return 0; +} + + +int eap_eke_decrypt_prot(struct eap_eke_session *sess, + const u8 *prot, size_t prot_len, + u8 *data, size_t *data_len) +{ + size_t block_size, icv_len; + u8 icv[EAP_EKE_MAX_HASH_LEN]; + + if (sess->encr == EAP_EKE_ENCR_AES128_CBC) + block_size = AES_BLOCK_SIZE; + else + return -1; + + if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) + icv_len = SHA1_MAC_LEN; + else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) + icv_len = SHA256_MAC_LEN; + else + return -1; + + if (prot_len < 2 * block_size + icv_len) + return -1; + if ((prot_len - icv_len) % block_size) + return -1; + + if (eap_eke_mac(sess->mac, sess->ki, prot + block_size, + prot_len - block_size - icv_len, icv) < 0) + return -1; + if (os_memcmp(icv, prot + prot_len - icv_len, icv_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: ICV mismatch in Prot() data"); + return -1; + } + + if (*data_len < prot_len - block_size - icv_len) { + wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for decrypted Prot() data"); + return -1; + } + + *data_len = prot_len - block_size - icv_len; + os_memcpy(data, prot + block_size, *data_len); + if (aes_128_cbc_decrypt(sess->ke, prot, data, *data_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt Prot() data"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted Prot() data", + data, *data_len); + + return 0; +} + + +int eap_eke_auth(struct eap_eke_session *sess, const char *label, + const struct wpabuf *msgs, u8 *auth) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: Auth(%s)", label); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka for Auth", + sess->ka, sess->auth_len); + wpa_hexdump_buf(MSG_MSGDUMP, "EAP-EKE: Messages for Auth", msgs); + return eap_eke_prf(sess->prf, sess->ka, sess->auth_len, + (const u8 *) label, os_strlen(label), + wpabuf_head(msgs), wpabuf_len(msgs), auth); +} + + +int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, + u8 prf, u8 mac) +{ + sess->dhgroup = dhgroup; + sess->encr = encr; + sess->prf = prf; + sess->mac = mac; + + sess->prf_len = eap_eke_prf_len(prf); + if (sess->prf_len < 0) + return -1; + sess->nonce_len = eap_eke_nonce_len(prf); + if (sess->nonce_len < 0) + return -1; + sess->auth_len = eap_eke_auth_len(prf); + if (sess->auth_len < 0) + return -1; + sess->dhcomp_len = eap_eke_dhcomp_len(sess->dhgroup, sess->encr); + if (sess->dhcomp_len < 0) + return -1; + sess->pnonce_len = eap_eke_pnonce_len(sess->mac); + if (sess->pnonce_len < 0) + return -1; + sess->pnonce_ps_len = eap_eke_pnonce_ps_len(sess->mac); + if (sess->pnonce_ps_len < 0) + return -1; + + return 0; +} + + +void eap_eke_session_clean(struct eap_eke_session *sess) +{ + os_memset(sess->shared_secret, 0, EAP_EKE_MAX_HASH_LEN); + os_memset(sess->ke, 0, EAP_EKE_MAX_KE_LEN); + os_memset(sess->ki, 0, EAP_EKE_MAX_KI_LEN); + os_memset(sess->ka, 0, EAP_EKE_MAX_KA_LEN); +} diff --git a/src/eap_common/eap_eke_common.h b/src/eap_common/eap_eke_common.h new file mode 100644 index 00000000..a4c04222 --- /dev/null +++ b/src/eap_common/eap_eke_common.h @@ -0,0 +1,114 @@ +/* + * EAP server/peer: EAP-EKE shared routines + * Copyright (c) 2011-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_EKE_COMMON_H +#define EAP_EKE_COMMON_H + +/* EKE Exchange */ +#define EAP_EKE_ID 1 +#define EAP_EKE_COMMIT 2 +#define EAP_EKE_CONFIRM 3 +#define EAP_EKE_FAILURE 4 + +/* Diffie-Hellman Group Registry */ +#define EAP_EKE_DHGROUP_EKE_2 1 +#define EAP_EKE_DHGROUP_EKE_5 2 +#define EAP_EKE_DHGROUP_EKE_14 3 /* mandatory to implement */ +#define EAP_EKE_DHGROUP_EKE_15 4 +#define EAP_EKE_DHGROUP_EKE_16 5 + +/* Encryption Algorithm Registry */ +#define EAP_EKE_ENCR_AES128_CBC 1 /* mandatory to implement */ + +/* Pseudo Random Function Registry */ +#define EAP_EKE_PRF_HMAC_SHA1 1 /* mandatory to implement */ +#define EAP_EKE_PRF_HMAC_SHA2_256 2 + +/* Keyed Message Digest (MAC) Registry */ +#define EAP_EKE_MAC_HMAC_SHA1 1 /* mandatory to implement */ +#define EAP_EKE_MAC_HMAC_SHA2_256 2 + +/* Identity Type Registry */ +#define EAP_EKE_ID_OPAQUE 1 +#define EAP_EKE_ID_NAI 2 +#define EAP_EKE_ID_IPv4 3 +#define EAP_EKE_ID_IPv6 4 +#define EAP_EKE_ID_FQDN 5 +#define EAP_EKE_ID_DN 6 + +/* Failure-Code */ +#define EAP_EKE_FAIL_NO_ERROR 1 +#define EAP_EKE_FAIL_PROTO_ERROR 2 +#define EAP_EKE_FAIL_PASSWD_NOT_FOUND 3 +#define EAP_EKE_FAIL_AUTHENTICATION_FAIL 4 +#define EAP_EKE_FAIL_AUTHORIZATION_FAIL 5 +#define EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN 6 +#define EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR 0xffffffff + +#define EAP_EKE_MAX_DH_LEN 512 +#define EAP_EKE_MAX_HASH_LEN 32 +#define EAP_EKE_MAX_KEY_LEN 16 +#define EAP_EKE_MAX_KE_LEN 16 +#define EAP_EKE_MAX_KI_LEN 32 +#define EAP_EKE_MAX_KA_LEN 32 +#define EAP_EKE_MAX_NONCE_LEN 16 + +struct eap_eke_session { + /* Selected proposal */ + u8 dhgroup; + u8 encr; + u8 prf; + u8 mac; + + u8 shared_secret[EAP_EKE_MAX_HASH_LEN]; + u8 ke[EAP_EKE_MAX_KE_LEN]; + u8 ki[EAP_EKE_MAX_KI_LEN]; + u8 ka[EAP_EKE_MAX_KA_LEN]; + + int prf_len; + int nonce_len; + int auth_len; + int dhcomp_len; + int pnonce_len; + int pnonce_ps_len; +}; + +int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, + u8 prf, u8 mac); +void eap_eke_session_clean(struct eap_eke_session *sess); +int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub); +int eap_eke_derive_key(struct eap_eke_session *sess, + const u8 *password, size_t password_len, + const u8 *id_s, size_t id_s_len, const u8 *id_p, + size_t id_p_len, u8 *key); +int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub, + u8 *ret_dhcomp); +int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, + const u8 *dhpriv, const u8 *peer_dhcomp); +int eap_eke_derive_ke_ki(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len); +int eap_eke_derive_ka(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s); +int eap_eke_derive_msk(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s, + u8 *msk, u8 *emsk); +int eap_eke_prot(struct eap_eke_session *sess, + const u8 *data, size_t data_len, + u8 *prot, size_t *prot_len); +int eap_eke_decrypt_prot(struct eap_eke_session *sess, + const u8 *prot, size_t prot_len, + u8 *data, size_t *data_len); +int eap_eke_auth(struct eap_eke_session *sess, const char *label, + const struct wpabuf *msgs, u8 *auth); + +#endif /* EAP_EKE_COMMON_H */ diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index ed909198..42f525b9 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -1,6 +1,6 @@ /* * EAP peer configuration data - * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * 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. @@ -634,6 +634,15 @@ struct eap_peer_config { * 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; }; diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c new file mode 100644 index 00000000..c71db5fd --- /dev/null +++ b/src/eap_peer/eap_eke.c @@ -0,0 +1,723 @@ +/* + * EAP peer method: EAP-EKE (RFC 6124) + * Copyright (c) 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/random.h" +#include "eap_peer/eap_i.h" +#include "eap_common/eap_eke_common.h" + +struct eap_eke_data { + enum { + IDENTITY, COMMIT, CONFIRM, SUCCESS, FAILURE + } state; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 *peerid; + size_t peerid_len; + u8 *serverid; + size_t serverid_len; + u8 dh_priv[EAP_EKE_MAX_DH_LEN]; + struct eap_eke_session sess; + u8 nonce_p[EAP_EKE_MAX_NONCE_LEN]; + u8 nonce_s[EAP_EKE_MAX_NONCE_LEN]; + struct wpabuf *msgs; +}; + + +static const char * eap_eke_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case COMMIT: + return "COMMIT"; + case CONFIRM: + return "CONFIRM"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_eke_state(struct eap_eke_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s", + eap_eke_state_txt(data->state), eap_eke_state_txt(state)); + data->state = state; +} + + +static void eap_eke_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_eke_init(struct eap_sm *sm) +{ + struct eap_eke_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (!password) { + wpa_printf(MSG_INFO, "EAP-EKE: No password configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + eap_eke_state(data, IDENTITY); + + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + data->peerid = os_malloc(identity_len); + if (data->peerid == NULL) { + eap_eke_deinit(sm, data); + return NULL; + } + os_memcpy(data->peerid, identity, identity_len); + data->peerid_len = identity_len; + } + + return data; +} + + +static void eap_eke_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + eap_eke_session_clean(&data->sess); + os_free(data->serverid); + os_free(data->peerid); + wpabuf_free(data->msgs); + os_free(data); +} + + +static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, int id, + size_t length, u8 eke_exch) +{ + struct wpabuf *msg; + size_t plen; + + plen = 1 + length; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen, + EAP_CODE_RESPONSE, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory"); + return NULL; + } + + wpabuf_put_u8(msg, eke_exch); + + return msg; +} + + +static int eap_eke_supp_dhgroup(u8 dhgroup) +{ + return dhgroup == EAP_EKE_DHGROUP_EKE_2 || + dhgroup == EAP_EKE_DHGROUP_EKE_5 || + dhgroup == EAP_EKE_DHGROUP_EKE_14 || + dhgroup == EAP_EKE_DHGROUP_EKE_15 || + dhgroup == EAP_EKE_DHGROUP_EKE_16; +} + + +static int eap_eke_supp_encr(u8 encr) +{ + return encr == EAP_EKE_ENCR_AES128_CBC; +} + + +static int eap_eke_supp_prf(u8 prf) +{ + return prf == EAP_EKE_PRF_HMAC_SHA1 || + prf == EAP_EKE_PRF_HMAC_SHA2_256; +} + + +static int eap_eke_supp_mac(u8 mac) +{ + return mac == EAP_EKE_MAC_HMAC_SHA1 || + mac == EAP_EKE_MAC_HMAC_SHA2_256; +} + + +static struct wpabuf * eap_eke_build_fail(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + u32 failure_code) +{ + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x", + failure_code); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), 4, EAP_EKE_FAILURE); + if (resp) + wpabuf_put_be32(resp, failure_code); + + os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); + eap_eke_session_clean(&data->sess); + + eap_eke_state(data, FAILURE); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + return resp; +} + + +static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct wpabuf *resp; + unsigned num_prop, i; + const u8 *pos, *end; + const u8 *prop = NULL; + u8 idtype; + + if (data->state != IDENTITY) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-ID/Request"); + + if (payload_len < 2 + 4) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + pos = payload; + end = payload + payload_len; + + num_prop = *pos++; + pos++; /* Ignore Reserved field */ + + if (pos + num_prop * 4 > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data (num_prop=%u)", + num_prop); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + for (i = 0; i < num_prop; i++) { + const u8 *tmp = pos; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Proposal #%u: dh=%u encr=%u prf=%u mac=%u", + i, pos[0], pos[1], pos[2], pos[3]); + pos += 4; + + if (!eap_eke_supp_dhgroup(*tmp)) + continue; + tmp++; + if (!eap_eke_supp_encr(*tmp)) + continue; + tmp++; + if (!eap_eke_supp_prf(*tmp)) + continue; + tmp++; + if (!eap_eke_supp_mac(*tmp)) + continue; + + prop = tmp - 3; + if (eap_eke_session_init(&data->sess, prop[0], prop[1], prop[2], + prop[3]) < 0) { + prop = NULL; + continue; + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Selected proposal"); + break; + } + + if (prop == NULL) { + wpa_printf(MSG_DEBUG, "EAP-EKE: No acceptable proposal found"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN); + } + + pos += (num_prop - i - 1) * 4; + + if (pos == end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data to include IDType/Identity"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + idtype = *pos++; + wpa_printf(MSG_DEBUG, "EAP-EKE: Server IDType %u", idtype); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity", + pos, end - pos); + os_free(data->serverid); + data->serverid = os_malloc(end - pos); + if (data->serverid == NULL) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + os_memcpy(data->serverid, pos, end - pos); + data->serverid_len = end - pos; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response"); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), + 2 + 4 + 1 + data->peerid_len, + EAP_EKE_ID); + if (resp == NULL) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + wpabuf_put_u8(resp, 1); /* NumProposals */ + wpabuf_put_u8(resp, 0); /* Reserved */ + wpabuf_put_data(resp, prop, 4); /* Selected Proposal */ + wpabuf_put_u8(resp, EAP_EKE_ID_NAI); + if (data->peerid) + wpabuf_put_data(resp, data->peerid, data->peerid_len); + + wpabuf_free(data->msgs); + data->msgs = wpabuf_alloc(wpabuf_len(reqData) + wpabuf_len(resp)); + if (data->msgs == NULL) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpabuf_put_buf(data->msgs, reqData); + wpabuf_put_buf(data->msgs, resp); + + eap_eke_state(data, COMMIT); + + return resp; +} + + +static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, + struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct wpabuf *resp; + const u8 *pos, *end, *dhcomp; + size_t prot_len; + u8 *rpos; + u8 key[EAP_EKE_MAX_KEY_LEN]; + u8 pub[EAP_EKE_MAX_DH_LEN]; + const u8 *password; + size_t password_len; + + if (data->state != COMMIT) { + wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Commit/Request received in unexpected state (%d)", data->state); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Commit/Request"); + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-EKE: No password configured!"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PASSWD_NOT_FOUND); + } + + pos = payload; + end = payload + payload_len; + + if (pos + data->sess.dhcomp_len > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_S", + pos, data->sess.dhcomp_len); + dhcomp = pos; + pos += data->sess.dhcomp_len; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos); + + /* + * temp = prf(0+, password) + * key = prf+(temp, ID_S | ID_P) + */ + if (eap_eke_derive_key(&data->sess, password, password_len, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, key) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + /* + * y_p = g ^ x_p (mod p) + * x_p = random number 2 .. p-1 + */ + if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + if (eap_eke_shared_secret(&data->sess, key, data->dh_priv, dhcomp) < 0) + { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + if (eap_eke_derive_ke_ki(&data->sess, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response"); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), + data->sess.dhcomp_len + data->sess.pnonce_len, + EAP_EKE_COMMIT); + if (resp == NULL) { + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + /* DHComponent_P = Encr(key, y_p) */ + rpos = wpabuf_put(resp, data->sess.dhcomp_len); + if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + os_memset(key, 0, sizeof(key)); + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", + rpos, data->sess.dhcomp_len); + + if (random_get_bytes(data->nonce_p, data->sess.nonce_len)) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", + data->nonce_p, data->sess.nonce_len); + prot_len = wpabuf_tailroom(resp); + if (eap_eke_prot(&data->sess, data->nonce_p, data->sess.nonce_len, + wpabuf_put(resp, 0), &prot_len) < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", + wpabuf_put(resp, 0), prot_len); + wpabuf_put(resp, prot_len); + + /* TODO: CBValue */ + + if (wpabuf_resize(&data->msgs, wpabuf_len(reqData) + wpabuf_len(resp)) + < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpabuf_put_buf(data->msgs, reqData); + wpabuf_put_buf(data->msgs, resp); + + eap_eke_state(data, CONFIRM); + + return resp; +} + + +static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct wpabuf *resp; + const u8 *pos, *end; + size_t prot_len; + u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN]; + u8 auth_s[EAP_EKE_MAX_HASH_LEN]; + size_t decrypt_len; + u8 *auth; + + if (data->state != CONFIRM) { + wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Confirm/Request received in unexpected state (%d)", + data->state); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Confirm/Request"); + + pos = payload; + end = payload + payload_len; + + if (pos + data->sess.pnonce_ps_len + data->sess.prf_len > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + decrypt_len = sizeof(nonces); + if (eap_eke_decrypt_prot(&data->sess, pos, data->sess.pnonce_ps_len, + nonces, &decrypt_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_PS"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + if (decrypt_len != (size_t) 2 * data->sess.nonce_len) { + wpa_printf(MSG_INFO, "EAP-EKE: PNonce_PS protected data length does not match length of Nonce_P and Nonce_S"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_P | Nonce_S", + nonces, 2 * data->sess.nonce_len); + if (os_memcmp(data->nonce_p, nonces, data->sess.nonce_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match trnsmitted Nonce_P"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + + os_memcpy(data->nonce_s, nonces + data->sess.nonce_len, + data->sess.nonce_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S", + data->nonce_s, data->sess.nonce_len); + + if (eap_eke_derive_ka(&data->sess, data->serverid, data->serverid_len, + data->peerid, data->peerid_len, + data->nonce_p, data->nonce_s) < 0) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth_s) < 0) + { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len); + if (os_memcmp(auth_s, pos + data->sess.pnonce_ps_len, + data->sess.prf_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Auth_S does not match"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response"); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), + data->sess.pnonce_len + data->sess.prf_len, + EAP_EKE_CONFIRM); + if (resp == NULL) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + prot_len = wpabuf_tailroom(resp); + if (eap_eke_prot(&data->sess, data->nonce_s, data->sess.nonce_len, + wpabuf_put(resp, 0), &prot_len) < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpabuf_put(resp, prot_len); + + auth = wpabuf_put(resp, data->sess.prf_len); + if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth) < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth, data->sess.prf_len); + + if (eap_eke_derive_msk(&data->sess, data->serverid, data->serverid_len, + data->peerid, data->peerid_len, + data->nonce_s, data->nonce_p, + data->msk, data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); + eap_eke_session_clean(&data->sess); + + eap_eke_state(data, SUCCESS); + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + return resp; +} + + +static struct wpabuf * eap_eke_process_failure(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Failure/Request"); + + if (payload_len < 4) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure"); + } else { + u32 code; + code = WPA_GET_BE32(payload); + wpa_printf(MSG_INFO, "EAP-EKE: Failure-Code 0x%x", code); + } + + return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_NO_ERROR); +} + + +static struct wpabuf * eap_eke_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_eke_data *data = priv; + struct wpabuf *resp; + const u8 *pos, *end; + size_t len; + u8 eke_exch; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, reqData, &len); + if (pos == NULL || len < 1) { + ret->ignore = TRUE; + return NULL; + } + + end = pos + len; + eke_exch = *pos++; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: exch %d", eke_exch); + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received Data", pos, end - pos); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (eke_exch) { + case EAP_EKE_ID: + resp = eap_eke_process_id(data, ret, reqData, pos, end - pos); + break; + case EAP_EKE_COMMIT: + resp = eap_eke_process_commit(sm, data, ret, reqData, + pos, end - pos); + break; + case EAP_EKE_CONFIRM: + resp = eap_eke_process_confirm(data, ret, reqData, + pos, end - pos); + break; + case EAP_EKE_FAILURE: + resp = eap_eke_process_failure(data, ret, reqData, + pos, end - pos); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-EKE: Ignoring message with unknown EKE-Exch %d", eke_exch); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) + ret->allowNotifications = FALSE; + + return resp; +} + + +static Boolean eap_eke_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +int eap_peer_eke_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); + if (eap == NULL) + return -1; + + eap->init = eap_eke_init; + eap->deinit = eap_eke_deinit; + eap->process = eap_eke_process; + eap->isKeyAvailable = eap_eke_isKeyAvailable; + eap->getKey = eap_eke_getKey; + eap->get_emsk = eap_eke_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h index 4994ff1c..a465fd23 100644 --- a/src/eap_peer/eap_methods.h +++ b/src/eap_peer/eap_methods.h @@ -105,5 +105,6 @@ 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/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index a777bb01..be8c3018 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -1,6 +1,6 @@ /* * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> + * 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. @@ -164,6 +164,10 @@ static int eap_tls_init_connection(struct eap_sm *sm, { 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 " diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index f2a7cd75..36b230b4 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -104,6 +104,9 @@ struct eap_config { int fragment_size; int pbc_in_m1; + + const u8 *server_id; + size_t server_id_len; }; diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index f92704a1..003e2020 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -188,6 +188,9 @@ struct eap_sm { int fragment_size; int pbc_in_m1; + + const u8 *server_id; + size_t server_id_len; }; int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h index bc810a9b..429cb72b 100644 --- a/src/eap_server/eap_methods.h +++ b/src/eap_server/eap_methods.h @@ -45,5 +45,6 @@ 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/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index 15f7e228..54b7533d 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -1278,6 +1278,8 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->fragment_size = conf->fragment_size; sm->pwd_group = conf->pwd_group; sm->pbc_in_m1 = conf->pbc_in_m1; + sm->server_id = conf->server_id; + sm->server_id_len = conf->server_id_len; wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c new file mode 100644 index 00000000..b19a321a --- /dev/null +++ b/src/eap_server/eap_server_eke.c @@ -0,0 +1,793 @@ +/* + * hostapd / EAP-EKE (RFC 6124) server + * Copyright (c) 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/random.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_eke_common.h" + + +struct eap_eke_data { + enum { + IDENTITY, COMMIT, CONFIRM, FAILURE_REPORT, SUCCESS, FAILURE + } state; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 *peerid; + size_t peerid_len; + u8 peerid_type; + u8 serverid_type; + u8 dh_priv[EAP_EKE_MAX_DH_LEN]; + u8 key[EAP_EKE_MAX_KEY_LEN]; + struct eap_eke_session sess; + u8 nonce_p[EAP_EKE_MAX_NONCE_LEN]; + u8 nonce_s[EAP_EKE_MAX_NONCE_LEN]; + struct wpabuf *msgs; + int phase2; + u32 failure_code; +}; + + +static const char * eap_eke_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case COMMIT: + return "COMMIT"; + case CONFIRM: + return "CONFIRM"; + case FAILURE_REPORT: + return "FAILURE_REPORT"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_eke_state(struct eap_eke_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s", + eap_eke_state_txt(data->state), + eap_eke_state_txt(state)); + data->state = state; +} + + +static void eap_eke_fail(struct eap_eke_data *data, u32 code) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: Failure - code 0x%x", code); + data->failure_code = code; + eap_eke_state(data, FAILURE_REPORT); +} + + +static void * eap_eke_init(struct eap_sm *sm) +{ + struct eap_eke_data *data; + size_t i; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + eap_eke_state(data, IDENTITY); + + data->serverid_type = EAP_EKE_ID_OPAQUE; + for (i = 0; i < sm->server_id_len; i++) { + if (sm->server_id[i] == '.' && + data->serverid_type == EAP_EKE_ID_OPAQUE) + data->serverid_type = EAP_EKE_ID_FQDN; + if (sm->server_id[i] == '@') + data->serverid_type = EAP_EKE_ID_NAI; + } + + data->phase2 = sm->init_phase2; + + return data; +} + + +static void eap_eke_reset(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + eap_eke_session_clean(&data->sess); + os_free(data->peerid); + wpabuf_free(data->msgs); + os_free(data); +} + + +static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, + u8 id, size_t length, u8 eke_exch) +{ + struct wpabuf *msg; + size_t plen; + + plen = 1 + length; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen, + EAP_CODE_REQUEST, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory"); + return NULL; + } + + wpabuf_put_u8(msg, eke_exch); + + return msg; +} + + +static int supported_proposal(const u8 *pos) +{ + if (pos[0] == EAP_EKE_DHGROUP_EKE_16 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && + pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) + return 1; + + if (pos[0] == EAP_EKE_DHGROUP_EKE_15 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && + pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) + return 1; + + if (pos[0] == EAP_EKE_DHGROUP_EKE_14 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && + pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) + return 1; + + if (pos[0] == EAP_EKE_DHGROUP_EKE_14 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA1 && + pos[3] == EAP_EKE_MAC_HMAC_SHA1) + return 1; + + return 0; +} + + +static struct wpabuf * eap_eke_build_failure(struct eap_eke_data *data, u8 id) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Failure: Failure-Code=0x%x", + data->failure_code); + + msg = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE); + if (msg == NULL) { + eap_eke_state(data, FAILURE); + return NULL; + } + wpabuf_put_be32(msg, data->failure_code); + + return msg; +} + + +static struct wpabuf * eap_eke_build_identity(struct eap_sm *sm, + struct eap_eke_data *data, + u8 id) +{ + struct wpabuf *msg; + size_t plen; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Identity"); + + plen = 2 + 4 * 4 + 1 + sm->server_id_len; + msg = eap_eke_build_msg(data, id, plen, EAP_EKE_ID); + if (msg == NULL) + return NULL; + + wpabuf_put_u8(msg, 4); /* NumProposals */ + wpabuf_put_u8(msg, 0); /* Reserved */ + + /* Proposal - DH Group 16 with AES128-CBC and SHA256 */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_16); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ + + /* Proposal - DH Group 15 with AES128-CBC and SHA256 */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_15); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ + + /* Proposal - DH Group 14 with AES128-CBC and SHA256 */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ + + /* + * Proposal - DH Group 14 with AES128-CBC and SHA1 + * (mandatory to implement algorithms) + */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA1); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA1); /* MAC */ + + /* Server IDType + Identity */ + wpabuf_put_u8(msg, data->serverid_type); + wpabuf_put_data(msg, sm->server_id, sm->server_id_len); + + wpabuf_free(data->msgs); + data->msgs = wpabuf_dup(msg); + if (data->msgs == NULL) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +static struct wpabuf * eap_eke_build_commit(struct eap_sm *sm, + struct eap_eke_data *data, u8 id) +{ + struct wpabuf *msg; + u8 pub[EAP_EKE_MAX_DH_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Commit"); + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-EKE: Password with not configured"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return eap_eke_build_failure(data, id); + } + + if (eap_eke_derive_key(&data->sess, sm->user->password, + sm->user->password_len, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, data->key) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + msg = eap_eke_build_msg(data, id, data->sess.dhcomp_len, + EAP_EKE_COMMIT); + if (msg == NULL) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + /* + * y_s = g ^ x_s (mod p) + * x_s = random number 2 .. p-1 + * temp = prf(0+, password) + * key = prf+(temp, ID_S | ID_P) + * DHComponent_S = Encr(key, y_s) + */ + + if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + if (eap_eke_dhcomp(&data->sess, data->key, pub, + wpabuf_put(msg, data->sess.dhcomp_len)) + < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S"); + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + if (wpabuf_resize(&data->msgs, wpabuf_len(msg)) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpabuf_put_buf(data->msgs, msg); + + return msg; +} + + +static struct wpabuf * eap_eke_build_confirm(struct eap_sm *sm, + struct eap_eke_data *data, u8 id) +{ + struct wpabuf *msg; + size_t plen, prot_len; + u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN]; + u8 *auth; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Confirm"); + + plen = data->sess.pnonce_ps_len + data->sess.prf_len; + msg = eap_eke_build_msg(data, id, plen, EAP_EKE_CONFIRM); + if (msg == NULL) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + if (random_get_bytes(data->nonce_s, data->sess.nonce_len)) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S", + data->nonce_s, data->sess.nonce_len); + + os_memcpy(nonces, data->nonce_p, data->sess.nonce_len); + os_memcpy(nonces + data->sess.nonce_len, data->nonce_s, + data->sess.nonce_len); + prot_len = wpabuf_tailroom(msg); + if (eap_eke_prot(&data->sess, nonces, 2 * data->sess.nonce_len, + wpabuf_put(msg, 0), &prot_len) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpabuf_put(msg, prot_len); + + if (eap_eke_derive_ka(&data->sess, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, + data->nonce_p, data->nonce_s) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + auth = wpabuf_put(msg, data->sess.prf_len); + if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth, data->sess.prf_len); + + return msg; +} + + +static struct wpabuf * eap_eke_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_eke_data *data = priv; + + switch (data->state) { + case IDENTITY: + return eap_eke_build_identity(sm, data, id); + case COMMIT: + return eap_eke_build_commit(sm, data, id); + case CONFIRM: + return eap_eke_build_confirm(sm, data, id); + case FAILURE_REPORT: + return eap_eke_build_failure(data, id); + default: + wpa_printf(MSG_DEBUG, "EAP-EKE: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_eke_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_eke_data *data = priv; + size_t len; + const u8 *pos; + u8 eke_exch; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-EKE: Invalid frame"); + return TRUE; + } + + eke_exch = *pos; + wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: EKE-Exch=%d", eke_exch); + + if (data->state == IDENTITY && eke_exch == EAP_EKE_ID) + return FALSE; + + if (data->state == COMMIT && eke_exch == EAP_EKE_COMMIT) + return FALSE; + + if (data->state == CONFIRM && eke_exch == EAP_EKE_CONFIRM) + return FALSE; + + if (eke_exch == EAP_EKE_FAILURE) + return FALSE; + + wpa_printf(MSG_INFO, "EAP-EKE: Unexpected EKE-Exch=%d in state=%d", + eke_exch, data->state); + + return TRUE; +} + + +static void eap_eke_process_identity(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + const u8 *pos, *end; + int i; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Identity"); + + if (data->state != IDENTITY) { + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + pos = payload; + end = payload + payloadlen; + + if (pos + 2 + 4 + 1 > end) { + wpa_printf(MSG_INFO, "EAP-EKE: Too short EAP-EKE-ID payload"); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + if (*pos != 1) { + wpa_printf(MSG_INFO, "EAP-EKE: Unexpected NumProposals %d (expected 1)", + *pos); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + pos += 2; + + if (!supported_proposal(pos)) { + wpa_printf(MSG_INFO, "EAP-EKE: Unexpected Proposal (%u:%u:%u:%u)", + pos[0], pos[1], pos[2], pos[3]); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Selected Proposal (%u:%u:%u:%u)", + pos[0], pos[1], pos[2], pos[3]); + if (eap_eke_session_init(&data->sess, pos[0], pos[1], pos[2], pos[3]) < + 0) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + pos += 4; + + data->peerid_type = *pos++; + os_free(data->peerid); + data->peerid = os_malloc(end - pos); + if (data->peerid == NULL) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + os_memcpy(data->peerid, pos, end - pos); + data->peerid_len = end - pos; + wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity", + data->peerid, data->peerid_len); + + if (eap_user_get(sm, data->peerid, data->peerid_len, data->phase2)) { + wpa_printf(MSG_INFO, "EAP-EKE: Peer Identity not found from user database"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return; + } + + for (i = 0; i < EAP_MAX_METHODS; i++) { + if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && + sm->user->methods[i].method == EAP_TYPE_EKE) + break; + } + if (i == EAP_MAX_METHODS) { + wpa_printf(MSG_INFO, "EAP-EKE: Matching user entry does not allow EAP-EKE"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return; + } + + if (sm->user->password == NULL || sm->user->password_len == 0) { + wpa_printf(MSG_INFO, "EAP-EKE: No password configured for peer"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return; + } + + if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + wpabuf_put_buf(data->msgs, respData); + + eap_eke_state(data, COMMIT); +} + + +static void eap_eke_process_commit(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + const u8 *pos, *end, *dhcomp, *pnonce; + size_t decrypt_len; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Commit"); + + if (data->state != COMMIT) { + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + pos = payload; + end = payload + payloadlen; + + if (pos + data->sess.dhcomp_len + data->sess.pnonce_len > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", + pos, data->sess.dhcomp_len); + dhcomp = pos; + pos += data->sess.dhcomp_len; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", pos, data->sess.pnonce_len); + pnonce = pos; + pos += data->sess.pnonce_len; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos); + + if (eap_eke_shared_secret(&data->sess, data->key, data->dh_priv, dhcomp) + < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + + if (eap_eke_derive_ke_ki(&data->sess, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + + decrypt_len = sizeof(data->nonce_p); + if (eap_eke_decrypt_prot(&data->sess, pnonce, data->sess.pnonce_len, + data->nonce_p, &decrypt_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_P"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + if (decrypt_len < (size_t) data->sess.nonce_len) { + wpa_printf(MSG_INFO, "EAP-EKE: PNonce_P protected data too short to include Nonce_P"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", + data->nonce_p, data->sess.nonce_len); + + if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + wpabuf_put_buf(data->msgs, respData); + + eap_eke_state(data, CONFIRM); +} + + +static void eap_eke_process_confirm(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + size_t decrypt_len; + u8 nonce[EAP_EKE_MAX_NONCE_LEN]; + u8 auth_p[EAP_EKE_MAX_HASH_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); + + if (data->state != CONFIRM) { + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); + + if (payloadlen < (size_t) data->sess.pnonce_len + data->sess.prf_len) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm"); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + decrypt_len = sizeof(nonce); + if (eap_eke_decrypt_prot(&data->sess, payload, data->sess.pnonce_len, + nonce, &decrypt_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_S"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + if (decrypt_len < (size_t) data->sess.nonce_len) { + wpa_printf(MSG_INFO, "EAP-EKE: PNonce_S protected data too short to include Nonce_S"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_S", + nonce, data->sess.nonce_len); + if (os_memcmp(nonce, data->nonce_s, data->sess.nonce_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_S does not match previously sent Nonce_S"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + + if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth_p) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Could not derive Auth_P"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth_p, data->sess.prf_len); + if (os_memcmp(auth_p, payload + data->sess.pnonce_len, + data->sess.prf_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Auth_P does not match"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + + if (eap_eke_derive_msk(&data->sess, sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, + data->nonce_s, data->nonce_p, + data->msk, data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + + os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); + os_memset(data->key, 0, sizeof(data->key)); + eap_eke_session_clean(&data->sess); + + eap_eke_state(data, SUCCESS); +} + + +static void eap_eke_process_failure(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + u32 code; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Failure"); + + if (payloadlen < 4) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure"); + eap_eke_state(data, FAILURE); + return; + } + + code = WPA_GET_BE32(payload); + wpa_printf(MSG_DEBUG, "EAP-EKE: Peer reported failure code 0x%x", code); + + eap_eke_state(data, FAILURE); +} + + +static void eap_eke_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_eke_data *data = priv; + u8 eke_exch; + size_t len; + const u8 *pos, *end; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); + if (pos == NULL || len < 1) + return; + + eke_exch = *pos; + end = pos + len; + pos++; + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received payload", pos, end - pos); + + switch (eke_exch) { + case EAP_EKE_ID: + eap_eke_process_identity(sm, data, respData, pos, end - pos); + break; + case EAP_EKE_COMMIT: + eap_eke_process_commit(sm, data, respData, pos, end - pos); + break; + case EAP_EKE_CONFIRM: + eap_eke_process_confirm(sm, data, respData, pos, end - pos); + break; + case EAP_EKE_FAILURE: + eap_eke_process_failure(sm, data, respData, pos, end - pos); + break; + } +} + + +static Boolean eap_eke_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +static Boolean eap_eke_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_eke_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); + if (eap == NULL) + return -1; + + eap->init = eap_eke_init; + eap->reset = eap_eke_reset; + eap->buildReq = eap_eke_buildReq; + eap->check = eap_eke_check; + eap->process = eap_eke_process; + eap->isDone = eap_eke_isDone; + eap->getKey = eap_eke_getKey; + eap->isSuccess = eap_eke_isSuccess; + eap->get_emsk = eap_eke_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c index 2853c486..66f42715 100644 --- a/src/eap_server/eap_server_gpsk.c +++ b/src/eap_server/eap_server_gpsk.c @@ -26,8 +26,6 @@ struct eap_gpsk_data { size_t pk_len; u8 *id_peer; size_t id_peer_len; - u8 *id_server; - size_t id_server_len; #define MAX_NUM_CSUITES 2 struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES]; size_t csuite_count; @@ -71,11 +69,6 @@ static void * eap_gpsk_init(struct eap_sm *sm) return NULL; data->state = GPSK_1; - /* TODO: add support for configuring ID_Server */ - data->id_server = (u8 *) os_strdup("hostapd"); - if (data->id_server) - data->id_server_len = os_strlen((char *) data->id_server); - data->csuite_count = 0; if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF, EAP_GPSK_CIPHER_AES)) { @@ -101,7 +94,6 @@ static void * eap_gpsk_init(struct eap_sm *sm) static void eap_gpsk_reset(struct eap_sm *sm, void *priv) { struct eap_gpsk_data *data = priv; - os_free(data->id_server); os_free(data->id_peer); os_free(data); } @@ -123,7 +115,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server", data->rand_server, EAP_GPSK_RAND_LEN); - len = 1 + 2 + data->id_server_len + EAP_GPSK_RAND_LEN + 2 + + len = 1 + 2 + sm->server_id_len + EAP_GPSK_RAND_LEN + 2 + data->csuite_count * sizeof(struct eap_gpsk_csuite); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, EAP_CODE_REQUEST, id); @@ -135,8 +127,8 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, } wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1); - wpabuf_put_be16(req, data->id_server_len); - wpabuf_put_data(req, data->id_server, data->id_server_len); + wpabuf_put_be16(req, sm->server_id_len); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); wpabuf_put_be16(req, data->csuite_count * sizeof(struct eap_gpsk_csuite)); @@ -158,7 +150,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3"); miclen = eap_gpsk_mic_len(data->vendor, data->specifier); - len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + data->id_server_len + + len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + sm->server_id_len + sizeof(struct eap_gpsk_csuite) + 2 + miclen; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, EAP_CODE_REQUEST, id); @@ -174,8 +166,8 @@ static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN); wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); - wpabuf_put_be16(req, data->id_server_len); - wpabuf_put_data(req, data->id_server, data->id_server_len); + wpabuf_put_be16(req, sm->server_id_len); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); csuite = wpabuf_put(req, sizeof(*csuite)); WPA_PUT_BE32(csuite->vendor, data->vendor); WPA_PUT_BE16(csuite->specifier, data->specifier); @@ -301,8 +293,8 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, eap_gpsk_state(data, FAILURE); return; } - if (alen != data->id_server_len || - os_memcmp(pos, data->id_server, alen) != 0) { + if (alen != sm->server_id_len || + os_memcmp(pos, sm->server_id, alen) != 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and " "GPSK-2 did not match"); eap_gpsk_state(data, FAILURE); @@ -416,7 +408,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, data->vendor, data->specifier, data->rand_peer, data->rand_server, data->id_peer, data->id_peer_len, - data->id_server, data->id_server_len, + sm->server_id, sm->server_id_len, data->msk, data->emsk, data->sk, &data->sk_len, data->pk, &data->pk_len) < 0) { diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c index 42aaca2b..1ada0c8a 100644 --- a/src/eap_server/eap_server_ikev2.c +++ b/src/eap_server/eap_server_ikev2.c @@ -103,8 +103,11 @@ static void * eap_ikev2_init(struct eap_sm *sm) data->ikev2.proposal.encr = ENCR_AES_CBC; data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP; - data->ikev2.IDi = (u8 *) os_strdup("hostapd"); - data->ikev2.IDi_len = 7; + data->ikev2.IDi = os_malloc(sm->server_id_len); + if (data->ikev2.IDi == NULL) + goto failed; + os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len); + data->ikev2.IDi_len = sm->server_id_len; data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret; data->ikev2.cb_ctx = sm; diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index 8d3dd523..3153d2ec 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -100,7 +100,6 @@ static struct wpabuf * eap_mschapv2_build_challenge( { struct wpabuf *req; struct eap_mschapv2_hdr *ms; - char *name = "hostapd"; /* TODO: make this configurable */ size_t ms_len; if (!data->auth_challenge_from_tls && @@ -111,7 +110,7 @@ static struct wpabuf * eap_mschapv2_build_challenge( return NULL; } - ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name); + ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, EAP_CODE_REQUEST, id); if (req == NULL) { @@ -133,7 +132,7 @@ static struct wpabuf * eap_mschapv2_build_challenge( wpabuf_put(req, CHALLENGE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", data->auth_challenge, CHALLENGE_LEN); - wpabuf_put_data(req, name, os_strlen(name)); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); return req; } diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c index 0cd97992..46bedd94 100644 --- a/src/eap_server/eap_server_psk.c +++ b/src/eap_server/eap_server_psk.c @@ -22,8 +22,8 @@ struct eap_psk_data { enum { PSK_1, PSK_3, SUCCESS, FAILURE } state; u8 rand_s[EAP_PSK_RAND_LEN]; u8 rand_p[EAP_PSK_RAND_LEN]; - u8 *id_p, *id_s; - size_t id_p_len, id_s_len; + u8 *id_p; + size_t id_p_len; u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; @@ -38,8 +38,6 @@ static void * eap_psk_init(struct eap_sm *sm) if (data == NULL) return NULL; data->state = PSK_1; - data->id_s = (u8 *) "hostapd"; - data->id_s_len = 7; return data; } @@ -70,7 +68,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm, data->rand_s, EAP_PSK_RAND_LEN); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, - sizeof(*psk) + data->id_s_len, + sizeof(*psk) + sm->server_id_len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " @@ -82,7 +80,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm, psk = wpabuf_put(req, sizeof(*psk)); psk->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */ os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN); - wpabuf_put_data(req, data->id_s, data->id_s_len); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); return req; } @@ -112,13 +110,13 @@ static struct wpabuf * eap_psk_build_3(struct eap_sm *sm, os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN); /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ - buflen = data->id_s_len + EAP_PSK_RAND_LEN; + buflen = sm->server_id_len + EAP_PSK_RAND_LEN; buf = os_malloc(buflen); if (buf == NULL) goto fail; - os_memcpy(buf, data->id_s, data->id_s_len); - os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); + os_memcpy(buf, sm->server_id, sm->server_id_len); + os_memcpy(buf + sm->server_id_len, data->rand_p, EAP_PSK_RAND_LEN); if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) { os_free(buf); goto fail; @@ -296,7 +294,7 @@ static void eap_psk_process_2(struct eap_sm *sm, os_memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN); /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ - buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; + buflen = data->id_p_len + sm->server_id_len + 2 * EAP_PSK_RAND_LEN; buf = os_malloc(buflen); if (buf == NULL) { data->state = FAILURE; @@ -304,8 +302,8 @@ static void eap_psk_process_2(struct eap_sm *sm, } os_memcpy(buf, data->id_p, data->id_p_len); pos = buf + data->id_p_len; - os_memcpy(pos, data->id_s, data->id_s_len); - pos += data->id_s_len; + os_memcpy(pos, sm->server_id, sm->server_id_len); + pos += sm->server_id_len; os_memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN); pos += EAP_PSK_RAND_LEN; os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c index f72e1bf5..68dd76b1 100644 --- a/src/eap_server/eap_server_sake.c +++ b/src/eap_server/eap_server_sake.c @@ -27,8 +27,6 @@ struct eap_sake_data { u8 session_id; u8 *peerid; size_t peerid_len; - u8 *serverid; - size_t serverid_len; }; @@ -77,11 +75,6 @@ static void * eap_sake_init(struct eap_sm *sm) wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d", data->session_id); - /* TODO: add support for configuring SERVERID */ - data->serverid = (u8 *) os_strdup("hostapd"); - if (data->serverid) - data->serverid_len = os_strlen((char *) data->serverid); - return data; } @@ -89,7 +82,6 @@ static void * eap_sake_init(struct eap_sm *sm) static void eap_sake_reset(struct eap_sm *sm, void *priv) { struct eap_sake_data *data = priv; - os_free(data->serverid); os_free(data->peerid); os_free(data); } @@ -131,8 +123,7 @@ static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity"); plen = 4; - if (data->serverid) - plen += 2 + data->serverid_len; + plen += 2 + sm->server_id_len; msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY); if (msg == NULL) { data->state = FAILURE; @@ -142,11 +133,9 @@ static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ"); eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2); - if (data->serverid) { - wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); - eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, - data->serverid, data->serverid_len); - } + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); + eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, + sm->server_id, sm->server_id_len); return msg; } @@ -169,9 +158,7 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm, wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", data->rand_s, EAP_SAKE_RAND_LEN); - plen = 2 + EAP_SAKE_RAND_LEN; - if (data->serverid) - plen += 2 + data->serverid_len; + plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->server_id_len; msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE); if (msg == NULL) { data->state = FAILURE; @@ -182,11 +169,9 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm, eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S, data->rand_s, EAP_SAKE_RAND_LEN); - if (data->serverid) { - wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); - eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, - data->serverid, data->serverid_len); - } + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); + eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, + sm->server_id, sm->server_id_len); return msg; } @@ -213,7 +198,7 @@ static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm, wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN); mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN); if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - data->serverid, data->serverid_len, + sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 0, wpabuf_head(msg), wpabuf_len(msg), mic, mic)) { @@ -362,7 +347,7 @@ static void eap_sake_process_challenge(struct eap_sm *sm, (u8 *) &data->tek, data->msk, data->emsk); eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - data->serverid, data->serverid_len, + sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 1, wpabuf_head(respData), wpabuf_len(respData), attr.mic_p, mic_p); @@ -399,7 +384,7 @@ static void eap_sake_process_confirm(struct eap_sm *sm, } eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - data->serverid, data->serverid_len, + sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 1, wpabuf_head(respData), wpabuf_len(respData), attr.mic_p, mic_p); diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index c3ccb46b..013d781a 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -830,6 +830,8 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.fragment_size = eapol->conf.fragment_size; eap_conf.pwd_group = eapol->conf.pwd_group; eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1; + eap_conf.server_id = eapol->conf.server_id; + eap_conf.server_id_len = eapol->conf.server_id_len; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -1045,6 +1047,8 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, os_free(dst->eap_req_id_text); dst->pwd_group = src->pwd_group; dst->pbc_in_m1 = src->pbc_in_m1; + dst->server_id = src->server_id; + dst->server_id_len = src->server_id_len; if (src->eap_req_id_text) { dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); if (dst->eap_req_id_text == NULL) diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h index b50bbdd0..3a0f4509 100644 --- a/src/eapol_auth/eapol_auth_sm.h +++ b/src/eapol_auth/eapol_auth_sm.h @@ -37,6 +37,8 @@ struct eapol_auth_config { int fragment_size; u16 pwd_group; int pbc_in_m1; + const u8 *server_id; + size_t server_id_len; /* Opaque context pointer to owner data for callback functions */ void *ctx; diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 0534b8ce..0a414eeb 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -1967,39 +1967,6 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) } -static int is_11b(u8 rate) -{ - return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; -} - - -static 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; -} - - static enum p2p_probe_req_status p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len) diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 5b2d7111..0144c9f8 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -223,6 +223,11 @@ struct radius_server_data { u16 pwd_group; /** + * server_id - Server identity + */ + const char *server_id; + + /** * wps - Wi-Fi Protected Setup context * * If WPS is used with an external RADIUS server (which is quite @@ -511,6 +516,8 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.tnc = data->tnc; eap_conf.wps = data->wps; eap_conf.pwd_group = data->pwd_group; + eap_conf.server_id = (const u8 *) data->server_id; + eap_conf.server_id_len = os_strlen(data->server_id); sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); if (sess->eap == NULL) { @@ -1280,6 +1287,7 @@ radius_server_init(struct radius_server_conf *conf) data->tnc = conf->tnc; data->wps = conf->wps; data->pwd_group = conf->pwd_group; + data->server_id = conf->server_id; if (conf->eap_req_id_text) { data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len); if (data->eap_req_id_text) { diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index 82466c30..284bd59d 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -144,6 +144,11 @@ struct radius_server_conf { u16 pwd_group; /** + * server_id - Server identity + */ + const char *server_id; + + /** * wps - Wi-Fi Protected Setup context * * If WPS is used with an external RADIUS server (which is quite diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index 93056ea8..33fa1a29 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -15,7 +15,7 @@ #include "wpa_i.h" #include "pmksa_cache.h" -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL static const int pmksa_cache_max_entries = 32; @@ -522,4 +522,4 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, return pmksa; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h index d5aa229a..6cbf89aa 100644 --- a/src/rsn_supp/pmksa_cache.h +++ b/src/rsn_supp/pmksa_cache.h @@ -44,7 +44,7 @@ enum pmksa_free_reason { PMKSA_EXPIRE, }; -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, @@ -69,7 +69,7 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *pmk, size_t pmk_len); -#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#else /* IEEE8021X_EAPOL */ static inline struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, @@ -122,10 +122,11 @@ static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, } static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, - void *network_ctx) + void *network_ctx, + const u8 *pmk, size_t pmk_len) { } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ #endif /* PMKSA_CACHE_H */ diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c index ab61867b..c51620eb 100644 --- a/src/rsn_supp/preauth.c +++ b/src/rsn_supp/preauth.c @@ -18,7 +18,7 @@ #include "wpa_i.h" -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL #define PMKID_CANDIDATE_PRIO_SCAN 1000 @@ -508,4 +508,4 @@ int rsn_preauth_in_progress(struct wpa_sm *sm) return sm->preauth_eapol != NULL; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ diff --git a/src/rsn_supp/preauth.h b/src/rsn_supp/preauth.h index 27d3112c..277f0663 100644 --- a/src/rsn_supp/preauth.h +++ b/src/rsn_supp/preauth.h @@ -11,7 +11,7 @@ struct wpa_scan_results; -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL void pmksa_candidate_free(struct wpa_sm *sm); int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, @@ -27,7 +27,7 @@ int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose); int rsn_preauth_in_progress(struct wpa_sm *sm); -#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#else /* IEEE8021X_EAPOL */ static inline void pmksa_candidate_free(struct wpa_sm *sm) { @@ -74,6 +74,6 @@ static inline int rsn_preauth_in_progress(struct wpa_sm *sm) return 0; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ #endif /* PREAUTH_H */ diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 221d5fdd..539aa256 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -1347,7 +1347,8 @@ static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, peer->supp_rates_len = merge_byte_arrays( peer->supp_rates, sizeof(peer->supp_rates), kde->supp_rates + 2, kde->supp_rates_len - 2, - kde->ext_supp_rates + 2, kde->ext_supp_rates_len - 2); + kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL, + kde->ext_supp_rates_len - 2); return 0; } diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index d83700a2..292255c1 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -392,7 +392,6 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, os_memset(&ie, 0, sizeof(ie)); -#ifndef CONFIG_NO_WPA2 if (sm->proto == WPA_PROTO_RSN) { /* RSN: msg 1/4 should contain PMKID for the selected PMK */ const u8 *_buf = (const u8 *) (key + 1); @@ -405,7 +404,6 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, "Authenticator", ie.pmkid, PMKID_LEN); } } -#endif /* CONFIG_NO_WPA2 */ res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); if (res == -2) { @@ -664,7 +662,6 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const u8 *gtk, size_t gtk_len, int key_info) { -#ifndef CONFIG_NO_WPA2 struct wpa_gtk_data gd; /* @@ -703,9 +700,6 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, wpa_supplicant_key_neg_complete(sm, sm->bssid, key_info & WPA_KEY_INFO_SECURE); return 0; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } @@ -2601,11 +2595,7 @@ 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) { -#ifndef CONFIG_NO_WPA2 return pmksa_cache_list(sm->pmksa, buf, len); -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } @@ -2636,9 +2626,7 @@ 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) { -#ifndef CONFIG_NO_WPA2 pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0); -#endif /* CONFIG_NO_WPA2 */ } diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index c757dcf1..26e9c6ca 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -245,6 +245,11 @@ static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf, return 0; } +static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm) +{ + return 0; +} + static inline void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index ba203e62..50b9272b 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -107,7 +107,6 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, int key_mgmt, int mgmt_group_cipher, struct wpa_sm *sm) { -#ifndef CONFIG_NO_WPA2 u8 *pos; struct rsn_ie_hdr *hdr; u16 capab; @@ -220,9 +219,6 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len); return pos - rsn_ie; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } diff --git a/src/utils/build_config.h b/src/utils/build_config.h index f9473885..c6f4e437 100644 --- a/src/utils/build_config.h +++ b/src/utils/build_config.h @@ -47,31 +47,4 @@ #endif /* USE_INTERNAL_CRYPTO */ #endif /* CONFIG_WIN32_DEFAULTS */ -#ifdef CONFIG_XCODE_DEFAULTS -#define CONFIG_DRIVER_OSX -#define CONFIG_BACKEND_FILE -#define IEEE8021X_EAPOL -#define PKCS12_FUNCS -#define CONFIG_CTRL_IFACE -#define CONFIG_CTRL_IFACE_UNIX -#define CONFIG_DEBUG_FILE -#define EAP_MD5 -#define EAP_TLS -#define EAP_MSCHAPv2 -#define EAP_PEAP -#define EAP_TTLS -#define EAP_GTC -#define EAP_OTP -#define EAP_LEAP -#define EAP_TNC -#define CONFIG_WPS -#define EAP_WSC - -#ifdef USE_INTERNAL_CRYPTO -#define CONFIG_TLS_INTERNAL_CLIENT -#define CONFIG_INTERNAL_LIBTOMMATH -#define CONFIG_CRYPTO_INTERNAL -#endif /* USE_INTERNAL_CRYPTO */ -#endif /* CONFIG_XCODE_DEFAULTS */ - #endif /* BUILD_CONFIG_H */ diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index af63e4db..bea2b331 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -982,6 +982,7 @@ static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) 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); @@ -995,7 +996,6 @@ static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) if (sm->multicast_sd >= 0) close(sm->multicast_sd); sm->multicast_sd = -1; - ssdp_listener_stop(sm); sm->started = 0; } |
