diff options
78 files changed, 4127 insertions, 381 deletions
diff --git a/hostapd/Android.mk b/hostapd/Android.mk index 1c2e1f5b..1054dc00 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -367,6 +367,13 @@ OBJS += src/eap_server/eap_server_pwd.c src/eap_common/eap_pwd_common.c NEED_SHA256=y endif +ifdef CONFIG_EAP_EKE +L_CFLAGS += -DEAP_SERVER_EKE +OBJS += src/eap_server/eap_server_eke.c src/eap_common/eap_eke_common.c +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +endif + ifdef CONFIG_EAP_VENDOR_TEST L_CFLAGS += -DEAP_SERVER_VENDOR_TEST OBJS += src/eap_server/eap_server_vendor_test.c diff --git a/hostapd/Makefile b/hostapd/Makefile index 26cc2b51..a30a2442 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -323,6 +323,13 @@ OBJS += ../src/eap_server/eap_server_pwd.o ../src/eap_common/eap_pwd_common.o NEED_SHA256=y endif +ifdef CONFIG_EAP_EKE +CFLAGS += -DEAP_SERVER_EKE +OBJS += ../src/eap_server/eap_server_eke.o ../src/eap_common/eap_eke_common.o +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +endif + ifdef CONFIG_EAP_VENDOR_TEST CFLAGS += -DEAP_SERVER_VENDOR_TEST OBJS += ../src/eap_server/eap_server_vendor_test.o diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 231b0f9f..8e6f35a5 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -1689,6 +1689,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, sizeof(conf->bss[0].iface)); } else if (os_strcmp(buf, "bridge") == 0) { os_strlcpy(bss->bridge, pos, sizeof(bss->bridge)); + } else if (os_strcmp(buf, "vlan_bridge") == 0) { + os_strlcpy(bss->vlan_bridge, pos, + sizeof(bss->vlan_bridge)); } else if (os_strcmp(buf, "wds_bridge") == 0) { os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge)); @@ -1830,6 +1833,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->private_key_passwd = os_strdup(pos); } else if (os_strcmp(buf, "check_crl") == 0) { bss->check_crl = atoi(pos); + } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) { + os_free(bss->ocsp_stapling_response); + bss->ocsp_stapling_response = os_strdup(pos); } else if (os_strcmp(buf, "dh_file") == 0) { os_free(bss->dh_file); bss->dh_file = os_strdup(pos); @@ -2628,6 +2634,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->upc = os_strdup(pos); } else if (os_strcmp(buf, "pbc_in_m1") == 0) { bss->pbc_in_m1 = atoi(pos); + } else if (os_strcmp(buf, "server_id") == 0) { + os_free(bss->server_id); + bss->server_id = os_strdup(pos); #ifdef CONFIG_WPS_NFC } else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) { bss->wps_nfc_dev_pw_id = atoi(pos); diff --git a/hostapd/defconfig b/hostapd/defconfig index 317fe740..c288f466 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -117,6 +117,9 @@ CONFIG_EAP_TTLS=y # Trusted Network Connect (EAP-TNC) #CONFIG_EAP_TNC=y +# EAP-EKE for the integrated EAP server +#CONFIG_EAP_EKE=y + # PKCS#12 (PFX) support (used to read private key and certificate file from # a file that usually has extension .p12 or .pfx) CONFIG_PKCS12=y diff --git a/hostapd/eap_register.c b/hostapd/eap_register.c index 0a7ff918..981e5394 100644 --- a/hostapd/eap_register.c +++ b/hostapd/eap_register.c @@ -134,5 +134,10 @@ int eap_server_register_methods(void) ret = eap_server_pwd_register(); #endif /* EAP_SERVER_PWD */ +#ifdef EAP_SERVER_EKE + if (ret == 0) + ret = eap_server_eke_register(); +#endif /* EAP_SERVER_EKE */ + return ret; } diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index be15b860..68c40694 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -666,6 +666,11 @@ eap_server=0 # Passphrase for private key #private_key_passwd=secret passphrase +# Server identity +# EAP methods that provide mechanism for authenticated server identity delivery +# use this value. If not set, "hostapd" is used as a default. +#server_id=server.example.com + # Enable CRL verification. # Note: hostapd does not yet support CRL downloading based on CDP. Thus, a # valid CRL signed by the CA is required to be included in the ca_cert file. @@ -677,6 +682,20 @@ eap_server=0 # 2 = check all CRLs in the certificate path #check_crl=1 +# Cached OCSP stapling response (DER encoded) +# If set, this file is sent as a certificate status response by the EAP server +# if the EAP peer requests certificate status in the ClientHello message. +# This cache file can be updated, e.g., by running following command +# periodically to get an update from the OCSP responder: +# openssl ocsp \ +# -no_nonce \ +# -CAfile /etc/hostapd.ca.pem \ +# -issuer /etc/hostapd.ca.pem \ +# -cert /etc/hostapd.server.pem \ +# -url http://ocsp.example.com:8888/ \ +# -respout /tmp/ocsp-cache.der +#ocsp_stapling_response=/tmp/ocsp-cache.der + # dh_file: File path to DH/DSA parameters file (in PEM format) # This is an optional configuration file for setting parameters for an # ephemeral DH key exchange. In most cases, the default RSA authentication does @@ -844,6 +863,12 @@ own_ip_addr=127.0.0.1 # to the bridge. #vlan_tagged_interface=eth0 +# Bridge (prefix) to add the wifi and the tagged interface to. This gets the +# VLAN ID appended. It defaults to brvlan%d if no tagged interface is given +# and br%s.%d if a tagged interface is given, provided %s = tagged interface +# and %d = VLAN ID. +#vlan_bridge=brvlan + # When hostapd creates a VLAN interface on vlan_tagged_interfaces, it needs # to know how to name it. # 0 = vlan<XXX>, e.g., vlan1 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; } diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index a0c97f07..8515b5a8 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -236,7 +236,7 @@ NEED_SHA1=y NEED_MD5=y NEED_RC4=y else -L_CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2 +L_CFLAGS += -DCONFIG_NO_WPA endif ifdef CONFIG_IBSS_RSN @@ -286,10 +286,6 @@ L_CFLAGS += -DCONFIG_INTERWORKING NEED_GAS=y endif -ifdef CONFIG_NO_WPA2 -L_CFLAGS += -DCONFIG_NO_WPA2 -endif - include $(LOCAL_PATH)/src/drivers/drivers.mk ifdef CONFIG_AP @@ -601,6 +597,22 @@ CONFIG_IEEE8021X_EAPOL=y NEED_SHA256=y endif +ifdef CONFIG_EAP_EKE +# EAP-EKE +ifeq ($(CONFIG_EAP_EKE), dyn) +L_CFLAGS += -DEAP_EKE_DYNAMIC +EAPDYN += src/eap_peer/eap_eke.so +else +L_CFLAGS += -DEAP_EKE +OBJS += src/eap_peer/eap_eke.c src/eap_common/eap_eke_common.c +OBJS_h += src/eap_server/eap_server_eke.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_SHA256=y +endif + ifdef CONFIG_WPS ifdef CONFIG_WPS2 L_CFLAGS += -DCONFIG_WPS2 diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 0634219b..56986196 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -215,7 +215,7 @@ NEED_SHA1=y NEED_MD5=y NEED_RC4=y else -CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2 +CFLAGS += -DCONFIG_NO_WPA endif ifdef CONFIG_IBSS_RSN @@ -265,10 +265,6 @@ CFLAGS += -DCONFIG_INTERWORKING NEED_GAS=y endif -ifdef CONFIG_NO_WPA2 -CFLAGS += -DCONFIG_NO_WPA2 -endif - include ../src/drivers/drivers.mak ifdef CONFIG_AP OBJS_d += $(DRV_BOTH_OBJS) @@ -579,6 +575,22 @@ CONFIG_IEEE8021X_EAPOL=y NEED_SHA256=y endif +ifdef CONFIG_EAP_EKE +# EAP-EKE +ifeq ($(CONFIG_EAP_EKE), dyn) +CFLAGS += -DEAP_EKE_DYNAMIC +EAPDYN += ../src/eap_peer/eap_eke.so +else +CFLAGS += -DEAP_EKE +OBJS += ../src/eap_peer/eap_eke.o ../src/eap_common/eap_eke_common.o +OBJS_h += ../src/eap_server/eap_server_eke.o +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_SHA256=y +endif + ifdef CONFIG_WPS ifdef CONFIG_WPS2 CFLAGS += -DCONFIG_WPS2 @@ -1581,6 +1593,10 @@ eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_com $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ -Deap_peer_ikev2_register=eap_peer_method_dynamic_init +eap_eke.so: ../src/eap_peer/eap_eke.c ../src/eap_common/eap_eke_common.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_eke_register=eap_peer_method_dynamic_init + %.so: %.c $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \ -D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config index 331940c8..8b3d6b48 100644 --- a/wpa_supplicant/android.config +++ b/wpa_supplicant/android.config @@ -262,11 +262,6 @@ CONFIG_WPA_CLI_EDIT=y # 35-50 kB in code size. #CONFIG_NO_WPA=y -# Remove WPA2 support. This allows WPA to be used, but removes WPA2 code to -# save about 1 kB in code size when building only WPA-Personal (no EAP support) -# or 6 kB if building for WPA-Enterprise. -#CONFIG_NO_WPA2=y - # Remove IEEE 802.11i/WPA-Personal ASCII passphrase support # This option can be used to reduce code size by removing support for # converting ASCII passphrases into PSK. If this functionality is removed, the diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index d9e1f82f..c48a2865 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -124,7 +124,9 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_P2P - if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) { + if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G && + (ssid->mode == WPAS_MODE_P2P_GO || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)) { /* Remove 802.11b rates from supported and basic rate sets */ int *list = os_malloc(4 * sizeof(int)); if (list) { @@ -492,6 +494,11 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, if (wpa_drv_associate(wpa_s, ¶ms) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality"); +#ifdef CONFIG_P2P + if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION && + wpa_s->global->p2p_group_formation == wpa_s) + wpas_p2p_group_formation_failed(wpa_s->parent); +#endif /* CONFIG_P2P */ return -1; } diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 7a860b66..a35be516 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1522,6 +1522,7 @@ static const struct parse_data ssid_fields[] = { { INT(eap_workaround) }, { STRe(pac_file) }, { INTe(fragment_size) }, + { INTe(ocsp) }, #endif /* IEEE8021X_EAPOL */ { INT_RANGE(mode, 0, 4) }, { INT_RANGE(proactive_key_caching, 0, 1) }, diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index a354a11e..a24abaf1 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -5018,23 +5018,76 @@ static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd #endif /* CONFIG_WNM */ +/* Get string representation of channel width */ +static const char * channel_width_name(enum chan_width width) +{ + switch (width) { + case CHAN_WIDTH_20_NOHT: + return "20 MHz (no HT)"; + case CHAN_WIDTH_20: + return "20 MHz"; + case CHAN_WIDTH_40: + return "40 MHz"; + case CHAN_WIDTH_80: + return "80 MHz"; + case CHAN_WIDTH_80P80: + return "80+80 MHz"; + case CHAN_WIDTH_160: + return "160 MHz"; + default: + return "unknown"; + } +} + + static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { struct wpa_signal_info si; int ret; + char *pos, *end; ret = wpa_drv_signal_poll(wpa_s, &si); if (ret) return -1; - ret = os_snprintf(buf, buflen, "RSSI=%d\nLINKSPEED=%d\n" + pos = buf; + end = buf + buflen; + + ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%d\n" "NOISE=%d\nFREQUENCY=%u\n", si.current_signal, si.current_txrate / 1000, si.current_noise, si.frequency); - if (ret < 0 || (unsigned int) ret > buflen) + if (ret < 0 || ret > end - pos) return -1; - return ret; + pos += ret; + + if (si.chanwidth != CHAN_WIDTH_UNKNOWN) { + ret = os_snprintf(pos, end - pos, "WIDTH=%s\n", + channel_width_name(si.chanwidth)); + if (ret < 0 || ret > end - pos) + return -1; + pos += ret; + } + + if (si.center_frq1 > 0 && si.center_frq2 > 0) { + ret = os_snprintf(pos, end - pos, + "CENTER_FRQ1=%d\nCENTER_FRQ2=%d\n", + si.center_frq1, si.center_frq2); + if (ret < 0 || ret > end - pos) + return -1; + pos += ret; + } + + if (si.avg_signal) { + ret = os_snprintf(pos, end - pos, + "AVG_RSSI=%d\n", si.avg_signal); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + + return pos - buf; } diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c index 1b4b9b0c..fc0d649e 100644 --- a/wpa_supplicant/ctrl_iface_unix.c +++ b/wpa_supplicant/ctrl_iface_unix.c @@ -602,7 +602,7 @@ static void wpa_supplicant_ctrl_iface_send(const char *ifname, int sock, offsetof(struct sockaddr_un, sun_path)); msg.msg_name = (void *) &dst->addr; msg.msg_namelen = dst->addrlen; - if (sendmsg(sock, &msg, 0) < 0) { + if (sendmsg(sock, &msg, MSG_DONTWAIT) < 0) { int _errno = errno; wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: " "%d - %s", diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index e9bd51f5..ddd2c825 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -869,6 +869,76 @@ nomem: } +/** + * wpas_dbus_signal_sta - Send a station related event signal + * @wpa_s: %wpa_supplicant network interface data + * @sta: station mac address + * @sig_name: signal name - StaAuthorized or StaDeauthorized + * + * Notify listeners about event related with station + */ +static void wpas_dbus_signal_sta(struct wpa_supplicant *wpa_s, + const u8 *sta, const char *sig_name) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + char sta_mac[WPAS_DBUS_OBJECT_PATH_MAX]; + char *dev_mac; + + os_snprintf(sta_mac, WPAS_DBUS_OBJECT_PATH_MAX, MACSTR, MAC2STR(sta)); + dev_mac = sta_mac; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, sig_name); + if (msg == NULL) + return; + + if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &dev_mac, + DBUS_TYPE_INVALID)) + dbus_connection_send(iface->con, msg, NULL); + else + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + dbus_message_unref(msg); + + wpa_printf(MSG_DEBUG, "dbus: Station MAC address '%s' '%s'", + sta_mac, sig_name); +} + + +/** + * wpas_dbus_signal_sta_authorized - Send a STA authorized signal + * @wpa_s: %wpa_supplicant network interface data + * @sta: station mac address + * + * Notify listeners a new station has been authorized + */ +void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ + wpas_dbus_signal_sta(wpa_s, sta, "StaAuthorized"); +} + + +/** + * wpas_dbus_signal_sta_deauthorized - Send a STA deauthorized signal + * @wpa_s: %wpa_supplicant network interface data + * @sta: station mac address + * + * Notify listeners a station has been deauthorized + */ +void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ + wpas_dbus_signal_sta(wpa_s, sta, "StaDeauthorized"); +} + + #ifdef CONFIG_P2P /** @@ -1920,15 +1990,6 @@ static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = { END_ARGS } }, -#ifdef CONFIG_AUTOSCAN - { "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_autoscan, - { - { "arg", "s", ARG_IN }, - END_ARGS - } - }, -#endif /* CONFIG_AUTOSCAN */ { NULL, NULL, NULL, { END_ARGS } } }; @@ -2666,6 +2727,15 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, +#ifdef CONFIG_AUTOSCAN + { "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_autoscan, + { + { "arg", "s", ARG_IN }, + END_ARGS + } + }, +#endif /* CONFIG_AUTOSCAN */ { NULL, NULL, NULL, { END_ARGS } } }; @@ -3017,6 +3087,18 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "StaAuthorized", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "name", "s", ARG_OUT }, + END_ARGS + } + }, + { "StaDeauthorized", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "name", "s", ARG_OUT }, + END_ARGS + } + }, { NULL, NULL, { END_ARGS } } }; diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h index 363a7e5d..61c480a6 100644 --- a/wpa_supplicant/dbus/dbus_new.h +++ b/wpa_supplicant/dbus/dbus_new.h @@ -222,6 +222,10 @@ void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, const u8 *ie, size_t ie_len, u32 ssi_signal); void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, const char *status, const char *parameter); +void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *sta); +void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, + const u8 *sta); #else /* CONFIG_CTRL_IFACE_DBUS_NEW */ @@ -493,6 +497,18 @@ static inline void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, { } +static inline +void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ +} + +static inline +void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ +} + #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #endif /* CTRL_IFACE_DBUS_H_NEW */ diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 5663a5fc..aa6005f7 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -210,6 +210,9 @@ CONFIG_EAP_LEAP=y # EAP-IKEv2 #CONFIG_EAP_IKEV2=y +# EAP-EKE +#CONFIG_EAP_EKE=y + # PKCS#12 (PFX) support (used to read private key and certificate file from # a file that usually has extension .p12 or .pfx) CONFIG_PKCS12=y @@ -261,11 +264,6 @@ CONFIG_CTRL_IFACE=y # 35-50 kB in code size. #CONFIG_NO_WPA=y -# Remove WPA2 support. This allows WPA to be used, but removes WPA2 code to -# save about 1 kB in code size when building only WPA-Personal (no EAP support) -# or 6 kB if building for WPA-Enterprise. -#CONFIG_NO_WPA2=y - # Remove IEEE 802.11i/WPA-Personal ASCII passphrase support # This option can be used to reduce code size by removing support for # converting ASCII passphrases into PSK. If this functionality is removed, the diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 6bab19cf..9922e072 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -129,6 +129,16 @@ static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, return -1; } +static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s, + const u8 *addr, int reason_code) +{ + if (wpa_s->driver->sta_deauth) { + return wpa_s->driver->sta_deauth(wpa_s->drv_priv, NULL, addr, + reason_code); + } + return -1; +} + static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *addr, int reason_code) { diff --git a/wpa_supplicant/eap_register.c b/wpa_supplicant/eap_register.c index d1eb4ff3..6cd2fc50 100644 --- a/wpa_supplicant/eap_register.c +++ b/wpa_supplicant/eap_register.c @@ -135,6 +135,11 @@ int eap_register_methods(void) ret = eap_peer_pwd_register(); #endif /* EAP_PWD */ +#ifdef EAP_EKE + if (ret == 0) + ret = eap_peer_eke_register(); +#endif /* EAP_EKE */ + #ifdef EAP_SERVER_IDENTITY if (ret == 0) ret = eap_server_identity_register(); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 60e71b69..d39216f2 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -44,8 +44,10 @@ #include "interworking.h" +#ifndef CONFIG_NO_SCAN_PROCESSING static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, int new_scan); +#endif /* CONFIG_NO_SCAN_PROCESSING */ static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, @@ -1957,7 +1959,9 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, int authenticating; u8 prev_pending_bssid[ETH_ALEN]; struct wpa_bss *fast_reconnect = NULL; +#ifndef CONFIG_NO_SCAN_PROCESSING struct wpa_ssid *fast_reconnect_ssid = NULL; +#endif /* CONFIG_NO_SCAN_PROCESSING */ struct wpa_ssid *last_ssid; authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING; @@ -1998,7 +2002,9 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, * time for some common cases. */ fast_reconnect = wpa_s->current_bss; +#ifndef CONFIG_NO_SCAN_PROCESSING fast_reconnect_ssid = wpa_s->current_ssid; +#endif /* CONFIG_NO_SCAN_PROCESSING */ } else if (wpa_s->wpa_state >= WPA_ASSOCIATING) #ifdef ANDROID wpa_supplicant_req_scan(wpa_s, 0, 500000); diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c index 046f181f..687c042f 100644 --- a/wpa_supplicant/ibss_rsn.c +++ b/wpa_supplicant/ibss_rsn.c @@ -296,6 +296,13 @@ static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, } +static void ibss_rsn_disconnect(void *ctx, const u8 *addr, u16 reason) +{ + struct ibss_rsn *ibss_rsn = ctx; + wpa_drv_sta_deauth(ibss_rsn->wpa_s, addr, reason); +} + + static int auth_for_each_sta(void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) @@ -386,6 +393,7 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, cb.get_psk = auth_get_psk; cb.set_key = auth_set_key; cb.for_each_sta = auth_for_each_sta; + cb.disconnect = ibss_rsn_disconnect; ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb); if (ibss_rsn->auth_group == NULL) { diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index e35628b2..36f75a16 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -599,19 +599,29 @@ static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred, static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) { - u8 plmn[3]; + u8 plmn[3], plmn2[3]; const u8 *pos, *end; u8 udhl; - /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */ + /* + * See Annex A of 3GPP TS 24.234 v8.1.0 for description. The network + * operator is allowed to include only two digits of the MNC, so allow + * matches based on both two and three digit MNC assumptions. Since some + * SIM/USIM cards may not expose MNC length conveniently, we may be + * provided the default MNC length 3 here and as such, checking with MNC + * length 2 is justifiable even though 3GPP TS 24.234 does not mention + * that case. Anyway, MCC/MNC pair where both 2 and 3 digit MNC is used + * with otherwise matching values would not be good idea in general, so + * this should not result in selecting incorrect networks. + */ + /* Match with 3 digit MNC */ plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4); - plmn[1] = imsi[2] - '0'; - /* default to MNC length 3 if unknown */ - if (mnc_len != 2) - plmn[1] |= (imsi[5] - '0') << 4; - else - plmn[1] |= 0xf0; + plmn[1] = (imsi[2] - '0') | ((imsi[5] - '0') << 4); plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4); + /* Match with 2 digit MNC */ + plmn2[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4); + plmn2[1] = (imsi[2] - '0') | 0xf0; + plmn2[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4); if (anqp == NULL) return 0; @@ -631,6 +641,10 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) } end = pos + udhl; + wpa_printf(MSG_DEBUG, "Interworking: Matching against MCC/MNC alternatives: %02x:%02x:%02x or %02x:%02x:%02x (IMSI %s, MNC length %d)", + plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2], + imsi, mnc_len); + while (pos + 2 <= end) { u8 iei, len; const u8 *l_end; @@ -643,14 +657,20 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) if (iei == 0 && len > 0) { /* PLMN List */ u8 num, i; + wpa_hexdump(MSG_DEBUG, "Interworking: PLMN List information element", + pos, len); num = *pos++; for (i = 0; i < num; i++) { - if (pos + 3 > end) + if (pos + 3 > l_end) break; - if (os_memcmp(pos, plmn, 3) == 0) + if (os_memcmp(pos, plmn, 3) == 0 || + os_memcmp(pos, plmn2, 3) == 0) return 1; /* Found matching PLMN */ pos += 3; } + } else { + wpa_hexdump(MSG_DEBUG, "Interworking: Unrecognized 3GPP information element", + pos, len); } pos = l_end; @@ -1337,6 +1357,8 @@ static struct wpa_cred * interworking_credentials_available_3gpp( char *sep; const char *imsi; int mnc_len; + char imsi_buf[16]; + size_t msin_len; #ifdef PCSC_FUNCS if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard && @@ -1346,6 +1368,13 @@ static struct wpa_cred * interworking_credentials_available_3gpp( goto compare; } #endif /* PCSC_FUNCS */ +#ifdef CONFIG_EAP_PROXY + if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) { + imsi = wpa_s->imsi; + mnc_len = wpa_s->mnc_len; + goto compare; + } +#endif /* CONFIG_EAP_PROXY */ if (cred->imsi == NULL || !cred->imsi[0] || cred->milenage == NULL || !cred->milenage[0]) @@ -1356,11 +1385,18 @@ static struct wpa_cred * interworking_credentials_available_3gpp( (sep - cred->imsi != 5 && sep - cred->imsi != 6)) continue; mnc_len = sep - cred->imsi - 3; - imsi = cred->imsi; - -#ifdef PCSC_FUNCS + os_memcpy(imsi_buf, cred->imsi, 3 + mnc_len); + sep++; + msin_len = os_strlen(cred->imsi); + if (3 + mnc_len + msin_len >= sizeof(imsi_buf) - 1) + msin_len = sizeof(imsi_buf) - 3 - mnc_len - 1; + os_memcpy(&imsi_buf[3 + mnc_len], sep, msin_len); + imsi_buf[3 + mnc_len + msin_len] = '\0'; + imsi = imsi_buf; + +#if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY) compare: -#endif /* PCSC_FUNCS */ +#endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */ wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " MACSTR, MAC2STR(bss->bssid)); ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len); diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c index 1b3364cb..39b837e5 100644 --- a/wpa_supplicant/main.c +++ b/wpa_supplicant/main.c @@ -14,6 +14,7 @@ #include "common.h" #include "wpa_supplicant_i.h" #include "driver_i.h" +#include "p2p_supplicant.h" extern struct wpa_driver_ops *wpa_drivers[]; @@ -289,6 +290,8 @@ int main(int argc, char *argv[]) } for (i = 0; exitcode == 0 && i < iface_count; i++) { + struct wpa_supplicant *wpa_s; + if ((ifaces[i].confname == NULL && ifaces[i].ctrl_interface == NULL) || ifaces[i].ifname == NULL) { @@ -299,8 +302,18 @@ int main(int argc, char *argv[]) exitcode = -1; break; } - if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL) + wpa_s = wpa_supplicant_add_iface(global, &ifaces[i]); + if (wpa_s == NULL) { + exitcode = -1; + break; + } +#ifdef CONFIG_P2P + if (wpa_s->global->p2p == NULL && + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && + wpas_p2p_add_p2pdev_interface(wpa_s) < 0) exitcode = -1; +#endif /* CONFIG_P2P */ } if (exitcode == 0) diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index 3ed826d6..f2cbdd76 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -551,6 +551,9 @@ static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, */ wpas_dbus_signal_p2p_peer_joined(wpa_s, sta); #endif /* CONFIG_P2P */ + + /* Notify listeners a new station has been authorized */ + wpas_dbus_signal_sta_authorized(wpa_s, sta); } @@ -570,6 +573,9 @@ static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s, */ wpas_dbus_signal_p2p_peer_disconnected(wpa_s, sta); #endif /* CONFIG_P2P */ + + /* Notify listeners a station has been deauthorized */ + wpas_dbus_signal_sta_deauthorized(wpa_s, sta); } diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index f9193aef..37f5ad3a 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -65,6 +65,8 @@ #define P2P_CONCURRENT_SEARCH_DELAY 500 #endif /* P2P_CONCURRENT_SEARCH_DELAY */ +#define P2P_MGMT_DEVICE_PREFIX "p2p-dev-" + enum p2p_group_removal_reason { P2P_GROUP_REMOVAL_UNKNOWN, P2P_GROUP_REMOVAL_SILENT, @@ -1013,6 +1015,26 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst, } +static void wpas_p2p_get_group_ifname(struct wpa_supplicant *wpa_s, + char *ifname, size_t len) +{ + char *ifname_ptr = wpa_s->ifname; + + if (os_strncmp(wpa_s->ifname, P2P_MGMT_DEVICE_PREFIX, + os_strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) { + ifname_ptr = os_strrchr(wpa_s->ifname, '-') + 1; + } + + os_snprintf(ifname, len, "p2p-%s-%d", ifname_ptr, wpa_s->p2p_group_idx); + if (os_strlen(ifname) >= IFNAMSIZ && + os_strlen(wpa_s->ifname) < IFNAMSIZ) { + /* Try to avoid going over the IFNAMSIZ length limit */ + os_snprintf(ifname, sizeof(ifname), "p2p-%d", + wpa_s->p2p_group_idx); + } +} + + static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, enum wpa_driver_if_type type) { @@ -1030,14 +1052,7 @@ static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, return 0; } - os_snprintf(ifname, sizeof(ifname), "p2p-%s-%d", wpa_s->ifname, - wpa_s->p2p_group_idx); - if (os_strlen(ifname) >= IFNAMSIZ && - os_strlen(wpa_s->ifname) < IFNAMSIZ) { - /* Try to avoid going over the IFNAMSIZ length limit */ - os_snprintf(ifname, sizeof(ifname), "p2p-%d", - wpa_s->p2p_group_idx); - } + wpas_p2p_get_group_ifname(wpa_s, ifname, sizeof(ifname)); force_ifname[0] = '\0'; wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group", @@ -1107,7 +1122,13 @@ wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go) os_memset(&iface, 0, sizeof(iface)); iface.ifname = wpa_s->pending_interface_name; iface.driver = wpa_s->driver->name; - iface.ctrl_interface = wpa_s->conf->ctrl_interface; + if (wpa_s->conf->ctrl_interface == NULL && + wpa_s->parent != wpa_s && + wpa_s->p2p_mgmt && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) + iface.ctrl_interface = wpa_s->parent->conf->ctrl_interface; + else + iface.ctrl_interface = wpa_s->conf->ctrl_interface; iface.driver_param = wpa_s->conf->driver_param; group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface); if (group_wpa_s == NULL) { @@ -1132,6 +1153,14 @@ static void wpas_p2p_group_formation_timeout(void *eloop_ctx, { struct wpa_supplicant *wpa_s = eloop_ctx; wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out"); + wpas_p2p_group_formation_failed(wpa_s); +} + + +void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); if (wpa_s->global->p2p) p2p_group_formation_failed(wpa_s->global->p2p); else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) @@ -3048,6 +3077,45 @@ static void wpas_p2p_debug_print(void *ctx, int level, const char *msg) } +int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s) +{ + struct wpa_interface iface; + struct wpa_supplicant *p2pdev_wpa_s; + char ifname[100]; + char force_name[100]; + int ret; + + os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s", + wpa_s->ifname); + force_name[0] = '\0'; + wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE; + ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, ifname, NULL, NULL, + force_name, wpa_s->pending_interface_addr, NULL); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to create P2P Device interface"); + return ret; + } + os_strlcpy(wpa_s->pending_interface_name, ifname, + sizeof(wpa_s->pending_interface_name)); + + os_memset(&iface, 0, sizeof(iface)); + iface.p2p_mgmt = 1; + iface.ifname = wpa_s->pending_interface_name; + iface.driver = wpa_s->driver->name; + iface.driver_param = wpa_s->conf->driver_param; + iface.confname = wpa_s->confname; + p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface); + if (!p2pdev_wpa_s) { + wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface"); + return -1; + } + p2pdev_wpa_s->parent = wpa_s; + + wpa_s->pending_interface_name[0] = '\0'; + return 0; +} + + /** * wpas_p2p_init - Initialize P2P module for %wpa_supplicant * @global: Pointer to global data from wpa_supplicant_init() @@ -3312,7 +3380,8 @@ void wpas_p2p_deinit_global(struct wpa_global *global) static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s) { - if (wpa_s->conf->p2p_no_group_iface) + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && + wpa_s->conf->p2p_no_group_iface) return 0; /* separate interface disabled per configuration */ if (wpa_s->drv_flags & (WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE | @@ -5595,14 +5664,7 @@ int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s) "session overlap"); if (wpa_s != wpa_s->parent) wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP); - - if (wpa_s->global->p2p) - p2p_group_formation_failed(wpa_s->global->p2p); - - eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); - - wpas_group_formation_completed(wpa_s, 0); + wpas_p2p_group_formation_failed(wpa_s); return 1; } diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index 04ba9b20..3ca6222d 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -14,10 +14,12 @@ struct p2p_go_neg_results; enum p2p_send_action_result; struct p2p_peer_info; struct p2p_channels; +struct wps_event_fail; int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s); void wpas_p2p_deinit(struct wpa_supplicant *wpa_s); void wpas_p2p_deinit_global(struct wpa_global *global); +int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s); int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *pin, enum p2p_wps_method wps_method, int persistent_group, int auto_join, int join, @@ -76,6 +78,7 @@ void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s); void wpas_dev_found(void *ctx, const u8 *addr, const struct p2p_peer_info *info, int new_device); +void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s); void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res); void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id); void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 27791b30..f6b881b1 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -17,6 +17,7 @@ #include "crypto/sha1.h" #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" +#include "eap_peer/eap_proxy.h" #include "eap_server/eap_methods.h" #include "rsn_supp/wpa.h" #include "eloop.h" @@ -2348,7 +2349,10 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) const u8 *addr = wpa_drv_get_mac_addr(wpa_s); if (addr) os_memcpy(wpa_s->own_addr, addr, ETH_ALEN); - } else if (!(wpa_s->drv_flags & + } else if ((!wpa_s->p2p_mgmt || + !(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) { l2_packet_deinit(wpa_s->l2); wpa_s->l2 = l2_packet_init(wpa_s->ifname, @@ -2368,10 +2372,6 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) return -1; } - wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR, - MAC2STR(wpa_s->own_addr)); - wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); - return 0; } @@ -2417,6 +2417,10 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) if (wpa_supplicant_update_mac_addr(wpa_s) < 0) return -1; + wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR, + MAC2STR(wpa_s->own_addr)); + wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); + if (wpa_s->bridge_ifname[0]) { wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge " "interface '%s'", wpa_s->bridge_ifname); @@ -2855,6 +2859,11 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, wpa_s->conf->driver_param = os_strdup(iface->driver_param); } + + if (iface->p2p_mgmt && !iface->ctrl_interface) { + os_free(wpa_s->conf->ctrl_interface); + wpa_s->conf->ctrl_interface = NULL; + } } else wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface, iface->driver_param); @@ -2980,11 +2989,24 @@ next_driver: if (wpa_s->max_remain_on_chan == 0) wpa_s->max_remain_on_chan = 1000; + /* + * Only take p2p_mgmt parameters when P2P Device is supported. + * Doing it here as it determines whether l2_packet_init() will be done + * during wpa_supplicant_driver_init(). + */ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) + wpa_s->p2p_mgmt = iface->p2p_mgmt; + else + iface->p2p_mgmt = 1; + if (wpa_supplicant_driver_init(wpa_s) < 0) return -1; #ifdef CONFIG_TDLS - if (wpa_tdls_init(wpa_s->wpa)) + if ((!iface->p2p_mgmt || + !(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) && + wpa_tdls_init(wpa_s->wpa)) return -1; #endif /* CONFIG_TDLS */ @@ -3022,7 +3044,7 @@ next_driver: } #ifdef CONFIG_P2P - if (wpas_p2p_init(wpa_s->global, wpa_s) < 0) { + if (iface->p2p_mgmt && wpas_p2p_init(wpa_s->global, wpa_s) < 0) { wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P"); return -1; } @@ -3031,6 +3053,20 @@ next_driver: if (wpa_bss_init(wpa_s) < 0) return -1; +#ifdef CONFIG_EAP_PROXY +{ + size_t len; + wpa_s->mnc_len = eap_proxy_get_imsi(wpa_s->imsi, &len); + if (wpa_s->mnc_len > 0) { + wpa_s->imsi[len] = '\0'; + wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)", + wpa_s->imsi, wpa_s->mnc_len); + } else { + wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available"); + } +} +#endif /* CONFIG_EAP_PROXY */ + if (pcsc_reader_init(wpa_s) < 0) return -1; @@ -3422,9 +3458,6 @@ void wpa_supplicant_deinit(struct wpa_global *global) #ifdef CONFIG_WIFI_DISPLAY wifi_display_deinit(global); #endif /* CONFIG_WIFI_DISPLAY */ -#ifdef CONFIG_P2P - wpas_p2p_deinit_global(global); -#endif /* CONFIG_P2P */ while (global->ifaces) wpa_supplicant_remove_iface(global, global->ifaces, 1); diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 8e21a3ae..d73d3715 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -815,6 +815,11 @@ fast_reauth=1 # interface used for EAPOL. The default value is suitable for most # cases. # +# 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 +# # EAP-FAST variables: # pac_file: File path for the PAC entries. wpa_supplicant will need to be able # to create this file and write updates to it when PAC is being diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index c971ae46..92408631 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -104,6 +104,15 @@ struct wpa_interface { * receiving of EAPOL frames from an additional interface. */ const char *bridge_ifname; + + /** + * p2p_mgmt - Interface used for P2P management (P2P Device operations) + * + * Indicates whether wpas_p2p_init() must be called for this interface. + * This is used only when the driver supports a dedicated P2P Device + * interface that is not a network interface. + */ + int p2p_mgmt; }; /** @@ -407,10 +416,8 @@ struct wpa_supplicant { * previous association event */ struct scard_data *scard; -#ifdef PCSC_FUNCS char imsi[20]; int mnc_len; -#endif /* PCSC_FUNCS */ unsigned char last_eapol_src[ETH_ALEN]; @@ -568,6 +575,8 @@ struct wpa_supplicant { unsigned int roc_waiting_drv_freq; int action_tx_wait_time; + int p2p_mgmt; + #ifdef CONFIG_P2P struct p2p_go_neg_results *go_params; int create_p2p_iface; diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index 40720c5e..61a42bd0 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -513,8 +513,6 @@ static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap) } #endif /* CONFIG_IEEE80211R */ -#endif /* CONFIG_NO_WPA */ - #ifdef CONFIG_TDLS @@ -596,6 +594,8 @@ static int wpa_supplicant_tdls_peer_addset( #endif /* CONFIG_TDLS */ +#endif /* CONFIG_NO_WPA */ + enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field) { diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index d73e023e..8e0207cb 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -868,9 +868,8 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s) while (ssid) { if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { if (ssid == wpa_s->current_ssid) { - wpa_s->current_ssid = NULL; - if (ssid != NULL) - wpas_notify_network_changed(wpa_s); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); } id = ssid->id; remove_ssid = ssid; |
