diff options
| author | Dmitry Shmidt <dimitrysh@google.com> | 2012-07-19 12:16:46 -0700 |
|---|---|---|
| committer | Dmitry Shmidt <dimitrysh@google.com> | 2012-07-19 16:03:19 -0700 |
| commit | 04949598a23f501be6eec21697465fd46a28840a (patch) | |
| tree | 6eb315b8d139b0690cb644dc914c53bacb42df42 /src | |
| parent | ce1e0634ed741293de60d5ba3476b26ccbd58ce2 (diff) | |
| download | android_external_wpa_supplicant_8-04949598a23f501be6eec21697465fd46a28840a.tar.gz android_external_wpa_supplicant_8-04949598a23f501be6eec21697465fd46a28840a.tar.bz2 android_external_wpa_supplicant_8-04949598a23f501be6eec21697465fd46a28840a.zip | |
wpa_supplicant: Update to 07-Jul-2012 TOT
commit a5ed45586c63ffd8f9d2b44e27c251d7bacbeaf4
Author: Jouni Malinen <j@w1.fi>
Date: Sat Jul 7 13:01:45 2012 +0300
WPS SSDP: Fix socket leaks on error paths
Change-Id: I0864aac7fc88fa2a60f5cca7d524b94363410c85
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
Diffstat (limited to 'src')
121 files changed, 5662 insertions, 1098 deletions
diff --git a/src/ap/accounting.c b/src/ap/accounting.c index 2a045606..2c3a6d9f 100644 --- a/src/ap/accounting.c +++ b/src/ap/accounting.c @@ -1,6 +1,6 @@ /* * hostapd / RADIUS Accounting - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -39,6 +39,8 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, u8 *val; size_t len; int i; + struct wpabuf *b; + struct hostapd_radius_attr *attr; msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, radius_client_get_id(hapd->radius)); @@ -67,7 +69,9 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, goto fail; } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, + if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, + RADIUS_ATTR_ACCT_AUTHENTIC) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, hapd->conf->ieee802_1x ? RADIUS_ACCT_AUTHENTIC_RADIUS : RADIUS_ACCT_AUTHENTIC_LOCAL)) { @@ -91,7 +95,9 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, } } - if (hapd->conf->own_ip_addr.af == AF_INET && + if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, + RADIUS_ATTR_NAS_IP_ADDRESS) && + hapd->conf->own_ip_addr.af == AF_INET && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { printf("Could not add NAS-IP-Address\n"); @@ -99,7 +105,9 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, } #ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && + if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, + RADIUS_ATTR_NAS_IPV6_ADDRESS) && + hapd->conf->own_ip_addr.af == AF_INET6 && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { printf("Could not add NAS-IPv6-Address\n"); @@ -107,7 +115,9 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, } #endif /* CONFIG_IPV6 */ - if (hapd->conf->nas_identifier && + if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, + RADIUS_ATTR_NAS_IDENTIFIER) && + hapd->conf->nas_identifier && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) hapd->conf->nas_identifier, os_strlen(hapd->conf->nas_identifier))) { @@ -115,7 +125,9 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, goto fail; } - if (sta && + if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, + RADIUS_ATTR_NAS_PORT) && + sta && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { printf("Could not add NAS-Port\n"); goto fail; @@ -123,7 +135,9 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, + RADIUS_ATTR_CALLED_STATION_ID) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, (u8 *) buf, os_strlen(buf))) { printf("Could not add Called-Station-Id\n"); goto fail; @@ -138,7 +152,10 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, goto fail; } - if (!radius_msg_add_attr_int32( + if (!hostapd_config_get_radius_attr( + hapd->conf->radius_acct_req_attr, + RADIUS_ATTR_NAS_PORT_TYPE) && + !radius_msg_add_attr_int32( msg, RADIUS_ATTR_NAS_PORT_TYPE, RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { printf("Could not add NAS-Port-Type\n"); @@ -149,7 +166,10 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, radius_sta_rate(hapd, sta) / 2, (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", radius_mode_txt(hapd)); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + if (!hostapd_config_get_radius_attr( + hapd->conf->radius_acct_req_attr, + RADIUS_ATTR_CONNECT_INFO) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, (u8 *) buf, os_strlen(buf))) { printf("Could not add Connect-Info\n"); goto fail; @@ -167,6 +187,26 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, goto fail; } } + + b = ieee802_1x_get_radius_cui(sta->eapol_sm); + if (b && + !radius_msg_add_attr(msg, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + wpabuf_head(b), wpabuf_len(b))) { + wpa_printf(MSG_ERROR, "Could not add CUI"); + goto fail; + } + } + + for (attr = hapd->conf->radius_acct_req_attr; attr; attr = attr->next) + { + if (!radius_msg_add_attr(msg, attr->type, + wpabuf_head(attr->val), + wpabuf_len(attr->val))) { + wpa_printf(MSG_ERROR, "Could not add RADIUS " + "attribute"); + goto fail; + } } return msg; @@ -259,8 +299,9 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) hapd, sta); msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); - if (msg) - radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr); + if (msg && + radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0) + radius_msg_free(msg); sta->acct_session_started = 1; } @@ -358,9 +399,10 @@ static void accounting_sta_report(struct hostapd_data *hapd, goto fail; } - radius_client_send(hapd->radius, msg, - stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, - sta->addr); + if (radius_client_send(hapd->radius, msg, + stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, + sta->addr) < 0) + goto fail; return; fail: @@ -463,7 +505,8 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) return; } - radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL); + if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0) + radius_msg_free(msg); } diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index b9f59941..811ffc16 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -1,6 +1,6 @@ /* * hostapd / Configuration helper functions - * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -87,6 +87,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) #ifdef CONFIG_IEEE80211R bss->ft_over_ds = 1; #endif /* CONFIG_IEEE80211R */ + + bss->radius_das_time_window = 300; } @@ -336,6 +338,30 @@ static void hostapd_config_free_radius(struct hostapd_radius_server *servers, } +struct hostapd_radius_attr * +hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type) +{ + for (; attr; attr = attr->next) { + if (attr->type == type) + return attr; + } + return NULL; +} + + +static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr) +{ + struct hostapd_radius_attr *prev; + + while (attr) { + prev = attr; + attr = attr->next; + wpabuf_free(prev->val); + os_free(prev); + } +} + + static void hostapd_config_free_eap_user(struct hostapd_eap_user *user) { os_free(user->identity); @@ -392,6 +418,8 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) conf->radius->num_auth_servers); hostapd_config_free_radius(conf->radius->acct_servers, conf->radius->num_acct_servers); + hostapd_config_free_radius_attr(conf->radius_auth_req_attr); + hostapd_config_free_radius_attr(conf->radius_acct_req_attr); os_free(conf->rsn_preauth_interfaces); os_free(conf->ctrl_interface); os_free(conf->ca_cert); @@ -406,6 +434,7 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->radius_server_clients); os_free(conf->test_socket); os_free(conf->radius); + os_free(conf->radius_das_shared_secret); hostapd_config_free_vlan(conf); if (conf->ssid.dyn_vlan_keys) { struct hostapd_ssid *ssid = &conf->ssid; @@ -465,9 +494,13 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->model_description); os_free(conf->model_url); os_free(conf->upc); + wpabuf_free(conf->wps_nfc_dh_pubkey); + wpabuf_free(conf->wps_nfc_dh_privkey); + wpabuf_free(conf->wps_nfc_dev_pw); #endif /* CONFIG_WPS */ os_free(conf->roaming_consortium); + os_free(conf->venue_name); #ifdef CONFIG_RADIUS_TEST os_free(conf->dump_msk_file); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 2ec25ad6..13d5549c 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -1,6 +1,6 @@ /* * hostapd / Configuration definitions and helpers functions - * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -116,6 +116,12 @@ struct hostapd_eap_user { int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ }; +struct hostapd_radius_attr { + u8 type; + struct wpabuf *val; + struct hostapd_radius_attr *next; +}; + #define NUM_TX_QUEUES 4 @@ -142,6 +148,12 @@ struct hostapd_roaming_consortium { u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; }; +struct hostapd_venue_name { + u8 lang[3]; + u8 name_len; + u8 name[252]; +}; + /** * struct hostapd_bss_config - Per-BSS configuration */ @@ -171,6 +183,15 @@ struct hostapd_bss_config { char *nas_identifier; struct hostapd_radius_servers *radius; int acct_interim_interval; + int radius_request_cui; + struct hostapd_radius_attr *radius_auth_req_attr; + struct hostapd_radius_attr *radius_acct_req_attr; + int radius_das_port; + unsigned int radius_das_time_window; + int radius_das_require_event_timestamp; + struct hostapd_ip_addr radius_das_client_addr; + u8 *radius_das_shared_secret; + size_t radius_das_shared_secret_len; struct hostapd_ssid ssid; @@ -326,6 +347,10 @@ struct hostapd_bss_config { char *model_url; char *upc; struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; + int wps_nfc_dev_pw_id; + struct wpabuf *wps_nfc_dh_pubkey; + struct wpabuf *wps_nfc_dh_privkey; + struct wpabuf *wps_nfc_dev_pw; #endif /* CONFIG_WPS */ int pbc_in_m1; @@ -343,6 +368,7 @@ struct hostapd_bss_config { #define TDLS_PROHIBIT_CHAN_SWITCH BIT(1) int tdls; int disable_11n; + int disable_11ac; /* IEEE 802.11v */ int time_advertisement; @@ -364,6 +390,13 @@ struct hostapd_bss_config { unsigned int roaming_consortium_count; struct hostapd_roaming_consortium *roaming_consortium; + /* IEEE 802.11u - Venue Name duples */ + unsigned int venue_name_count; + struct hostapd_venue_name *venue_name; + + u16 gas_comeback_delay; + int gas_frag_limit; + u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ #ifdef CONFIG_RADIUS_TEST @@ -423,6 +456,9 @@ struct hostapd_config { int ieee80211n; int secondary_channel; int require_ht; + u32 vht_capab; + int ieee80211ac; + u8 vht_oper_chwidth; }; @@ -444,5 +480,7 @@ const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, const struct hostapd_eap_user * hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, size_t identity_len, int phase2); +struct hostapd_radius_attr * +hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type); #endif /* HOSTAPD_CONFIG_H */ diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index d07cc6bc..859b529b 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -584,3 +584,15 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, reason); } + + +int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *data, + size_t len) +{ + if (hapd->driver == NULL || hapd->driver->send_action == NULL) + return 0; + return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, + hapd->own_addr, hapd->own_addr, data, + len, 0); +} diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 8038fa26..169c91b7 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -86,6 +86,9 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason); int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, int reason); +int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *data, + size_t len); int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, u16 auth_alg); int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c index b4252cf1..933b1585 100644 --- a/src/ap/ap_list.c +++ b/src/ap/ap_list.c @@ -289,10 +289,8 @@ void ap_list_process_beacon(struct hostapd_iface *iface, ap->num_beacons++; os_get_time(&now); ap->last_beacon = now.sec; - if (fi) { - ap->ssi_signal = fi->ssi_signal; + if (fi) ap->datarate = fi->datarate; - } if (!new_ap && ap != iface->ap_list) { /* move AP entry into the beginning of the list so that the @@ -320,7 +318,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface, #endif /* CONFIG_IEEE80211N */ if (set_beacon) - ieee802_11_set_beacons(iface); + ieee802_11_update_beacons(iface); } @@ -375,7 +373,7 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) } if (set_beacon) - ieee802_11_set_beacons(iface); + ieee802_11_update_beacons(iface); } diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h index 201f6ec3..f0b41259 100644 --- a/src/ap/ap_list.h +++ b/src/ap/ap_list.h @@ -34,7 +34,6 @@ struct ap_info { int channel; int datarate; /* in 100 kbps */ - int ssi_signal; int ht_support; diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 4ea86844..0f29ccdb 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -262,6 +262,11 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_adv_proto(hapd, pos); pos = hostapd_eid_roaming_consortium(hapd, pos); +#ifdef CONFIG_IEEE80211AC + pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_operation(hapd, pos); +#endif /* CONFIG_IEEE80211AC */ + /* Wi-Fi Alliance WMM */ pos = hostapd_eid_wmm(hapd, pos); @@ -293,7 +298,8 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, void handle_probe_req(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len) + const struct ieee80211_mgmt *mgmt, size_t len, + int ssi_signal) { u8 *resp; struct ieee802_11_elems elems; @@ -311,7 +317,7 @@ void handle_probe_req(struct hostapd_data *hapd, for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, mgmt->sa, mgmt->da, mgmt->bssid, - ie, ie_len) > 0) + ie, ie_len, ssi_signal) > 0) return; if (!hapd->iconf->send_probe_response) @@ -594,6 +600,11 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos = hostapd_eid_adv_proto(hapd, tailpos); tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); +#ifdef CONFIG_IEEE80211AC + tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_vht_operation(hapd, tailpos); +#endif /* CONFIG_IEEE80211AC */ + /* Wi-Fi Alliance WMM */ tailpos = hostapd_eid_wmm(hapd, tailpos); @@ -682,6 +693,7 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) !is_zero_ether_addr(hapd->conf->hessid)) params.hessid = hapd->conf->hessid; params.access_network_type = hapd->conf->access_network_type; + params.ap_max_inactivity = hapd->conf->ap_max_inactivity; if (hostapd_drv_set_ap(hapd, ¶ms)) wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); @@ -699,4 +711,14 @@ void ieee802_11_set_beacons(struct hostapd_iface *iface) ieee802_11_set_beacon(iface->bss[i]); } + +/* only update beacons if started */ +void ieee802_11_update_beacons(struct hostapd_iface *iface) +{ + size_t i; + for (i = 0; i < iface->num_bss; i++) + if (iface->bss[i]->beacon_set_done) + ieee802_11_set_beacon(iface->bss[i]); +} + #endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/src/ap/beacon.h b/src/ap/beacon.h index a944f5f3..37f10d2f 100644 --- a/src/ap/beacon.h +++ b/src/ap/beacon.h @@ -19,8 +19,10 @@ struct ieee80211_mgmt; void handle_probe_req(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len); + const struct ieee80211_mgmt *mgmt, size_t len, + int ssi_signal); void ieee802_11_set_beacon(struct hostapd_data *hapd); void ieee802_11_set_beacons(struct hostapd_iface *iface); +void ieee802_11_update_beacons(struct hostapd_iface *iface); #endif /* BEACON_H */ diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index d76b381b..ab9c83eb 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "common/ieee802_11_defs.h" #include "hostapd.h" #include "ieee802_1x.h" #include "wpa_auth.h" @@ -17,6 +18,7 @@ #include "wps_hostapd.h" #include "p2p_hostapd.h" #include "ctrl_iface_ap.h" +#include "ap_drv_ops.h" static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, @@ -100,3 +102,170 @@ int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, } return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); } + + +#ifdef CONFIG_P2P_MANAGER +static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, + u8 minor_reason_code, const u8 *addr) +{ + struct ieee80211_mgmt *mgmt; + int ret; + u8 *pos; + + if (hapd->driver->send_frame == NULL) + return -1; + + mgmt = os_zalloc(sizeof(*mgmt) + 100); + if (mgmt == NULL) + return -1; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR + " with minor reason code %u (stype=%u)", + MAC2STR(addr), minor_reason_code, stype); + + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype); + os_memcpy(mgmt->da, addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); + if (stype == WLAN_FC_STYPE_DEAUTH) { + mgmt->u.deauth.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + pos = (u8 *) (&mgmt->u.deauth.reason_code + 1); + } else { + mgmt->u.disassoc.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1); + } + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 4 + 3 + 1; + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + *pos++ = P2P_OUI_TYPE; + + *pos++ = P2P_ATTR_MINOR_REASON_CODE; + WPA_PUT_LE16(pos, 1); + pos += 2; + *pos++ = minor_reason_code; + + ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt, + pos - (u8 *) mgmt, 1); + os_free(mgmt); + + return ret < 0 ? -1 : 0; +} +#endif /* CONFIG_P2P_MANAGER */ + + +int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + const char *pos; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", + txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + pos = os_strstr(txtaddr, " test="); + if (pos) { + struct ieee80211_mgmt mgmt; + int encrypt; + if (hapd->driver->send_frame == NULL) + return -1; + pos += 6; + encrypt = atoi(pos); + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + os_memcpy(mgmt.da, addr, ETH_ALEN); + os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); + mgmt.u.deauth.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, + IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), + encrypt) < 0) + return -1; + return 0; + } + +#ifdef CONFIG_P2P_MANAGER + pos = os_strstr(txtaddr, " p2p="); + if (pos) { + return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH, + atoi(pos + 5), addr); + } +#endif /* CONFIG_P2P_MANAGER */ + + hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + sta = ap_get_sta(hapd, addr); + if (sta) + ap_sta_deauthenticate(hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); + else if (addr[0] == 0xff) + hostapd_free_stas(hapd); + + return 0; +} + + +int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + const char *pos; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", + txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + pos = os_strstr(txtaddr, " test="); + if (pos) { + struct ieee80211_mgmt mgmt; + int encrypt; + if (hapd->driver->send_frame == NULL) + return -1; + pos += 6; + encrypt = atoi(pos); + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + os_memcpy(mgmt.da, addr, ETH_ALEN); + os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); + mgmt.u.disassoc.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, + IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), + encrypt) < 0) + return -1; + return 0; + } + +#ifdef CONFIG_P2P_MANAGER + pos = os_strstr(txtaddr, " p2p="); + if (pos) { + return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC, + atoi(pos + 5), addr); + } +#endif /* CONFIG_P2P_MANAGER */ + + hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + sta = ap_get_sta(hapd, addr); + if (sta) + ap_sta_disassociate(hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); + else if (addr[0] == 0xff) + hostapd_free_stas(hapd); + + return 0; +} diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h index 0e4286d6..e83f8941 100644 --- a/src/ap/ctrl_iface_ap.h +++ b/src/ap/ctrl_iface_ap.h @@ -15,5 +15,9 @@ int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, char *buf, size_t buflen); int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, char *buf, size_t buflen); +int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, + const char *txtaddr); +int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, + const char *txtaddr); #endif /* CTRL_IFACE_AP_H */ diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 820a9031..cf06a4fb 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -26,6 +26,7 @@ #include "wps_hostapd.h" #include "ap_drv_ops.h" #include "ap_config.h" +#include "hw_features.h" int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, @@ -78,10 +79,19 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta = ap_get_sta(hapd, addr); if (sta) { accounting_sta_stop(hapd, sta); + + /* + * Make sure that the previously registered inactivity timer + * will not remove the STA immediately. + */ + sta->timeout_next = STA_NULLFUNC; } else { sta = ap_sta_add(hapd, addr); - if (sta == NULL) + if (sta == NULL) { + hostapd_drv_sta_disassoc(hapd, addr, + WLAN_REASON_DISASSOC_AP_BUSY); return -1; + } } sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); @@ -264,8 +274,36 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) } +void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, + int offset) +{ +#ifdef NEED_AP_MLME + int channel; + + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "driver had channel switch: " + "freq=%d, ht=%d, offset=%d", freq, ht, offset); + + hapd->iface->freq = freq; + + channel = hostapd_hw_get_channel(hapd, freq); + if (!channel) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, "driver switched to " + "bad channel!"); + return; + } + + hapd->iconf->channel = channel; + hapd->iconf->ieee80211n = ht; + hapd->iconf->secondary_channel = offset; +#endif /* NEED_AP_MLME */ +} + + int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, - const u8 *bssid, const u8 *ie, size_t ie_len) + const u8 *bssid, const u8 *ie, size_t ie_len, + int ssi_signal) { size_t i; int ret = 0; @@ -276,7 +314,8 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, random_add_randomness(sa, ETH_ALEN); for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) { if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, - sa, da, bssid, ie, ie_len) > 0) { + sa, da, bssid, ie, ie_len, + ssi_signal) > 0) { ret = 1; break; } @@ -541,7 +580,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->rx_probe_req.da, data->rx_probe_req.bssid, data->rx_probe_req.ie, - data->rx_probe_req.ie_len); + data->rx_probe_req.ie_len, + data->rx_probe_req.ssi_signal); break; case EVENT_NEW_STA: hostapd_event_new_sta(hapd, data->new_sta.addr); @@ -578,6 +618,13 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, hostapd_rx_action(hapd, &data->rx_action); break; #endif /* NEED_AP_MLME */ + case EVENT_CH_SWITCH: + if (!data) + break; + hostapd_event_ch_switch(hapd, data->ch_switch.freq, + data->ch_switch.ht_enabled, + data->ch_switch.ch_offset); + break; default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c new file mode 100644 index 00000000..2177b02a --- /dev/null +++ b/src/ap/gas_serv.c @@ -0,0 +1,673 @@ +/* + * Generic advertisement service (GAS) server + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "sta_info.h" +#include "gas_serv.h" + + +static struct gas_dialog_info * +gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) +{ + struct sta_info *sta; + struct gas_dialog_info *dia = NULL; + int i, j; + + sta = ap_get_sta(hapd, addr); + if (!sta) { + /* + * We need a STA entry to be able to maintain state for + * the GAS query. + */ + wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for " + "GAS query"); + sta = ap_sta_add(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR + " for GAS query", MAC2STR(addr)); + return NULL; + } + sta->flags |= WLAN_STA_GAS; + /* + * The default inactivity is 300 seconds. We don't need + * it to be that long. + */ + ap_sta_session_timeout(hapd, sta, 5); + } + + if (sta->gas_dialog == NULL) { + sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX * + sizeof(struct gas_dialog_info)); + if (sta->gas_dialog == NULL) + return NULL; + } + + for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) { + if (i == GAS_DIALOG_MAX) + i = 0; + if (sta->gas_dialog[i].valid) + continue; + dia = &sta->gas_dialog[i]; + dia->valid = 1; + dia->index = i; + dia->dialog_token = dialog_token; + sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i; + return dia; + } + + wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for " + MACSTR " dialog_token %u. Consider increasing " + "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token); + + return NULL; +} + + +struct gas_dialog_info * +gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, + u8 dialog_token) +{ + struct sta_info *sta; + int i; + + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR, + MAC2STR(addr)); + return NULL; + } + for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) { + if (sta->gas_dialog[i].dialog_token != dialog_token || + !sta->gas_dialog[i].valid) + continue; + return &sta->gas_dialog[i]; + } + wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for " + MACSTR " dialog_token %u", MAC2STR(addr), dialog_token); + return NULL; +} + + +void gas_serv_dialog_clear(struct gas_dialog_info *dia) +{ + wpabuf_free(dia->sd_resp); + os_memset(dia, 0, sizeof(*dia)); +} + + +static void gas_serv_free_dialogs(struct hostapd_data *hapd, + const u8 *sta_addr) +{ + struct sta_info *sta; + int i; + + sta = ap_get_sta(hapd, sta_addr); + if (sta == NULL || sta->gas_dialog == NULL) + return; + + for (i = 0; i < GAS_DIALOG_MAX; i++) { + if (sta->gas_dialog[i].valid) + return; + } + + os_free(sta->gas_dialog); + sta->gas_dialog = NULL; +} + + +static void anqp_add_capab_list(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + u8 *len; + + len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST); + wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); + if (hapd->conf->venue_name) + wpabuf_put_le16(buf, ANQP_VENUE_NAME); + if (hapd->conf->roaming_consortium) + wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); + gas_anqp_set_element_len(buf, len); +} + + +static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) +{ + if (hapd->conf->venue_name) { + u8 *len; + unsigned int i; + len = gas_anqp_add_element(buf, ANQP_VENUE_NAME); + wpabuf_put_u8(buf, hapd->conf->venue_group); + wpabuf_put_u8(buf, hapd->conf->venue_type); + for (i = 0; i < hapd->conf->venue_name_count; i++) { + struct hostapd_venue_name *vn; + vn = &hapd->conf->venue_name[i]; + wpabuf_put_u8(buf, 3 + vn->name_len); + wpabuf_put_data(buf, vn->lang, 3); + wpabuf_put_data(buf, vn->name, vn->name_len); + } + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_roaming_consortium(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + unsigned int i; + u8 *len; + + len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM); + for (i = 0; i < hapd->conf->roaming_consortium_count; i++) { + struct hostapd_roaming_consortium *rc; + rc = &hapd->conf->roaming_consortium[i]; + wpabuf_put_u8(buf, rc->len); + wpabuf_put_data(buf, rc->oi, rc->len); + } + gas_anqp_set_element_len(buf, len); +} + + +static struct wpabuf * +gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, + unsigned int request, + struct gas_dialog_info *di) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(1400); + if (buf == NULL) + return NULL; + + if (request & ANQP_REQ_CAPABILITY_LIST) + anqp_add_capab_list(hapd, buf); + if (request & ANQP_REQ_VENUE_NAME) + anqp_add_venue_name(hapd, buf); + if (request & ANQP_REQ_ROAMING_CONSORTIUM) + anqp_add_roaming_consortium(hapd, buf); + + return buf; +} + + +static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx) +{ + struct gas_dialog_info *dia = eloop_data; + + wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for " + "dialog token %d", dia->dialog_token); + + gas_serv_dialog_clear(dia); +} + + +struct anqp_query_info { + unsigned int request; + unsigned int remote_request; + const void *param; + u32 param_arg; + u16 remote_delay; +}; + + +static void set_anqp_req(unsigned int bit, const char *name, int local, + unsigned int remote, u16 remote_delay, + struct anqp_query_info *qi) +{ + qi->request |= bit; + if (local) { + wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name); + } else if (bit & remote) { + wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name); + qi->remote_request |= bit; + if (remote_delay > qi->remote_delay) + qi->remote_delay = remote_delay; + } else { + wpa_printf(MSG_DEBUG, "ANQP: %s not available", name); + } +} + + +static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, + struct anqp_query_info *qi) +{ + switch (info_id) { + case ANQP_CAPABILITY_LIST: + set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0, + 0, qi); + break; + case ANQP_VENUE_NAME: + set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", + hapd->conf->venue_name != NULL, 0, 0, qi); + break; + case ANQP_ROAMING_CONSORTIUM: + set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium", + hapd->conf->roaming_consortium != NULL, 0, 0, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", + info_id); + break; + } +} + + +static void rx_anqp_query_list(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list", + (unsigned int) (end - pos) / 2); + + while (pos + 2 <= end) { + rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi); + pos += 2; + } +} + + +static void gas_serv_req_local_processing(struct hostapd_data *hapd, + const u8 *sa, u8 dialog_token, + struct anqp_query_info *qi) +{ + struct wpabuf *buf, *tx_buf; + + buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL); + wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", + buf); + if (!buf) + return; + + if (wpabuf_len(buf) > hapd->gas_frag_limit || + hapd->conf->gas_comeback_delay) { + struct gas_dialog_info *di; + u16 comeback_delay = 1; + + if (hapd->conf->gas_comeback_delay) { + /* Testing - allow overriding of the delay value */ + comeback_delay = hapd->conf->gas_comeback_delay; + } + + wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in " + "initial response - use GAS comeback"); + di = gas_dialog_create(hapd, sa, dialog_token); + if (!di) { + wpa_printf(MSG_INFO, "ANQP: Could not create dialog " + "for " MACSTR " (dialog token %u)", + MAC2STR(sa), dialog_token); + wpabuf_free(buf); + return; + } + di->sd_resp = buf; + di->sd_resp_pos = 0; + tx_buf = gas_anqp_build_initial_resp_buf( + dialog_token, WLAN_STATUS_SUCCESS, comeback_delay, + NULL); + } else { + wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)"); + tx_buf = gas_anqp_build_initial_resp_buf( + dialog_token, WLAN_STATUS_SUCCESS, 0, buf); + wpabuf_free(buf); + } + if (!tx_buf) + return; + + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); +} + + +static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, + const u8 *sa, + const u8 *data, size_t len) +{ + const u8 *pos = data; + const u8 *end = data + len; + const u8 *next; + u8 dialog_token; + u16 slen; + struct anqp_query_info qi; + const u8 *adv_proto; + + if (len < 1 + 2) + return; + + os_memset(&qi, 0, sizeof(qi)); + + dialog_token = *pos++; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ", + MAC2STR(sa), dialog_token); + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Unexpected IE in GAS Initial Request: %u", *pos); + return; + } + adv_proto = pos++; + + slen = *pos++; + next = pos + slen; + if (next > end || slen < 2) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Invalid IE in GAS Initial Request"); + return; + } + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + struct wpabuf *buf; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Unsupported GAS advertisement protocol id %u", + *pos); + if (sa[0] & 0x01) + return; /* Invalid source address - drop silently */ + buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED, + 0, 2 + slen + 2); + if (buf == NULL) + return; + wpabuf_put_data(buf, adv_proto, 2 + slen); + wpabuf_put_le16(buf, 0); /* Query Response Length */ + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + return; + } + + pos = next; + /* Query Request */ + if (pos + 2 > end) + return; + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end) + return; + end = pos + slen; + + /* ANQP Query Request */ + while (pos < end) { + u16 info_id, elen; + + if (pos + 4 > end) + return; + + info_id = WPA_GET_LE16(pos); + pos += 2; + elen = WPA_GET_LE16(pos); + pos += 2; + + if (pos + elen > end) { + wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request"); + return; + } + + switch (info_id) { + case ANQP_QUERY_LIST: + rx_anqp_query_list(hapd, pos, pos + elen, &qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " + "Request element %u", info_id); + break; + } + + pos += elen; + } + + gas_serv_req_local_processing(hapd, sa, dialog_token, &qi); +} + + +void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, + struct gas_dialog_info *dialog) +{ + struct wpabuf *buf, *tx_buf; + u8 dialog_token = dialog->dialog_token; + size_t frag_len; + + if (dialog->sd_resp == NULL) { + buf = gas_serv_build_gas_resp_payload(hapd, + dialog->all_requested, + dialog); + wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", + buf); + if (!buf) + goto tx_gas_response_done; + dialog->sd_resp = buf; + dialog->sd_resp_pos = 0; + } + frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; + if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay || + hapd->conf->gas_comeback_delay) { + u16 comeback_delay_tus = dialog->comeback_delay + + GAS_SERV_COMEBACK_DELAY_FUDGE; + u32 comeback_delay_secs, comeback_delay_usecs; + + if (hapd->conf->gas_comeback_delay) { + /* Testing - allow overriding of the delay value */ + comeback_delay_tus = hapd->conf->gas_comeback_delay; + } + + wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit " + "%u) and comeback delay %u, " + "requesting comebacks", (unsigned int) frag_len, + (unsigned int) hapd->gas_frag_limit, + dialog->comeback_delay); + tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, + WLAN_STATUS_SUCCESS, + comeback_delay_tus, + NULL); + if (tx_buf) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Tx GAS Initial Resp (comeback = 10TU)"); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, + dst, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + } + wpabuf_free(tx_buf); + + /* start a timer of 1.5 * comeback-delay */ + comeback_delay_tus = comeback_delay_tus + + (comeback_delay_tus / 2); + comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000; + comeback_delay_usecs = (comeback_delay_tus * 1024) - + (comeback_delay_secs * 1000000); + eloop_register_timeout(comeback_delay_secs, + comeback_delay_usecs, + gas_serv_clear_cached_ies, dialog, + NULL); + goto tx_gas_response_done; + } + + buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + + dialog->sd_resp_pos, frag_len); + if (buf == NULL) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation " + "failed"); + goto tx_gas_response_done; + } + tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, + WLAN_STATUS_SUCCESS, 0, buf); + wpabuf_free(buf); + if (tx_buf == NULL) + goto tx_gas_response_done; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial " + "Response (frag_id %d frag_len %d)", + dialog->sd_frag_id, (int) frag_len); + dialog->sd_frag_id++; + + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst, + wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); +tx_gas_response_done: + gas_serv_clear_cached_ies(dialog, NULL); +} + + +static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, + const u8 *sa, + const u8 *data, size_t len) +{ + struct gas_dialog_info *dialog; + struct wpabuf *buf, *tx_buf; + u8 dialog_token; + size_t frag_len; + int more = 0; + + wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len); + if (len < 1) + return; + dialog_token = *data; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u", + dialog_token); + + dialog = gas_serv_dialog_find(hapd, sa, dialog_token); + if (!dialog) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD " + "response fragment for " MACSTR " dialog token %u", + MAC2STR(sa), dialog_token); + + if (sa[0] & 0x01) + return; /* Invalid source address - drop silently */ + tx_buf = gas_anqp_build_comeback_resp_buf( + dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0, + 0, NULL); + if (tx_buf == NULL) + return; + goto send_resp; + } + + if (dialog->sd_resp == NULL) { + wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x", + dialog->requested, dialog->received); + if ((dialog->requested & dialog->received) != + dialog->requested) { + wpa_printf(MSG_DEBUG, "GAS: Did not receive response " + "from remote processing"); + gas_serv_dialog_clear(dialog); + tx_buf = gas_anqp_build_comeback_resp_buf( + dialog_token, + WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0, + NULL); + if (tx_buf == NULL) + return; + goto send_resp; + } + + buf = gas_serv_build_gas_resp_payload(hapd, + dialog->all_requested, + dialog); + wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", + buf); + if (!buf) + goto rx_gas_comeback_req_done; + dialog->sd_resp = buf; + dialog->sd_resp_pos = 0; + } + frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; + if (frag_len > hapd->gas_frag_limit) { + frag_len = hapd->gas_frag_limit; + more = 1; + } + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", + (unsigned int) frag_len); + buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + + dialog->sd_resp_pos, frag_len); + if (buf == NULL) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate " + "buffer"); + goto rx_gas_comeback_req_done; + } + tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, + WLAN_STATUS_SUCCESS, + dialog->sd_frag_id, + more, 0, buf); + wpabuf_free(buf); + if (tx_buf == NULL) + goto rx_gas_comeback_req_done; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response " + "(frag_id %d more=%d frag_len=%d)", + dialog->sd_frag_id, more, (int) frag_len); + dialog->sd_frag_id++; + dialog->sd_resp_pos += frag_len; + + if (more) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain " + "to be sent", + (int) (wpabuf_len(dialog->sd_resp) - + dialog->sd_resp_pos)); + } else { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " + "SD response sent"); + gas_serv_dialog_clear(dialog); + gas_serv_free_dialogs(hapd, sa); + } + +send_resp: + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); + return; + +rx_gas_comeback_req_done: + gas_serv_clear_cached_ies(dialog, NULL); +} + + +static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, + int freq) +{ + struct hostapd_data *hapd = ctx; + const struct ieee80211_mgmt *mgmt; + size_t hdr_len; + const u8 *sa, *data; + + mgmt = (const struct ieee80211_mgmt *) buf; + hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf; + if (hdr_len > len) + return; + if (mgmt->u.action.category != WLAN_ACTION_PUBLIC) + return; + sa = mgmt->sa; + len -= hdr_len; + data = &mgmt->u.action.u.public_action.action; + switch (data[0]) { + case WLAN_PA_GAS_INITIAL_REQ: + gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1); + break; + case WLAN_PA_GAS_COMEBACK_REQ: + gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1); + break; + } +} + + +int gas_serv_init(struct hostapd_data *hapd) +{ + hapd->public_action_cb = gas_serv_rx_public_action; + hapd->public_action_cb_ctx = hapd; + hapd->gas_frag_limit = 1400; + if (hapd->conf->gas_frag_limit > 0) + hapd->gas_frag_limit = hapd->conf->gas_frag_limit; + return 0; +} + + +void gas_serv_deinit(struct hostapd_data *hapd) +{ +} diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h new file mode 100644 index 00000000..0e2eaf66 --- /dev/null +++ b/src/ap/gas_serv.h @@ -0,0 +1,49 @@ +/* + * Generic advertisement service (GAS) server + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef GAS_SERV_H +#define GAS_SERV_H + +#define ANQP_REQ_CAPABILITY_LIST \ + (1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST)) +#define ANQP_REQ_VENUE_NAME \ + (1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_ROAMING_CONSORTIUM \ + (1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST)) + +/* To account for latencies between hostapd and external ANQP processor */ +#define GAS_SERV_COMEBACK_DELAY_FUDGE 10 +#define GAS_SERV_MIN_COMEBACK_DELAY 100 /* in TU */ + +struct gas_dialog_info { + u8 valid; + u8 index; + struct wpabuf *sd_resp; /* Fragmented response */ + u8 dialog_token; + size_t sd_resp_pos; /* Offset in sd_resp */ + u8 sd_frag_id; + u16 comeback_delay; + + unsigned int requested; + unsigned int received; + unsigned int all_requested; +}; + +struct hostapd_data; + +void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, + struct gas_dialog_info *dialog); +struct gas_dialog_info * +gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, + u8 dialog_token); +void gas_serv_dialog_clear(struct gas_dialog_info *dialog); + +int gas_serv_init(struct hostapd_data *hapd); +void gas_serv_deinit(struct hostapd_data *hapd); + +#endif /* GAS_SERV_H */ diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index c5cbdf70..22c5e654 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -1,6 +1,6 @@ /* * hostapd / Initialization and configuration - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "radius/radius_client.h" +#include "radius/radius_das.h" #include "drivers/driver.h" #include "hostapd.h" #include "authsrv.h" @@ -30,15 +31,33 @@ #include "ap_drv_ops.h" #include "ap_config.h" #include "p2p_hostapd.h" +#include "gas_serv.h" -static int hostapd_flush_old_stations(struct hostapd_data *hapd); +static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); extern int wpa_debug_level; +int hostapd_for_each_interface(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx) +{ + size_t i; + int ret; + + for (i = 0; i < interfaces->count; i++) { + ret = cb(interfaces->iface[i], ctx); + if (ret) + return ret; + } + + return 0; +} + + static void hostapd_reload_bss(struct hostapd_data *hapd) { #ifndef CONFIG_NO_RADIUS @@ -105,7 +124,8 @@ int hostapd_reload_config(struct hostapd_iface *iface) * allow them to use the BSS anymore. */ for (j = 0; j < iface->num_bss; j++) { - hostapd_flush_old_stations(iface->bss[j]); + hostapd_flush_old_stations(iface->bss[j], + WLAN_REASON_PREV_AUTH_NOT_VALID); hostapd_broadcast_wep_clear(iface->bss[j]); #ifndef CONFIG_NO_RADIUS @@ -210,21 +230,9 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) return errors; } -/** - * hostapd_cleanup - Per-BSS cleanup (deinitialization) - * @hapd: Pointer to BSS data - * - * This function is used to free all per-BSS data structures and resources. - * This gets called in a loop for each BSS between calls to - * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface - * is deinitialized. Most of the modules that are initialized in - * hostapd_setup_bss() are deinitialized here. - */ -static void hostapd_cleanup(struct hostapd_data *hapd) -{ - if (hapd->iface->ctrl_iface_deinit) - hapd->iface->ctrl_iface_deinit(hapd); +static void hostapd_free_hapd_data(struct hostapd_data *hapd) +{ iapp_deinit(hapd->iapp); hapd->iapp = NULL; accounting_deinit(hapd); @@ -234,6 +242,8 @@ static void hostapd_cleanup(struct hostapd_data *hapd) #ifndef CONFIG_NO_RADIUS radius_client_deinit(hapd->radius); hapd->radius = NULL; + radius_das_deinit(hapd->radius_das); + hapd->radius_das = NULL; #endif /* CONFIG_NO_RADIUS */ hostapd_deinit_wps(hapd); @@ -257,6 +267,28 @@ static void hostapd_cleanup(struct hostapd_data *hapd) #endif /* CONFIG_P2P */ wpabuf_free(hapd->time_adv); + +#ifdef CONFIG_INTERWORKING + gas_serv_deinit(hapd); +#endif /* CONFIG_INTERWORKING */ +} + + +/** + * hostapd_cleanup - Per-BSS cleanup (deinitialization) + * @hapd: Pointer to BSS data + * + * This function is used to free all per-BSS data structures and resources. + * This gets called in a loop for each BSS between calls to + * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface + * is deinitialized. Most of the modules that are initialized in + * hostapd_setup_bss() are deinitialized here. + */ +static void hostapd_cleanup(struct hostapd_data *hapd) +{ + if (hapd->iface->ctrl_iface_deinit) + hapd->iface->ctrl_iface_deinit(hapd); + hostapd_free_hapd_data(hapd); } @@ -272,6 +304,18 @@ static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface) } +static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) +{ + hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); + iface->hw_features = NULL; + os_free(iface->current_rates); + iface->current_rates = NULL; + os_free(iface->basic_rates); + iface->basic_rates = NULL; + ap_list_deinit(iface); +} + + /** * hostapd_cleanup_iface - Complete per-interface cleanup * @iface: Pointer to interface data @@ -281,13 +325,7 @@ static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface) */ static void hostapd_cleanup_iface(struct hostapd_iface *iface) { - hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); - iface->hw_features = NULL; - os_free(iface->current_rates); - iface->current_rates = NULL; - os_free(iface->basic_rates); - iface->basic_rates = NULL; - ap_list_deinit(iface); + hostapd_cleanup_iface_partial(iface); hostapd_config_free(iface->conf); iface->conf = NULL; @@ -297,6 +335,15 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface) } +static void hostapd_clear_wep(struct hostapd_data *hapd) +{ + if (hapd->drv_priv) { + hostapd_set_privacy(hapd, 0); + hostapd_broadcast_wep_clear(hapd); + } +} + + static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) { int i; @@ -333,7 +380,7 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) } -static int hostapd_flush_old_stations(struct hostapd_data *hapd) +static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason) { int ret = 0; u8 addr[ETH_ALEN]; @@ -349,7 +396,7 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd) } wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations"); os_memset(addr, 0xff, ETH_ALEN); - hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_drv_sta_deauth(hapd, addr, reason); hostapd_free_stas(hapd); return ret; @@ -464,6 +511,86 @@ static int mac_in_conf(struct hostapd_config *conf, const void *a) } +#ifndef CONFIG_NO_RADIUS + +static int hostapd_das_nas_mismatch(struct hostapd_data *hapd, + struct radius_das_attrs *attr) +{ + /* TODO */ + return 0; +} + + +static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, + struct radius_das_attrs *attr) +{ + struct sta_info *sta = NULL; + char buf[128]; + + if (attr->sta_addr) + sta = ap_get_sta(hapd, attr->sta_addr); + + if (sta == NULL && attr->acct_session_id && + attr->acct_session_id_len == 17) { + for (sta = hapd->sta_list; sta; sta = sta->next) { + os_snprintf(buf, sizeof(buf), "%08X-%08X", + sta->acct_session_id_hi, + sta->acct_session_id_lo); + if (os_memcmp(attr->acct_session_id, buf, 17) == 0) + break; + } + } + + if (sta == NULL && attr->cui) { + for (sta = hapd->sta_list; sta; sta = sta->next) { + struct wpabuf *cui; + cui = ieee802_1x_get_radius_cui(sta->eapol_sm); + if (cui && wpabuf_len(cui) == attr->cui_len && + os_memcmp(wpabuf_head(cui), attr->cui, + attr->cui_len) == 0) + break; + } + } + + if (sta == NULL && attr->user_name) { + for (sta = hapd->sta_list; sta; sta = sta->next) { + u8 *identity; + size_t identity_len; + identity = ieee802_1x_get_identity(sta->eapol_sm, + &identity_len); + if (identity && + identity_len == attr->user_name_len && + os_memcmp(identity, attr->user_name, identity_len) + == 0) + break; + } + } + + return sta; +} + + +static enum radius_das_res +hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + if (hostapd_das_nas_mismatch(hapd, attr)) + return RADIUS_DAS_NAS_MISMATCH; + + sta = hostapd_das_find_sta(hapd, attr); + if (sta == NULL) + return RADIUS_DAS_SESSION_NOT_FOUND; + + hostapd_drv_sta_deauth(hapd, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); + + return RADIUS_DAS_SUCCESS; +} + +#endif /* CONFIG_NO_RADIUS */ /** @@ -519,7 +646,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) if (conf->wmm_enabled < 0) conf->wmm_enabled = hapd->iconf->ieee80211n; - hostapd_flush_old_stations(hapd); + hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID); hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); @@ -583,6 +710,27 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) wpa_printf(MSG_ERROR, "RADIUS client initialization failed."); return -1; } + + if (hapd->conf->radius_das_port) { + struct radius_das_conf das_conf; + os_memset(&das_conf, 0, sizeof(das_conf)); + das_conf.port = hapd->conf->radius_das_port; + das_conf.shared_secret = hapd->conf->radius_das_shared_secret; + das_conf.shared_secret_len = + hapd->conf->radius_das_shared_secret_len; + das_conf.client_addr = &hapd->conf->radius_das_client_addr; + das_conf.time_window = hapd->conf->radius_das_time_window; + das_conf.require_event_timestamp = + hapd->conf->radius_das_require_event_timestamp; + das_conf.ctx = hapd; + das_conf.disconnect = hostapd_das_disconnect; + hapd->radius_das = radius_das_init(&das_conf); + if (hapd->radius_das == NULL) { + wpa_printf(MSG_ERROR, "RADIUS DAS initialization " + "failed."); + return -1; + } + } #endif /* CONFIG_NO_RADIUS */ if (hostapd_acl_init(hapd)) { @@ -615,6 +763,13 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) return -1; } +#ifdef CONFIG_INTERWORKING + if (gas_serv_init(hapd)) { + wpa_printf(MSG_ERROR, "GAS server initialization failed"); + return -1; + } +#endif /* CONFIG_INTERWORKING */ + if (hapd->iface->ctrl_iface_init && hapd->iface->ctrl_iface_init(hapd)) { wpa_printf(MSG_ERROR, "Failed to setup control interface"); @@ -857,6 +1012,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, hapd->conf = bss; hapd->iface = hapd_iface; hapd->driver = hapd->iconf->driver; + hapd->ctrl_sock = -1; return hapd; } @@ -873,7 +1029,8 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) for (j = 0; j < iface->num_bss; j++) { struct hostapd_data *hapd = iface->bss[j]; hostapd_free_stas(hapd); - hostapd_flush_old_stations(hapd); + hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); + hostapd_clear_wep(hapd); hostapd_cleanup(hapd); } } @@ -937,4 +1094,12 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); } else wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); + + wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + "for " MACSTR " (%d seconds - ap_max_inactivity)", + __func__, MAC2STR(sta->addr), + hapd->conf->ap_max_inactivity); + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, + ap_handle_timer, hapd, sta); } diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index bc288056..f7ed3110 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -15,7 +15,6 @@ struct wpa_driver_ops; struct wpa_ctrl_dst; struct radius_server_data; struct upnp_wps_device_sm; -struct hapd_interfaces; struct hostapd_data; struct sta_info; struct hostap_sta_driver_data; @@ -24,9 +23,15 @@ struct full_dynamic_vlan; enum wps_event; union wps_event_data; +struct hapd_interfaces { + size_t count; + struct hostapd_iface **iface; +}; + + struct hostapd_probereq_cb { int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, - const u8 *ie, size_t ie_len); + const u8 *ie, size_t ie_len, int ssi_signal); void *ctx; }; @@ -40,7 +45,7 @@ struct hostapd_rate_data { struct hostapd_frame_info { u32 channel; u32 datarate; - u32 ssi_signal; + int ssi_signal; /* dBm */ }; @@ -80,6 +85,7 @@ struct hostapd_data { struct radius_client_data *radius; u32 acct_session_id_hi, acct_session_id_lo; + struct radius_das_data *radius_das; struct iapp_data *iapp; @@ -164,6 +170,9 @@ struct hostapd_data { int noa_start; int noa_duration; #endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + size_t gas_frag_limit; +#endif /* CONFIG_INTERWORKING */ }; @@ -242,6 +251,9 @@ struct hostapd_iface { }; /* hostapd.c */ +int hostapd_for_each_interface(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx); int hostapd_reload_config(struct hostapd_iface *iface); struct hostapd_data * hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, @@ -258,7 +270,8 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, int hostapd_register_probereq_cb(struct hostapd_data *hapd, int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, - const u8 *ie, size_t ie_len), + const u8 *ie, size_t ie_len, + int ssi_signal), void *ctx); void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr); @@ -268,6 +281,9 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr); int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, - const u8 *bssid, const u8 *ie, size_t ie_len); + const u8 *bssid, const u8 *ie, size_t ie_len, + int ssi_signal); +void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, + int offset); #endif /* HOSTAPD_H */ diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 8c6fef2f..76c42118 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -2,7 +2,7 @@ * hostapd / Hardware feature query and different modes * Copyright 2002-2003, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. - * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -416,7 +416,7 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) int res; /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is - * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */ + * allowed per IEEE Std 802.11-2012, 10.15.3.2 */ iface->scan_cb = NULL; @@ -447,6 +447,46 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) } +static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, + struct wpa_driver_scan_params *params) +{ + /* Scan only the affected frequency range */ + int pri_freq, sec_freq; + int affected_start, affected_end; + int i, pos; + struct hostapd_hw_modes *mode; + + if (iface->current_mode == NULL) + return; + + pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); + if (iface->conf->secondary_channel > 0) + sec_freq = pri_freq + 20; + else + sec_freq = pri_freq - 20; + affected_start = (pri_freq + sec_freq) / 2 - 25; + affected_end = (pri_freq + sec_freq) / 2 + 25; + wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", + affected_start, affected_end); + + mode = iface->current_mode; + params->freqs = os_zalloc((mode->num_channels + 1) * sizeof(int)); + if (params->freqs == NULL) + return; + pos = 0; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + if (chan->freq < affected_start || + chan->freq > affected_end) + continue; + params->freqs[pos++] = chan->freq; + } +} + + static int ieee80211n_check_40mhz(struct hostapd_iface *iface) { struct wpa_driver_scan_params params; @@ -457,12 +497,15 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface) wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " "40 MHz channel"); os_memset(¶ms, 0, sizeof(params)); - /* TODO: scan only the needed frequency */ + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + ieee80211n_scan_channels_2g4(iface, ¶ms); if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { wpa_printf(MSG_ERROR, "Failed to request a scan of " "neighboring BSSes"); + os_free(params.freqs); return -1; } + os_free(params.freqs); iface->scan_cb = ieee80211n_check_scan; return 1; diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 9c931ca4..3996c90d 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -876,6 +876,7 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211N */ p = hostapd_eid_ext_capab(hapd, p); + p = hostapd_eid_bss_max_idle_period(hapd, p); if (sta->flags & WLAN_STA_WMM) p = hostapd_eid_wmm(hapd, p); @@ -1392,7 +1393,7 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, if (stype == WLAN_FC_STYPE_PROBE_REQ) { - handle_probe_req(hapd, mgmt, len); + handle_probe_req(hapd, mgmt, len, fi->ssi_signal); return; } diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index d30e90f3..9993bee1 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -45,6 +45,8 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); int hostapd_ht_operation_update(struct hostapd_iface *iface); void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, const u8 *addr, const u8 *trans_id); @@ -72,5 +74,6 @@ u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid); int hostapd_update_time_adv(struct hostapd_data *hapd); void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr); +u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid); #endif /* IEEE802_11_H */ diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index 109c4bc4..0c4c5f32 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -193,7 +193,8 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, goto fail; } - radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr); + if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0) + goto fail; return 0; fail: @@ -492,7 +493,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, } if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && - cache->psk == NULL) + !cache->has_psk) cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index 0935cd50..b3fdf3d6 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -397,3 +397,31 @@ int hostapd_update_time_adv(struct hostapd_data *hapd) return 0; } + + +u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + +#ifdef CONFIG_WNM + if (hapd->conf->ap_max_inactivity > 0) { + unsigned int val; + *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; + *pos++ = 3; + val = hapd->conf->ap_max_inactivity; + if (val > 68000) + val = 68000; + val *= 1000; + val /= 1024; + if (val == 0) + val = 1; + if (val > 65535) + val = 65535; + WPA_PUT_LE16(pos, val); + pos += 2; + *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ + } +#endif /* CONFIG_WNM */ + + return pos; +} diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c new file mode 100644 index 00000000..3ad33c89 --- /dev/null +++ b/src/ap/ieee802_11_vht.c @@ -0,0 +1,72 @@ +/* + * hostapd / IEEE 802.11ac VHT + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of BSD license + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "drivers/driver.h" +#include "hostapd.h" +#include "ap_config.h" +#include "sta_info.h" +#include "beacon.h" +#include "ieee802_11.h" + + +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_vht_capabilities *cap; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode || + hapd->conf->disable_11ac) + return eid; + + *pos++ = WLAN_EID_VHT_CAP; + *pos++ = sizeof(*cap); + + cap = (struct ieee80211_vht_capabilities *) pos; + os_memset(cap, 0, sizeof(*cap)); + cap->vht_capabilities_info = host_to_le32( + hapd->iface->current_mode->vht_capab); + + /* Supported MCS set comes from hw */ + os_memcpy(cap->vht_supported_mcs_set, + hapd->iface->current_mode->vht_mcs_set, 8); + + pos += sizeof(*cap); + + return pos; +} + + +u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_vht_operation *oper; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac) + return eid; + + *pos++ = WLAN_EID_VHT_OPERATION; + *pos++ = sizeof(*oper); + + oper = (struct ieee80211_vht_operation *) pos; + os_memset(oper, 0, sizeof(*oper)); + + oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth; + + /* VHT Basic MCS set comes from hw */ + /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */ + oper->vht_basic_mcs_set = 0xfffc; + pos += sizeof(*oper); + + return pos; +} diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index a3297777..dd0df1d3 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -416,6 +416,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, struct radius_msg *msg; char buf[128]; struct eapol_state_machine *sm = sta->eapol_sm; + struct hostapd_radius_attr *attr; if (sm == NULL) return; @@ -442,7 +443,9 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, goto fail; } - if (hapd->conf->own_ip_addr.af == AF_INET && + if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + RADIUS_ATTR_NAS_IP_ADDRESS) && + hapd->conf->own_ip_addr.af == AF_INET && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { printf("Could not add NAS-IP-Address\n"); @@ -450,7 +453,9 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, } #ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && + if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + RADIUS_ATTR_NAS_IPV6_ADDRESS) && + hapd->conf->own_ip_addr.af == AF_INET6 && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { printf("Could not add NAS-IPv6-Address\n"); @@ -458,7 +463,9 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, } #endif /* CONFIG_IPV6 */ - if (hapd->conf->nas_identifier && + if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + RADIUS_ATTR_NAS_IDENTIFIER) && + hapd->conf->nas_identifier && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) hapd->conf->nas_identifier, os_strlen(hapd->conf->nas_identifier))) { @@ -466,7 +473,9 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, goto fail; } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { + if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + RADIUS_ATTR_NAS_PORT) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { printf("Could not add NAS-Port\n"); goto fail; } @@ -474,7 +483,9 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); buf[sizeof(buf) - 1] = '\0'; - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + RADIUS_ATTR_CALLED_STATION_ID) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, (u8 *) buf, os_strlen(buf))) { printf("Could not add Called-Station-Id\n"); goto fail; @@ -492,12 +503,16 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, /* TODO: should probably check MTU from driver config; 2304 is max for * IEEE 802.11, but use 1400 to avoid problems with too large packets */ - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { + if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + RADIUS_ATTR_FRAMED_MTU) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { printf("Could not add Framed-MTU\n"); goto fail; } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + RADIUS_ATTR_NAS_PORT_TYPE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { printf("Could not add NAS-Port-Type\n"); goto fail; @@ -513,7 +528,9 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, radius_mode_txt(hapd)); buf[sizeof(buf) - 1] = '\0'; } - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + RADIUS_ATTR_CONNECT_INFO) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, (u8 *) buf, os_strlen(buf))) { printf("Could not add Connect-Info\n"); goto fail; @@ -541,6 +558,36 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, } } + if (hapd->conf->radius_request_cui) { + const u8 *cui; + size_t cui_len; + /* Add previously learned CUI or nul CUI to request CUI */ + if (sm->radius_cui) { + cui = wpabuf_head(sm->radius_cui); + cui_len = wpabuf_len(sm->radius_cui); + } else { + cui = (const u8 *) "\0"; + cui_len = 1; + } + if (!radius_msg_add_attr(msg, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + cui, cui_len)) { + wpa_printf(MSG_ERROR, "Could not add CUI"); + goto fail; + } + } + + for (attr = hapd->conf->radius_auth_req_attr; attr; attr = attr->next) + { + if (!radius_msg_add_attr(msg, attr->type, + wpabuf_head(attr->val), + wpabuf_len(attr->val))) { + wpa_printf(MSG_ERROR, "Could not add RADIUS " + "attribute"); + goto fail; + } + } + if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0) goto fail; @@ -861,12 +908,22 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) if (!force_1x && !hapd->conf->ieee802_1x) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - " "802.1X not enabled or forced for WPS"); + /* + * Clear any possible EAPOL authenticator state to support + * reassociation change from WPS to PSK. + */ + ieee802_1x_free_station(sta); return; } key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK"); + /* + * Clear any possible EAPOL authenticator state to support + * reassociation change from WPA-EAP to PSK. + */ + ieee802_1x_free_station(sta); return; } @@ -968,6 +1025,7 @@ void ieee802_1x_free_station(struct sta_info *sta) #ifndef CONFIG_NO_RADIUS radius_msg_free(sm->last_recv_radius); radius_free_class(&sm->radius_class); + wpabuf_free(sm->radius_cui); #endif /* CONFIG_NO_RADIUS */ os_free(sm->identity); @@ -1189,6 +1247,32 @@ static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, } +/* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */ +static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd, + struct sta_info *sta, + struct radius_msg *msg) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + struct wpabuf *cui; + u8 *buf; + size_t len; + + if (sm == NULL) + return; + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) < 0) + return; + + cui = wpabuf_alloc_copy(buf, len); + if (cui == NULL) + return; + + wpabuf_free(sm->radius_cui); + sm->radius_cui = cui; +} + + struct sta_id_search { u8 identifier; struct eapol_state_machine *sm; @@ -1348,6 +1432,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, shared_secret_len); ieee802_1x_store_radius_class(hapd, sta, msg); ieee802_1x_update_sta_identity(hapd, sta, msg); + ieee802_1x_update_sta_cui(hapd, sta, msg); if (sm->eap_if->eapKeyAvailable && wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, session_timeout_set ? @@ -1865,6 +1950,14 @@ u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, } +struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm) +{ + if (sm == NULL) + return NULL; + return sm->radius_cui; +} + + const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len) { *len = 0; diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h index 58f6084e..f9b05caf 100644 --- a/src/ap/ieee802_1x.h +++ b/src/ap/ieee802_1x.h @@ -67,6 +67,7 @@ int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len); u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, int idx); +struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm); const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len); void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, int enabled); diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c index 9eb48405..ba2c033a 100644 --- a/src/ap/pmksa_cache_auth.c +++ b/src/ap/pmksa_cache_auth.c @@ -1,6 +1,6 @@ /* * hostapd - PMKSA cache for IEEE 802.11i RSN - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -40,6 +40,7 @@ static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) if (entry == NULL) return; os_free(entry->identity); + wpabuf_free(entry->cui); #ifndef CONFIG_NO_RADIUS radius_free_class(&entry->radius_class); #endif /* CONFIG_NO_RADIUS */ @@ -136,6 +137,9 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, } } + if (eapol->radius_cui) + entry->cui = wpabuf_dup(eapol->radius_cui); + #ifndef CONFIG_NO_RADIUS radius_copy_class(&entry->radius_class, &eapol->radius_class); #endif /* CONFIG_NO_RADIUS */ @@ -163,6 +167,11 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, eapol->identity, eapol->identity_len); } + if (entry->cui) { + wpabuf_free(eapol->radius_cui); + eapol->radius_cui = wpabuf_dup(entry->cui); + } + #ifndef CONFIG_NO_RADIUS radius_free_class(&eapol->radius_class); radius_copy_class(&eapol->radius_class, &entry->radius_class); @@ -299,6 +308,8 @@ pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, old_entry->identity_len); } } + if (old_entry->cui) + entry->cui = wpabuf_dup(old_entry->cui); #ifndef CONFIG_NO_RADIUS radius_copy_class(&entry->radius_class, &old_entry->radius_class); #endif /* CONFIG_NO_RADIUS */ diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h index 74b73c47..d473f3fd 100644 --- a/src/ap/pmksa_cache_auth.h +++ b/src/ap/pmksa_cache_auth.h @@ -1,6 +1,6 @@ /* * hostapd - PMKSA cache for IEEE 802.11i RSN - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -25,6 +25,7 @@ struct rsn_pmksa_cache_entry { u8 *identity; size_t identity_len; + struct wpabuf *cui; struct radius_class_data radius_class; u8 eap_type_authsrv; int vlan_id; diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index afabdaa7..95b701cc 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -28,6 +28,7 @@ #include "vlan_init.h" #include "p2p_hostapd.h" #include "ap_drv_ops.h" +#include "gas_serv.h" #include "sta_info.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, @@ -194,6 +195,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) if (set_beacon) ieee802_11_set_beacons(hapd->iface); + wpa_printf(MSG_DEBUG, "%s: cancel ap_handle_timer for " MACSTR, + __func__, MAC2STR(sta->addr)); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); @@ -218,6 +221,15 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) p2p_group_notif_disassoc(hapd->p2p_group, sta->addr); #endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + if (sta->gas_dialog) { + int i; + for (i = 0; i < GAS_DIALOG_MAX; i++) + gas_serv_dialog_clear(&sta->gas_dialog[i]); + os_free(sta->gas_dialog); + } +#endif /* CONFIG_INTERWORKING */ + wpabuf_free(sta->wps_ie); wpabuf_free(sta->p2p_ie); @@ -262,6 +274,9 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) struct sta_info *sta = timeout_ctx; unsigned long next_time = 0; + wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d", + __func__, MAC2STR(sta->addr), sta->flags, + sta->timeout_next); if (sta->timeout_next == STA_REMOVE) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "deauthenticated due to " @@ -274,6 +289,12 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC)) { int inactive_sec; + /* + * Add random value to timeout so that we don't end up bouncing + * all stations at the same time if we have lots of associated + * stations that are idle (but keep re-associating). + */ + int fuzz = os_random() % 20; inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr); if (inactive_sec == -1) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, @@ -285,7 +306,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) * Anyway, try again after the next inactivity timeout, * but do not disconnect the station now. */ - next_time = hapd->conf->ap_max_inactivity; + next_time = hapd->conf->ap_max_inactivity + fuzz; } else if (inactive_sec < hapd->conf->ap_max_inactivity && sta->flags & WLAN_STA_ASSOC) { /* station activity detected; reset timeout state */ @@ -293,7 +314,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) "Station " MACSTR " has been active %is ago", MAC2STR(sta->addr), inactive_sec); sta->timeout_next = STA_NULLFUNC; - next_time = hapd->conf->ap_max_inactivity - + next_time = hapd->conf->ap_max_inactivity + fuzz - inactive_sec; } else { wpa_msg(hapd->msg_ctx, MSG_DEBUG, @@ -320,6 +341,9 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) } if (next_time) { + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%lu seconds)", + __func__, MAC2STR(sta->addr), next_time); eloop_register_timeout(next_time, 0, ap_handle_timer, hapd, sta); return; @@ -353,6 +377,9 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) switch (sta->timeout_next) { case STA_NULLFUNC: sta->timeout_next = STA_DISASSOC; + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%d seconds - AP_DISASSOC_DELAY)", + __func__, MAC2STR(sta->addr), AP_DISASSOC_DELAY); eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer, hapd, sta); break; @@ -369,6 +396,9 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) HOSTAPD_LEVEL_INFO, "disassociated due to " "inactivity"); sta->timeout_next = STA_DEAUTH; + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)", + __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY); eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, hapd, sta); mlme_disassociate_indication( @@ -397,8 +427,14 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) struct sta_info *sta = timeout_ctx; u8 addr[ETH_ALEN]; - if (!(sta->flags & WLAN_STA_AUTH)) + if (!(sta->flags & WLAN_STA_AUTH)) { + if (sta->flags & WLAN_STA_GAS) { + wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA " + "entry " MACSTR, MAC2STR(sta->addr)); + ap_free_sta(hapd, sta); + } return; + } mlme_deauthenticate_indication(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); @@ -455,6 +491,10 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) sta->acct_interim_interval = hapd->conf->acct_interim_interval; /* initialize STA info data */ + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%d seconds - ap_max_inactivity)", + __func__, MAC2STR(addr), + hapd->conf->ap_max_inactivity); eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, ap_handle_timer, hapd, sta); os_memcpy(sta->addr, addr, ETH_ALEN); @@ -528,6 +568,11 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, sta->flags &= ~WLAN_STA_ASSOC; ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_DEAUTH; + wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + "for " MACSTR " (%d seconds - " + "AP_MAX_INACTIVITY_AFTER_DISASSOC)", + __func__, MAC2STR(sta->addr), + AP_MAX_INACTIVITY_AFTER_DISASSOC); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0, ap_handle_timer, hapd, sta); @@ -561,6 +606,11 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_REMOVE; + wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + "for " MACSTR " (%d seconds - " + "AP_MAX_INACTIVITY_AFTER_DEAUTH)", + __func__, MAC2STR(sta->addr), + AP_MAX_INACTIVITY_AFTER_DEAUTH); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, ap_handle_timer, hapd, sta); @@ -576,6 +626,23 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, } +#ifdef CONFIG_WPS +int ap_sta_wps_cancel(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + if (sta && (sta->flags & WLAN_STA_WPS)) { + ap_sta_deauthenticate(hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); + wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR, + __func__, MAC2STR(sta->addr)); + return 1; + } + + return 0; +} +#endif /* CONFIG_WPS */ + + int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, int old_vlanid) { @@ -778,11 +845,20 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized) { const u8 *dev_addr = NULL; +#ifdef CONFIG_P2P + u8 addr[ETH_ALEN]; +#endif /* CONFIG_P2P */ + if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED)) return; #ifdef CONFIG_P2P - dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr); + if (hapd->p2p_group == NULL) { + if (sta->p2p_ie != NULL && + p2p_parse_dev_addr_in_p2p_ie(sta->p2p_ie, addr) == 0) + dev_addr = addr; + } else + dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr); #endif /* CONFIG_P2P */ if (authorized) { @@ -848,6 +924,11 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + "for " MACSTR " (%d seconds - " + "AP_MAX_INACTIVITY_AFTER_DEAUTH)", + __func__, MAC2STR(sta->addr), + AP_MAX_INACTIVITY_AFTER_DEAUTH); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, ap_handle_timer, hapd, sta); diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 60b3a7ba..cef428df 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -27,6 +27,7 @@ #define WLAN_STA_WDS BIT(14) #define WLAN_STA_ASSOC_REQ_OK BIT(15) #define WLAN_STA_WPS2 BIT(16) +#define WLAN_STA_GAS BIT(17) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) @@ -107,6 +108,12 @@ struct sta_info { struct os_time sa_query_start; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_INTERWORKING +#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */ + struct gas_dialog_info *gas_dialog; + u8 gas_dialog_next; +#endif /* CONFIG_INTERWORKING */ + struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */ }; @@ -137,7 +144,6 @@ int ap_for_each_sta(struct hostapd_data *hapd, struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); -void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); void hostapd_free_stas(struct hostapd_data *hapd); void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, @@ -149,6 +155,10 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason); void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason); +#ifdef CONFIG_WPS +int ap_sta_wps_cancel(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx); +#endif /* CONFIG_WPS */ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, int old_vlanid); void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c index 60088ee5..dd5aa687 100644 --- a/src/ap/tkip_countermeasures.c +++ b/src/ap/tkip_countermeasures.c @@ -1,6 +1,6 @@ /* * hostapd / TKIP countermeasures - * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "radius/radius.h" #include "hostapd.h" #include "sta_info.h" #include "ap_mlme.h" @@ -44,12 +45,17 @@ static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd) eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop, hapd, NULL); - for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { + while ((sta = hapd->sta_list)) { + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET; + if (sta->flags & WLAN_STA_AUTH) { + mlme_deauthenticate_indication( + hapd, sta, + WLAN_REASON_MICHAEL_MIC_FAILURE); + } hostapd_drv_sta_deauth(hapd, sta->addr, WLAN_REASON_MICHAEL_MIC_FAILURE); - ap_sta_set_authorized(hapd, sta, 0); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - hostapd_drv_sta_remove(hapd, sta->addr); + ap_free_sta(hapd, sta); } } diff --git a/src/ap/utils.c b/src/ap/utils.c index 36c1182b..3e9fc081 100644 --- a/src/ap/utils.c +++ b/src/ap/utils.c @@ -17,7 +17,8 @@ int hostapd_register_probereq_cb(struct hostapd_data *hapd, int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, - const u8 *ie, size_t ie_len), + const u8 *ie, size_t ie_len, + int ssi_signal), void *ctx) { struct hostapd_probereq_cb *n; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index d1b9b4d2..1d942a44 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1641,10 +1641,23 @@ SM_STATE(WPA_PTK, AUTHENTICATION2) wpa_group_ensure_init(sm->wpa_auth, sm->group); - os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN); + /* + * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat + * ambiguous. The Authenticator state machine uses a counter that is + * incremented by one for each 4-way handshake. However, the security + * analysis of 4-way handshake points out that unpredictable nonces + * help in preventing precomputation attacks. Instead of the state + * machine definition, use an unpredictable nonce value here to provide + * stronger protection against potential precomputation attacks. + */ + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_ERROR, "WPA: Failed to get random data for " + "ANonce."); + wpa_sta_disconnect(sm->wpa_auth, sm->addr); + return; + } wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce, WPA_NONCE_LEN); - inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); sm->ReAuthenticationRequest = FALSE; /* IEEE 802.11i does not clear TimeoutCtr here, but this is more * logical place than INITIALIZE since AUTHENTICATION2 can be diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index 89992179..07ce06c1 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -1,6 +1,6 @@ /* * hostapd / WPS integration - * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,6 +12,8 @@ #include "utils/eloop.h" #include "utils/uuid.h" #include "crypto/dh_groups.h" +#include "crypto/dh_group5.h" +#include "crypto/random.h" #include "common/wpa_ctrl.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -20,6 +22,7 @@ #include "wps/wps.h" #include "wps/wps_defs.h" #include "wps/wps_dev_attr.h" +#include "wps/wps_attr_parse.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" @@ -37,7 +40,8 @@ static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd); static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, const u8 *bssid, - const u8 *ie, size_t ie_len); + const u8 *ie, size_t ie_len, + int ssi_signal); static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); @@ -183,19 +187,23 @@ static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, struct wps_stop_reg_data { struct hostapd_data *current_hapd; const u8 *uuid_e; + const u8 *dev_pw; + size_t dev_pw_len; }; static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx) { struct wps_stop_reg_data *data = ctx; if (hapd != data->current_hapd && hapd->wps != NULL) - wps_registrar_complete(hapd->wps->registrar, data->uuid_e); + wps_registrar_complete(hapd->wps->registrar, data->uuid_e, + data->dev_pw, data->dev_pw_len); return 0; } static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, - const u8 *uuid_e) + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len) { struct hostapd_data *hapd = ctx; char uuid[40]; @@ -209,6 +217,8 @@ static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, mac_addr, uuid_e); data.current_hapd = hapd; data.uuid_e = uuid_e; + data.dev_pw = dev_pw; + data.dev_pw_len = dev_pw_len; hostapd_wps_for_each(hapd, wps_stop_registrar, &data); } @@ -984,6 +994,20 @@ int hostapd_init_wps_complete(struct hostapd_data *hapd) } +static void hostapd_wps_nfc_clear(struct wps_context *wps) +{ +#ifdef CONFIG_WPS_NFC + wps->ap_nfc_dev_pw_id = 0; + wpabuf_free(wps->ap_nfc_dh_pubkey); + wps->ap_nfc_dh_pubkey = NULL; + wpabuf_free(wps->ap_nfc_dh_privkey); + wps->ap_nfc_dh_privkey = NULL; + wpabuf_free(wps->ap_nfc_dev_pw); + wps->ap_nfc_dev_pw = NULL; +#endif /* CONFIG_WPS_NFC */ +} + + void hostapd_deinit_wps(struct hostapd_data *hapd) { eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); @@ -1001,6 +1025,7 @@ void hostapd_deinit_wps(struct hostapd_data *hapd) wpabuf_free(hapd->wps->oob_conf.pubkey_hash); wpabuf_free(hapd->wps->oob_conf.dev_password); wps_free_pending_msgs(hapd->wps->upnp_msgs); + hostapd_wps_nfc_clear(hapd->wps); os_free(hapd->wps); hapd->wps = NULL; hostapd_wps_clear_ies(hapd); @@ -1098,6 +1123,24 @@ int hostapd_wps_button_pushed(struct hostapd_data *hapd, } +static int wps_cancel(struct hostapd_data *hapd, void *ctx) +{ + if (hapd->wps == NULL) + return 0; + + wps_registrar_wps_cancel(hapd->wps->registrar); + ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); + + return 0; +} + + +int hostapd_wps_cancel(struct hostapd_data *hapd) +{ + return hostapd_wps_for_each(hapd, wps_cancel, NULL); +} + + #ifdef CONFIG_WPS_OOB int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, char *path, char *method, char *name) @@ -1154,7 +1197,8 @@ error: static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, const u8 *bssid, - const u8 *ie, size_t ie_len) + const u8 *ie, size_t ie_len, + int ssi_signal) { struct hostapd_data *hapd = ctx; struct wpabuf *wps_ie; @@ -1471,3 +1515,156 @@ int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, return wps_registrar_config_ap(hapd->wps->registrar, &cred); } + + +#ifdef CONFIG_WPS_NFC + +struct wps_nfc_password_token_data { + const u8 *oob_dev_pw; + size_t oob_dev_pw_len; + int added; +}; + + +static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx) +{ + struct wps_nfc_password_token_data *data = ctx; + int ret; + + if (hapd->wps == NULL) + return 0; + ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar, + data->oob_dev_pw, + data->oob_dev_pw_len); + if (ret == 0) + data->added++; + return ret; +} + + +static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd, + struct wps_parse_attr *attr) +{ + struct wps_nfc_password_token_data data; + + data.oob_dev_pw = attr->oob_dev_password; + data.oob_dev_pw_len = attr->oob_dev_password_len; + data.added = 0; + if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0) + return -1; + return data.added ? 0 : -1; +} + + +static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd, + const struct wpabuf *wps) +{ + struct wps_parse_attr attr; + + wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps); + + if (wps_parse_msg(wps, &attr)) { + wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag"); + return -1; + } + + if (attr.oob_dev_password) + return hostapd_wps_add_nfc_password_token(hapd, &attr); + + wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag"); + return -1; +} + + +int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd, + const struct wpabuf *data) +{ + const struct wpabuf *wps = data; + struct wpabuf *tmp = NULL; + int ret; + + if (wpabuf_len(data) < 4) + return -1; + + if (*wpabuf_head_u8(data) != 0x10) { + /* Assume this contains full NDEF record */ + tmp = ndef_parse_wifi(data); + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF"); + return -1; + } + wps = tmp; + } + + ret = hostapd_wps_nfc_tag_process(hapd, wps); + wpabuf_free(tmp); + return ret; +} + + +struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, + int ndef) +{ + struct wpabuf *ret; + + if (hapd->wps == NULL) + return NULL; + + ret = wps_get_oob_cred(hapd->wps); + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} + + +struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef) +{ + return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id, + &hapd->conf->wps_nfc_dh_pubkey, + &hapd->conf->wps_nfc_dh_privkey, + &hapd->conf->wps_nfc_dev_pw); +} + + +int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd) +{ + struct wps_context *wps = hapd->wps; + + if (wps == NULL) + return -1; + + if (!hapd->conf->wps_nfc_dh_pubkey || + !hapd->conf->wps_nfc_dh_privkey || + !hapd->conf->wps_nfc_dev_pw || + !hapd->conf->wps_nfc_dev_pw_id) + return -1; + + hostapd_wps_nfc_clear(wps); + wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id; + wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); + wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); + wps->ap_nfc_dev_pw = wpabuf_dup(hapd->conf->wps_nfc_dev_pw); + + if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey || + !wps->ap_nfc_dev_pw) { + hostapd_wps_nfc_clear(wps); + return -1; + } + + return 0; +} + + +void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd) +{ + hostapd_wps_nfc_clear(hapd->wps); +} + +#endif /* CONFIG_WPS_NFC */ diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h index 91942250..f968e151 100644 --- a/src/ap/wps_hostapd.h +++ b/src/ap/wps_hostapd.h @@ -1,6 +1,6 @@ /* * hostapd / WPS integration - * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -20,6 +20,7 @@ int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, const char *uuid, const char *pin, int timeout); int hostapd_wps_button_pushed(struct hostapd_data *hapd, const u8 *p2p_dev_addr); +int hostapd_wps_cancel(struct hostapd_data *hapd); int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, char *path, char *method, char *name); int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, @@ -32,6 +33,13 @@ int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, void hostapd_wps_update_ie(struct hostapd_data *hapd); int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, const char *auth, const char *encr, const char *key); +int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd, + const struct wpabuf *data); +struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, + int ndef); +struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef); +int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd); +void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd); #else /* CONFIG_WPS */ @@ -67,6 +75,11 @@ static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd, return 0; } +static inline int hostapd_wps_cancel(struct hostapd_data *hapd) +{ + return 0; +} + #endif /* CONFIG_WPS */ #endif /* WPS_HOSTAPD_H */ diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index b0d310dd..d65675cd 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -97,6 +97,11 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->p2p = pos; elems->p2p_len = elen; break; + case HS20_INDICATION_OUI_TYPE: + /* Hotspot 2.0 */ + elems->hs20 = pos; + elems->hs20_len = elen; + break; default: wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " @@ -257,6 +262,15 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->interworking = pos; elems->interworking_len = elen; break; + case WLAN_EID_EXT_CAPAB: + elems->ext_capab = pos; + elems->ext_capab_len = elen; + break; + case WLAN_EID_BSS_MAX_IDLE_PERIOD: + if (elen < 3) + break; + elems->bss_max_idle_period = pos; + break; default: unknown++; if (!show_errors) diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index bfac88ce..d9b2b6c1 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -37,6 +37,9 @@ struct ieee802_11_elems { const u8 *p2p; const u8 *link_id; const u8 *interworking; + const u8 *hs20; + const u8 *ext_capab; + const u8 *bss_max_idle_period; u8 ssid_len; u8 supp_rates_len; @@ -63,6 +66,8 @@ struct ieee802_11_elems { u8 vendor_ht_cap_len; u8 p2p_len; u8 interworking_len; + u8 hs20_len; + u8 ext_capab_len; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 3bef006a..b4f92759 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -228,12 +228,25 @@ #define WLAN_EID_20_40_BSS_INTOLERANT 73 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 #define WLAN_EID_MMIE 76 +#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90 +#define WLAN_EID_TFS_REQ 91 +#define WLAN_EID_TFS_RESP 92 +#define WLAN_EID_WNMSLEEP 93 #define WLAN_EID_TIME_ZONE 98 #define WLAN_EID_LINK_ID 101 #define WLAN_EID_INTERWORKING 107 #define WLAN_EID_ADV_PROTO 108 #define WLAN_EID_ROAMING_CONSORTIUM 111 #define WLAN_EID_EXT_CAPAB 127 +#define WLAN_EID_VHT_CAP 191 +#define WLAN_EID_VHT_OPERATION 192 +#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193 +#define WLAN_EID_VHT_WIDE_BW_CHSWITCH 194 +#define WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE 195 +#define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196 +#define WLAN_EID_VHT_AID 197 +#define WLAN_EID_VHT_QUIET_CHANNEL 198 +#define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199 #define WLAN_EID_VENDOR_SPECIFIC 221 @@ -254,6 +267,7 @@ #define WLAN_ACTION_VENDOR_SPECIFIC 127 /* Public action codes */ +#define WLAN_PA_20_40_BSS_COEX 0 #define WLAN_PA_VENDOR_SPECIFIC 9 #define WLAN_PA_GAS_INITIAL_REQ 10 #define WLAN_PA_GAS_INITIAL_RESP 11 @@ -487,6 +501,17 @@ struct ieee80211_mgmt { } STRUCT_PACKED sa_query_resp; struct { u8 action; + u8 dialogtoken; + u8 variable[0]; + } STRUCT_PACKED wnm_sleep_req; + struct { + u8 action; + u8 dialogtoken; + le16 keydata_len; + u8 variable[0]; + } STRUCT_PACKED wnm_sleep_resp; + struct { + u8 action; u8 variable[0]; } STRUCT_PACKED public_action; struct { @@ -534,6 +559,19 @@ struct ieee80211_ht_operation { u8 basic_set[16]; } STRUCT_PACKED; + +struct ieee80211_vht_capabilities { + le32 vht_capabilities_info; + u8 vht_supported_mcs_set[8]; +} STRUCT_PACKED; + +struct ieee80211_vht_operation { + u8 vht_op_info_chwidth; + u8 vht_op_info_chan_center_freq_seg0_idx; + u8 vht_op_info_chan_center_freq_seg1_idx; + le16 vht_basic_mcs_set; +} STRUCT_PACKED; + #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ @@ -630,12 +668,40 @@ struct ieee80211_ht_operation { #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 +/* VHT Defines */ +#define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0)) +#define VHT_CAP_MAX_MPDU_LENGTH_11454 ((u32) BIT(1)) +#define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2)) +#define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3)) +#define VHT_CAP_RXLDPC ((u32) BIT(4)) +#define VHT_CAP_SHORT_GI_80 ((u32) BIT(5)) +#define VHT_CAP_SHORT_GI_160 ((u32) BIT(6)) +#define VHT_CAP_TXSTBC ((u32) BIT(7)) +#define VHT_CAP_RXSTBC_1 ((u32) BIT(8)) +#define VHT_CAP_RXSTBC_2 ((u32) BIT(9)) +#define VHT_CAP_RXSTBC_3 ((u32) BIT(8) | BIT(9)) +#define VHT_CAP_RXSTBC_4 ((u32) BIT(10)) +#define VHT_CAP_SU_BEAMFORMER_CAPABLE ((u32) BIT(11)) +#define VHT_CAP_SU_BEAMFORMEE_CAPABLE ((u32) BIT(12)) +#define VHT_CAP_BEAMFORMER_ANTENNAS_MAX ((u32) BIT(13) | BIT(14)) +#define VHT_CAP_SOUNDING_DIMENTION_MAX ((u32) BIT(16) | BIT(17)) +#define VHT_CAP_MU_BEAMFORMER_CAPABLE ((u32) BIT(19)) +#define VHT_CAP_MU_BEAMFORMEE_CAPABLE ((u32) BIT(20)) +#define VHT_CAP_VHT_TXOP_PS ((u32) BIT(21)) +#define VHT_CAP_HTC_VHT ((u32) BIT(22)) +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT ((u32) BIT(23)) +#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB ((u32) BIT(27)) +#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27)) +#define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28)) +#define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29)) + #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) * 00:50:F2 */ #define WPA_IE_VENDOR_TYPE 0x0050f201 #define WPS_IE_VENDOR_TYPE 0x0050f204 #define OUI_WFA 0x506f9a #define P2P_IE_VENDOR_TYPE 0x506f9a09 +#define HS20_IE_VENDOR_TYPE 0x506f9a10 #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 @@ -749,6 +815,16 @@ enum { }; +#define HS20_INDICATION_OUI_TYPE 16 +#define HS20_ANQP_OUI_TYPE 17 +#define HS20_STYPE_QUERY_LIST 1 +#define HS20_STYPE_CAPABILITY_LIST 2 +#define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3 +#define HS20_STYPE_WAN_METRICS 4 +#define HS20_STYPE_CONNECTION_CAPABILITY 5 +#define HS20_STYPE_NAI_HOME_REALM_QUERY 6 +#define HS20_STYPE_OPERATING_CLASS 7 + /* Wi-Fi Direct (P2P) */ #define P2P_OUI_TYPE 9 @@ -915,4 +991,48 @@ enum wnm_action { #define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3) #define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4) +/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */ +#define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0) +#define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1) +#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ BIT(2) +#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ BIT(3) +#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT BIT(4) + +struct ieee80211_2040_bss_coex_ie { + u8 element_id; + u8 length; + u8 coex_param; +} STRUCT_PACKED; + +struct ieee80211_2040_intol_chan_report { + u8 element_id; + u8 length; + u8 op_class; + u8 variable[0]; /* Channel List */ +} STRUCT_PACKED; + +/* IEEE 802.11v - WNM-Sleep Mode element */ +struct wnm_sleep_element { + u8 eid; /* WLAN_EID_WNMSLEEP */ + u8 len; + u8 action_type; /* WLAN_WNM_SLEEP_ENTER/EXIT */ + u8 status; + le16 intval; +} STRUCT_PACKED; + +enum wnm_sleep_mode_response_status { + WNM_STATUS_SLEEP_ACCEPT = 0, + WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1, + WNM_STATUS_DENIED_ACTION = 2, + WNM_STATUS_DENIED_TMP = 3, + WNM_STATUS_DENIED_KEY = 4, + WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5 +}; + +/* WNM-Sleep Mode subelement IDs */ +enum wnm_sleep_mode_subelement_id { + WNM_SLEEP_SUBELEM_GTK = 0, + WNM_SLEEP_SUBELEM_IGTK = 1 +}; + #endif /* IEEE802_11_DEFS_H */ diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 5768fdd3..a440b69e 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -120,6 +120,8 @@ extern "C" { #define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ " /* parameters: <peer address> */ #define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP " +/* parameters: <peer address> <status> */ +#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE" /* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */ #define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ " /* parameters: <src addr> <update indicator> <TLVs> */ @@ -143,6 +145,27 @@ extern "C" { #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED " +/* BSS command information masks */ + +#define WPA_BSS_MASK_ALL 0xFFFFFFFF +#define WPA_BSS_MASK_ID BIT(0) +#define WPA_BSS_MASK_BSSID BIT(1) +#define WPA_BSS_MASK_FREQ BIT(2) +#define WPA_BSS_MASK_BEACON_INT BIT(3) +#define WPA_BSS_MASK_CAPABILITIES BIT(4) +#define WPA_BSS_MASK_QUAL BIT(5) +#define WPA_BSS_MASK_NOISE BIT(6) +#define WPA_BSS_MASK_LEVEL BIT(7) +#define WPA_BSS_MASK_TSF BIT(8) +#define WPA_BSS_MASK_AGE BIT(9) +#define WPA_BSS_MASK_IE BIT(10) +#define WPA_BSS_MASK_FLAGS BIT(11) +#define WPA_BSS_MASK_SSID BIT(12) +#define WPA_BSS_MASK_WPS_SCAN BIT(13) +#define WPA_BSS_MASK_P2P_SCAN BIT(14) +#define WPA_BSS_MASK_INTERNETW BIT(15) + + /* wpa_supplicant/hostapd control interface access */ /** diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index 1171f29f..8506fff4 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / wrapper functions for libcrypto - * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,7 @@ #include <openssl/bn.h> #include <openssl/evp.h> #include <openssl/dh.h> +#include <openssl/hmac.h> #include "common.h" #include "wpabuf.h" @@ -452,6 +453,41 @@ err: } +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) +{ + DH *dh; + + dh = DH_new(); + if (dh == NULL) + return NULL; + + dh->g = BN_new(); + if (dh->g == NULL || BN_set_word(dh->g, 2) != 1) + goto err; + + dh->p = get_group5_prime(); + if (dh->p == NULL) + goto err; + + dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL); + if (dh->priv_key == NULL) + goto err; + + dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL); + if (dh->pub_key == NULL) + goto err; + + if (DH_generate_key(dh) != 1) + goto err; + + return dh; + +err: + DH_free(dh); + return NULL; +} + + struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, const struct wpabuf *own_private) { @@ -497,3 +533,93 @@ void dh5_free(void *ctx) dh = ctx; DH_free(dh); } + + +struct crypto_hash { + HMAC_CTX ctx; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + const EVP_MD *md; + + switch (alg) { +#ifndef OPENSSL_NO_MD5 + case CRYPTO_HASH_ALG_HMAC_MD5: + md = EVP_md5(); + break; +#endif /* OPENSSL_NO_MD5 */ +#ifndef OPENSSL_NO_SHA + case CRYPTO_HASH_ALG_HMAC_SHA1: + md = EVP_sha1(); + break; +#endif /* OPENSSL_NO_SHA */ +#ifndef OPENSSL_NO_SHA256 +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + md = EVP_sha256(); + break; +#endif /* CONFIG_SHA256 */ +#endif /* OPENSSL_NO_SHA256 */ + default: + return NULL; + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) { + os_free(ctx); + return NULL; + } +#endif /* openssl < 0.9.9 */ + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (ctx == NULL) + return; + HMAC_Update(&ctx->ctx, data, len); +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + unsigned int mdlen; + int res; + + if (ctx == NULL) + return -2; + + if (mac == NULL || len == NULL) { + os_free(ctx); + return 0; + } + + mdlen = *len; +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx->ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx->ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx->ctx); + os_free(ctx); + + if (res == 1) { + *len = mdlen; + return 0; + } + + return -1; +} diff --git a/src/crypto/dh_group5.c b/src/crypto/dh_group5.c index 9a94ca59..ccdbfc81 100644 --- a/src/crypto/dh_group5.c +++ b/src/crypto/dh_group5.c @@ -1,6 +1,6 @@ /* * Diffie-Hellman group 5 operations - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,6 +22,12 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) } +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) +{ + return (void *) 1; +} + + struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, const struct wpabuf *own_private) { diff --git a/src/crypto/dh_group5.h b/src/crypto/dh_group5.h index 88134273..abee8eaa 100644 --- a/src/crypto/dh_group5.h +++ b/src/crypto/dh_group5.h @@ -1,6 +1,6 @@ /* * Diffie-Hellman group 5 operations - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #define DH_GROUP5_H void * dh5_init(struct wpabuf **priv, struct wpabuf **publ); +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ); struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, const struct wpabuf *own_private); void dh5_free(void *ctx); diff --git a/src/crypto/random.c b/src/crypto/random.c index 55e93913..d85c3e66 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -128,8 +128,6 @@ void random_add_randomness(const void *buf, size_t len) static unsigned int count = 0; count++; - wpa_printf(MSG_MSGDUMP, "Add randomness: count=%u entropy=%u", - count, entropy); if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) { /* * No need to add more entropy at this point, so save CPU and @@ -137,6 +135,8 @@ void random_add_randomness(const void *buf, size_t len) */ return; } + wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u", + count, entropy); os_get_time(&t); wpa_hexdump_key(MSG_EXCESSIVE, "random pool", diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 2bd3bbbb..990f6e6e 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -21,8 +21,10 @@ struct tls_keys { }; enum tls_event { + TLS_CERT_CHAIN_SUCCESS, TLS_CERT_CHAIN_FAILURE, - TLS_PEER_CERTIFICATE + TLS_PEER_CERTIFICATE, + TLS_ALERT }; /* @@ -57,6 +59,12 @@ union tls_event_data { const u8 *hash; size_t hash_len; } peer_cert; + + struct { + int is_local; + const char *type; + const char *description; + } alert; }; struct tls_config { diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index aaa920bf..09b02e4f 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -527,6 +527,15 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) else conn->write_alerts++; } + if (tls_global->event_cb != NULL) { + union tls_event_data ev; + os_memset(&ev, 0, sizeof(ev)); + ev.alert.is_local = !(where & SSL_CB_READ); + ev.alert.type = SSL_alert_type_string_long(ret); + ev.alert.description = SSL_alert_desc_string_long(ret); + tls_global->event_cb(tls_global->cb_ctx, TLS_ALERT, + &ev); + } } else if (where & SSL_CB_EXIT && ret <= 0) { wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", str, ret == 0 ? "failed" : "error", @@ -1274,6 +1283,10 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) TLS_FAIL_SERVER_CHAIN_PROBE); } + if (preverify_ok && tls_global->event_cb != NULL) + tls_global->event_cb(tls_global->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); + return preverify_ok; } diff --git a/src/drivers/driver.h b/src/drivers/driver.h index f1d4a144..0aab61ec 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -100,6 +100,16 @@ struct hostapd_hw_modes { */ u8 a_mpdu_params; + /** + * vht_capab - VHT (IEEE 802.11ac) capabilities + */ + u32 vht_capab; + + /** + * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters + */ + u8 vht_mcs_set[8]; + unsigned int flags; /* HOSTAPD_MODE_FLAG_* */ }; @@ -333,6 +343,13 @@ struct wpa_driver_associate_params { int freq; /** + * bg_scan_period - Background scan period in seconds, 0 to disable + * background scan, or -1 to indicate no change to default driver + * configuration + */ + int bg_scan_period; + + /** * wpa_ie - WPA information element for (Re)Association Request * WPA information element to be included in (Re)Association * Request (including information element id and length). Use @@ -707,6 +724,13 @@ struct wpa_driver_ap_params { * enabled. */ u8 access_network_type; + + /** + * ap_max_inactivity - Timeout in seconds to detect STA's inactivity + * + * This is used by driver which advertises this capability. + */ + int ap_max_inactivity; }; /** @@ -726,6 +750,7 @@ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002 #define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004 #define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008 +#define WPA_DRIVER_CAPA_ENC_WEP128 0x00000010 unsigned int enc; #define WPA_DRIVER_AUTH_OPEN 0x00000001 @@ -790,6 +815,8 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD 0x00200000 /* Driver supports U-APSD in AP mode */ #define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000 +/* Driver supports inactivity timer in AP mode */ +#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000 unsigned int flags; int max_scan_ssids; @@ -2003,6 +2030,16 @@ struct wpa_driver_ops { int (*deinit_ap)(void *priv); /** + * deinit_p2p_cli - Deinitialize P2P client mode + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This optional function can be used to disable P2P client mode. It + * can be used to change the interface type back to station mode. + */ + int (*deinit_p2p_cli)(void *priv); + + /** * suspend - Notification on system suspend/hibernate event * @priv: Private driver interface data */ @@ -2508,19 +2545,30 @@ struct wpa_driver_ops { */ void (*poll_client)(void *priv, const u8 *own_addr, const u8 *addr, int qos); -#ifdef ANDROID_P2P + /** - * switch_channel - Announce channel switch and migrate the GO to a - * given frequency. + * radio_disable - Disable/enable radio * @priv: Private driver interface data - * @freq: frequency in MHz + * @disabled: 1=disable 0=enable radio * Returns: 0 on success, -1 on failure * - * This function is used to move the GO to the legacy STA channel to avoid - * frequency conflict in single channel concurrency. + * This optional command is for testing purposes. It can be used to + * disable the radio on a testbed device to simulate out-of-radio-range + * conditions. + */ + int (*radio_disable)(void *priv, int disabled); + + /** + * switch_channel - Announce channel switch and migrate the GO to the + * given frequency + * @priv: Private driver interface data + * @freq: Frequency in MHz + * Returns: 0 on success, -1 on failure + * + * This function is used to move the GO to the legacy STA channel to + * avoid frequency conflict in single channel concurrency. */ int (*switch_channel)(void *priv, unsigned int freq); -#endif }; @@ -2951,7 +2999,14 @@ enum wpa_event_type { /** * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status */ - EVENT_EAPOL_TX_STATUS + EVENT_EAPOL_TX_STATUS, + + /** + * EVENT_CH_SWITCH - AP or GO decided to switch channels + * + * Described in wpa_event_data.ch_switch + * */ + EVENT_CH_SWITCH }; @@ -3267,7 +3322,7 @@ union wpa_event_data { const u8 *frame; size_t frame_len; u32 datarate; - u32 ssi_signal; + int ssi_signal; /* dBm */ } rx_mgmt; /** @@ -3385,6 +3440,11 @@ union wpa_event_data { * ie_len - Length of ie buffer in octets */ size_t ie_len; + + /** + * signal - signal strength in dBm (or 0 if not available) + */ + int ssi_signal; } rx_probe_req; /** @@ -3541,6 +3601,18 @@ union wpa_event_data { int data_len; int ack; } eapol_tx_status; + + /** + * struct ch_switch + * @freq: Frequency of new channel in MHz + * @ht_enabled: Whether this is an HT channel + * @ch_offset: Secondary channel offset + */ + struct ch_switch { + int freq; + int ht_enabled; + int ch_offset; + } ch_switch; }; /** diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index b17d1a6a..73898d3f 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -5,14 +5,8 @@ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> * Copyright (c) 2009, Atheros Communications * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -39,11 +33,11 @@ #ifdef CONFIG_WPS #include <netpacket/packet.h> +#endif /* CONFIG_WPS */ #ifndef ETH_P_80211_RAW #define ETH_P_80211_RAW 0x0019 #endif -#endif /* CONFIG_WPS */ #include "linux_wext.h" @@ -73,6 +67,7 @@ struct atheros_driver_data { struct wpabuf *wpa_ie; struct wpabuf *wps_beacon_ie; struct wpabuf *wps_probe_resp_ie; + u8 own_addr[ETH_ALEN]; }; static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, @@ -732,8 +727,8 @@ atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, } #ifdef CONFIG_WPS -static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) +static void atheros_raw_recv_wps(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) { struct atheros_driver_data *drv = ctx; const struct ieee80211_mgmt *mgmt; @@ -762,28 +757,139 @@ static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, } #endif /* CONFIG_WPS */ -static int atheros_receive_probe_req(struct atheros_driver_data *drv) +#ifdef CONFIG_IEEE80211R +static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_driver_data *drv = ctx; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u16 fc; + u16 stype; + int ielen; + const u8 *iebuf; + + /* Do 11R processing for ASSOC/AUTH/FT ACTION frames */ + if (len < IEEE80211_HDRLEN) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) + return; + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, + (int) len); + + if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", + __func__); + return; + } + switch (stype) { + case WLAN_FC_STYPE_ASSOC_REQ: + if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.assoc_req)) + break; + ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); + iebuf = mgmt->u.assoc_req.variable; + drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0); + break; + case WLAN_FC_STYPE_REASSOC_REQ: + if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.reassoc_req)) + break; + ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); + iebuf = mgmt->u.reassoc_req.variable; + drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1); + break; + case WLAN_FC_STYPE_ACTION: + if (&mgmt->u.action.category > buf + len) + break; + os_memset(&event, 0, sizeof(event)); + event.rx_action.da = mgmt->da; + event.rx_action.sa = mgmt->sa; + event.rx_action.bssid = mgmt->bssid; + event.rx_action.category = mgmt->u.action.category; + event.rx_action.data = &mgmt->u.action.category; + event.rx_action.len = buf + len - event.rx_action.data; + wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event); + break; + case WLAN_FC_STYPE_AUTH: + if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.auth)) + break; + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); + os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN); + event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); + event.auth.status_code = + le_to_host16(mgmt->u.auth.status_code); + event.auth.auth_transaction = + le_to_host16(mgmt->u.auth.auth_transaction); + event.auth.ies = mgmt->u.auth.variable; + event.auth.ies_len = len - IEEE80211_HDRLEN - + sizeof(mgmt->u.auth); + wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event); + break; + default: + break; + } +} +#endif /* CONFIG_IEEE80211R */ + +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) +static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) { - int ret = 0; #ifdef CONFIG_WPS + atheros_raw_recv_wps(ctx, src_addr, buf, len); +#endif /* CONFIG_WPS */ +#ifdef CONFIG_IEEE80211R + atheros_raw_recv_11r(ctx, src_addr, buf, len); +#endif /* CONFIG_IEEE80211R */ +} +#endif /* CONFIG_WPS || CONFIG_IEEE80211R */ + +static int atheros_receive_pkt(struct atheros_driver_data *drv) +{ + int ret = 0; struct ieee80211req_set_filter filt; wpa_printf(MSG_DEBUG, "%s Enter", __func__); - filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ; - - ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, - sizeof(struct ieee80211req_set_filter)); - if (ret) - return ret; + filt.app_filterype = 0; +#ifdef CONFIG_WPS + filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ; +#endif /* CONFIG_WPS */ +#ifdef CONFIG_IEEE80211R + filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ | + IEEE80211_FILTER_TYPE_AUTH); +#endif + if (filt.app_filterype) { + ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); + if (ret) + return ret; + } +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, atheros_raw_receive, drv, 1); if (drv->sock_raw == NULL) return -1; -#endif /* CONFIG_WPS */ +#endif /* CONFIG_WPS || CONFIG_IEEE80211R */ + if (l2_packet_get_own_addr(drv->sock_xmit, drv->own_addr)) + return -1; return ret; } +static int atheros_reset_appfilter(struct atheros_driver_data *drv) +{ + struct ieee80211req_set_filter filt; + filt.app_filterype = 0; + return set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); +} + #ifdef CONFIG_WPS static int atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) @@ -852,6 +958,84 @@ atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, #define atheros_set_ap_wps_ie NULL #endif /* CONFIG_WPS */ +#ifdef CONFIG_IEEE80211R +static int +atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq, + u16 status_code, const u8 *ie, size_t len) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d", + __func__, ether_sprintf(addr), status_code); + + mlme.im_op = IEEE80211_MLME_AUTH; + mlme.im_reason = status_code; + mlme.im_seq = seq; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + mlme.im_optie_len = len; + if (len) { + if (len < IEEE80211_MAX_OPT_IE) { + os_memcpy(mlme.im_optie, ie, len); + } else { + wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " + "opt_ie STA (addr " MACSTR " reason %d, " + "ie_len %d)", + __func__, MAC2STR(addr), status_code, + (int) len); + return -1; + } + } + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), status_code); + } + return ret; +} + +static int +atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr, + int reassoc, u16 status_code, const u8 *ie, size_t len) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d reassoc %d", + __func__, ether_sprintf(addr), status_code, reassoc); + + if (reassoc) + mlme.im_op = IEEE80211_MLME_REASSOC; + else + mlme.im_op = IEEE80211_MLME_ASSOC; + mlme.im_reason = status_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + mlme.im_optie_len = len; + if (len) { + if (len < IEEE80211_MAX_OPT_IE) { + os_memcpy(mlme.im_optie, ie, len); + } else { + wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " + "opt_ie STA (addr " MACSTR " reason %d, " + "ie_len %d)", + __func__, MAC2STR(addr), status_code, + (int) len); + return -1; + } + } + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to assoc STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), status_code); + } + return ret; +} +#endif /* CONFIG_IEEE80211R */ + static void atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) { @@ -980,6 +1164,9 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, * so all are enabled for WPS... ugh. */ wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL); +#endif /* CONFIG_WPS */ +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) +#define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */ } else if (strncmp(custom, "Manage.prob_req ", 16) == 0) { /* * Atheros driver uses a hack to pass Probe Request frames as a @@ -987,7 +1174,6 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, * packet sniffing) didn't work when bridging. * Format: "Manage.prob_req <frame len>" | zero padding | frame */ -#define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */ int len = atoi(custom + 16); if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " @@ -996,7 +1182,40 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, } atheros_raw_receive(drv, NULL, (u8 *) custom + WPS_FRAM_TAG_SIZE, len); -#endif /* CONFIG_WPS */ + } else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) { + /* Format: "Manage.assoc_req <frame len>" | zero padding | + * frame */ + int len = atoi(custom + 17); + if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" + "assoc_req/auth event length %d", len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + WPS_FRAM_TAG_SIZE, len); + } else if (strncmp(custom, "Manage.action ", 14) == 0) { + /* Format: "Manage.assoc_req <frame len>" | zero padding | + * frame */ + int len = atoi(custom + 14); + if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" + "assoc_req/auth event length %d", len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + WPS_FRAM_TAG_SIZE, len); + } else if (strncmp(custom, "Manage.auth ", 12) == 0) { + /* Format: "Manage.auth <frame len>" | zero padding | frame + */ + int len = atoi(custom + 12); + if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" + "assoc_req/auth event length %d", len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + WPS_FRAM_TAG_SIZE, len); +#endif /* CONFIG_WPS or CONFIG_IEEE80211R */ } } @@ -1278,7 +1497,7 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); atheros_set_privacy(drv, 0); /* default to no privacy */ - atheros_receive_probe_req(drv); + atheros_receive_pkt(drv); if (atheros_wireless_event_init(drv)) goto bad; @@ -1302,6 +1521,7 @@ atheros_deinit(void *priv) { struct atheros_driver_data *drv = priv; + atheros_reset_appfilter(drv); netlink_deinit(drv->netlink); (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); if (drv->ioctl_sock >= 0) @@ -1348,7 +1568,6 @@ atheros_get_ssid(void *priv, u8 *buf, int len) memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.pointer = (caddr_t) buf; - iwr.u.essid.length = len; iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ? IW_ESSID_MAX_SIZE : len; @@ -1421,6 +1640,75 @@ static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params) return 0; } + +#ifdef CONFIG_IEEE80211R + +static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, + int noack) +{ + struct atheros_driver_data *drv = priv; + u8 buf[1510]; + const struct ieee80211_mgmt *mgmt; + struct ieee80211req_mgmtbuf *mgmt_frm; + + mgmt = (const struct ieee80211_mgmt *) frm; + wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__, + (unsigned long) data_len, MAC2STR(mgmt->da)); + mgmt_frm = (struct ieee80211req_mgmtbuf *) buf; + memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN); + mgmt_frm->buflen = data_len; + if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) { + wpa_printf(MSG_INFO, "atheros: Too long frame for " + "atheros_send_mgmt (%u)", (unsigned int) data_len); + return -1; + } + os_memcpy(&mgmt_frm->buf[0], frm, data_len); + return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm, + sizeof(struct ieee80211req_mgmtbuf) + data_len); +} + + +static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie, + size_t tspec_ielen) +{ + struct atheros_driver_data *drv = priv; + int retv; + struct ieee80211req_res req; + struct ieee80211req_res_addts *addts = &req.u.addts; + + wpa_printf(MSG_DEBUG, "%s", __func__); + req.type = IEEE80211_RESREQ_ADDTS; + os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN); + os_memcpy(addts->tspecie, tspec_ie, tspec_ielen); + retv = set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req, + sizeof(struct ieee80211req_res)); + if (retv < 0) { + wpa_printf(MSG_DEBUG, "%s IEEE80211_IOCTL_RES_REQ FAILED " + "retv = %d", __func__, retv); + return -1; + } + os_memcpy(tspec_ie, addts->tspecie, tspec_ielen); + return addts->status; +} + + +static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_res req; + struct ieee80211req_res_addnode *addnode = &req.u.addnode; + + wpa_printf(MSG_DEBUG, "%s", __func__); + req.type = IEEE80211_RESREQ_ADDNODE; + os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN); + addnode->auth_alg = auth_alg; + return set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req, + sizeof(struct ieee80211req_res)); +} + +#endif /* CONFIG_IEEE80211R */ + + const struct wpa_driver_ops wpa_driver_atheros_ops = { .name = "atheros", .hapd_init = atheros_init, @@ -1444,4 +1732,11 @@ const struct wpa_driver_ops wpa_driver_atheros_ops = { .set_ap_wps_ie = atheros_set_ap_wps_ie, .set_authmode = atheros_set_authmode, .set_ap = atheros_set_ap, +#ifdef CONFIG_IEEE80211R + .sta_assoc = atheros_sta_assoc, + .sta_auth = atheros_sta_auth, + .send_mlme = atheros_send_mgmt, + .add_tspec = atheros_add_tspec, + .add_sta_node = atheros_add_sta_node, +#endif /* CONFIG_IEEE80211R */ }; diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index 4596a518..67abb672 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -3,14 +3,8 @@ * Copyright (c) 2004, Sam Leffler <sam@errno.com> * Copyright (c) 2004, 2Wire, Inc * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index 345e851d..81856aa9 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -77,6 +77,7 @@ const char * event_to_string(enum wpa_event_type event) E2S(SCHED_SCAN_STOPPED); E2S(DRIVER_CLIENT_POLL_OK); E2S(EAPOL_TX_STATUS); + E2S(CH_SWITCH); } return "UNKNOWN"; diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c index edb086f1..bb48011d 100644 --- a/src/drivers/driver_madwifi.c +++ b/src/drivers/driver_madwifi.c @@ -4,14 +4,8 @@ * Copyright (c) 2004, Video54 Technologies * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * While this driver wrapper supports both AP (hostapd) and station * (wpa_supplicant) operations, the station side is deprecated and diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 87951041..d54d0f5f 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -189,6 +189,7 @@ struct i802_bss { unsigned int beacon_set:1; unsigned int added_if_into_bridge:1; unsigned int added_bridge:1; + unsigned int in_deinit:1; u8 addr[ETH_ALEN]; @@ -456,6 +457,7 @@ static int send_and_recv_msgs_global(struct nl80211_global *global, valid_data); } + int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, int (*valid_handler)(struct nl_msg *, void *), @@ -797,10 +799,28 @@ static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, del ? "removed" : "added"); if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) { - if (del) + if (del) { + if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "nl80211: if_removed " + "already set - ignore event"); + return; + } drv->if_removed = 1; - else + } else { + if (if_nametoindex(drv->first_bss.ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Interface %s " + "does not exist - ignore " + "RTM_NEWLINK", + drv->first_bss.ifname); + return; + } + if (!drv->if_removed) { + wpa_printf(MSG_DEBUG, "nl80211: if_removed " + "already cleared - ignore event"); + return; + } drv->if_removed = 0; + } } wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); @@ -917,6 +937,14 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s is down", namebuf); + } else if (if_nametoindex(drv->first_bss.ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " + "event since interface %s does not exist", + drv->first_bss.ifname); + } else if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " + "event since interface %s is marked " + "removed", drv->first_bss.ifname); } else { wpa_printf(MSG_DEBUG, "nl80211: Interface up"); drv->if_disabled = 0; @@ -1103,6 +1131,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, } event.assoc_info.freq = drv->assoc_freq; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } @@ -1176,9 +1205,43 @@ static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, drv->associated = 0; os_memset(&data, 0, sizeof(data)); if (reason) - data.disassoc_info.reason_code = nla_get_u16(reason); - data.disassoc_info.locally_generated = by_ap == NULL; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data); + data.deauth_info.reason_code = nla_get_u16(reason); + data.deauth_info.locally_generated = by_ap == NULL; + wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data); +} + + +static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, + struct nlattr *freq, struct nlattr *type) +{ + union wpa_event_data data; + int ht_enabled = 1; + int chan_offset = 0; + + wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); + + if (!freq || !type) + return; + + switch (nla_get_u32(type)) { + case NL80211_CHAN_NO_HT: + ht_enabled = 0; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + chan_offset = 1; + break; + case NL80211_CHAN_HT40MINUS: + chan_offset = -1; + break; + } + + data.ch_switch.freq = nla_get_u32(freq); + data.ch_switch.ht_enabled = ht_enabled; + data.ch_switch.ch_offset = chan_offset; + + wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data); } @@ -1208,11 +1271,13 @@ static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv, - struct nlattr *freq, const u8 *frame, size_t len) + struct nlattr *freq, struct nlattr *sig, + const u8 *frame, size_t len) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; u16 fc, stype; + int ssi_signal = 0; mgmt = (const struct ieee80211_mgmt *) frame; if (len < 24) { @@ -1223,6 +1288,9 @@ static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv, fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); + if (sig) + ssi_signal = (s32) nla_get_u32(sig); + os_memset(&event, 0, sizeof(event)); if (freq) { event.rx_action.freq = nla_get_u32(freq); @@ -1239,6 +1307,7 @@ static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv, } else { event.rx_mgmt.frame = frame; event.rx_mgmt.frame_len = len; + event.rx_mgmt.ssi_signal = ssi_signal; wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); } } @@ -1379,7 +1448,7 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *frame, struct nlattr *addr, struct nlattr *timed_out, struct nlattr *freq, struct nlattr *ack, - struct nlattr *cookie) + struct nlattr *cookie, struct nlattr *sig) { if (timed_out && addr) { mlme_timeout_event(drv, cmd, addr); @@ -1412,7 +1481,8 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv, nla_data(frame), nla_len(frame)); break; case NL80211_CMD_FRAME: - mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame)); + mlme_event_mgmt(drv, freq, sig, nla_data(frame), + nla_len(frame)); break; case NL80211_CMD_FRAME_TX_STATUS: mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame), @@ -2084,7 +2154,8 @@ static void do_process_drv_event(struct wpa_driver_nl80211_data *drv, mlme_event(drv, cmd, tb[NL80211_ATTR_FRAME], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], - tb[NL80211_ATTR_COOKIE]); + tb[NL80211_ATTR_COOKIE], + tb[NL80211_ATTR_RX_SIGNAL_DBM]); break; case NL80211_CMD_CONNECT: case NL80211_CMD_ROAM: @@ -2094,6 +2165,10 @@ static void do_process_drv_event(struct wpa_driver_nl80211_data *drv, tb[NL80211_ATTR_REQ_IE], tb[NL80211_ATTR_RESP_IE]); break; + case NL80211_CMD_CH_SWITCH_NOTIFY: + mlme_event_ch_switch(drv, tb[NL80211_ATTR_WIPHY_FREQ], + tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + break; case NL80211_CMD_DISCONNECT: mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], tb[NL80211_ATTR_MAC], @@ -2176,7 +2251,7 @@ static int process_global_event(struct nl_msg *msg, void *arg) struct nl80211_global *global = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct wpa_driver_nl80211_data *drv; + struct wpa_driver_nl80211_data *drv, *tmp; int ifidx = -1; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -2185,15 +2260,11 @@ static int process_global_event(struct nl_msg *msg, void *arg) if (tb[NL80211_ATTR_IFINDEX]) ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - dl_list_for_each(drv, &global->interfaces, - struct wpa_driver_nl80211_data, list) { + dl_list_for_each_safe(drv, tmp, &global->interfaces, + struct wpa_driver_nl80211_data, list) { if (ifidx == -1 || ifidx == drv->ifindex || - have_ifidx(drv, ifidx)) { + have_ifidx(drv, ifidx)) do_process_drv_event(drv, gnlh->cmd, tb); -#ifdef ANDROID_P2P - break; -#endif - } } return NL_SKIP; @@ -2215,7 +2286,8 @@ static int process_bss_event(struct nl_msg *msg, void *arg) mlme_event(bss->drv, gnlh->cmd, tb[NL80211_ATTR_FRAME], tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], - tb[NL80211_ATTR_COOKIE]); + tb[NL80211_ATTR_COOKIE], + tb[NL80211_ATTR_RX_SIGNAL_DBM]); break; case NL80211_CMD_UNEXPECTED_FRAME: nl80211_spurious_frame(bss, tb, 0); @@ -2504,6 +2576,9 @@ broken_combination: if (flags & NL80211_FEATURE_SK_TX_STATUS) info->data_tx_status = 1; + + if (flags & NL80211_FEATURE_INACTIVITY_TIMER) + capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER; } if (tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]) { @@ -2571,17 +2646,29 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES; drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; - drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; +#ifndef ANDROID_P2P + if (!info.device_ap_sme) +#endif + drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; drv->device_ap_sme = info.device_ap_sme; drv->poll_command_supported = info.poll_command_supported; drv->data_tx_status = info.data_tx_status; +#ifdef ANDROID_P2P + if(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) { + /* Driver is new enough to support monitorless mode*/ + wpa_printf(MSG_DEBUG, "nl80211: Driver is new " + "enough to support monitor-less mode"); + drv->use_monitor = 0; + } +#else /* * If poll command is supported mac80211 is new enough to * have everything we need to not need monitor interfaces. */ drv->use_monitor = !info.poll_command_supported; +#endif if (drv->device_ap_sme && drv->use_monitor) { /* @@ -2803,10 +2890,7 @@ static void wpa_driver_nl80211_handle_eapol_tx_status(int sock, u8 data[2048]; struct msghdr msg; struct iovec entry; - struct { - struct cmsghdr cm; - char control[512]; - } control; + u8 control[512]; struct cmsghdr *cmsg; int res, found_ee = 0, found_wifi = 0, acked = 0; union wpa_event_data event; @@ -3309,6 +3393,7 @@ static void wpa_driver_nl80211_deinit(void *priv) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; + bss->in_deinit = 1; if (drv->data_tx_status) eloop_unregister_read_sock(drv->eapol_tx_sock); if (drv->eapol_tx_sock >= 0) @@ -3467,6 +3552,8 @@ static int wpa_driver_nl80211_scan(void *priv, } if (params->p2p_probe) { + wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); + /* * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates * by masking out everything else apart from the OFDM rates 6, @@ -4731,6 +4818,18 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) os_memcpy(mode->mcs_set, mcs, 16); } + if (tb_band[NL80211_BAND_ATTR_VHT_CAPA]) { + mode->vht_capab = nla_get_u32( + tb_band[NL80211_BAND_ATTR_VHT_CAPA]); + } + + if (tb_band[NL80211_BAND_ATTR_VHT_MCS_SET] && + nla_len(tb_band[NL80211_BAND_ATTR_VHT_MCS_SET])) { + u8 *mcs; + mcs = nla_data(tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]); + os_memcpy(mode->vht_mcs_set, mcs, 8); + } + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), nla_len(nl_freq), freq_policy); @@ -5191,7 +5290,9 @@ static int wpa_driver_nl80211_send_mlme_freq(struct i802_bss *bss, if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) { if (freq == 0) freq = bss->freq; - return nl80211_send_frame_cmd(bss, freq, 0, + return nl80211_send_frame_cmd(bss, freq, + (int) freq == bss->freq ? 0 : + wait_time, data, data_len, &drv->send_action_cookie, no_cck, noack, offchanok); @@ -5403,6 +5504,11 @@ static int wpa_driver_nl80211_set_ap(void *priv, wpabuf_head(params->assocresp_ies)); } + if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER) { + NLA_PUT_U16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT, + params->ap_max_inactivity); + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", @@ -5413,7 +5519,6 @@ static int wpa_driver_nl80211_set_ap(void *priv, params->short_slot_time, params->ht_opmode, params->isolate, params->basic_rates); } - return ret; nla_put_failure: nlmsg_free(msg); @@ -5851,8 +5956,8 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) case IEEE80211_RADIOTAP_RATE: datarate = *iter.this_arg * 5; break; - case IEEE80211_RADIOTAP_DB_ANTSIGNAL: - ssi_signal = *iter.this_arg; + case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: + ssi_signal = (s8) *iter.this_arg; break; } } @@ -6149,7 +6254,6 @@ static int nl80211_setup_ap(struct i802_bss *bss) if (nl80211_mgmt_subscribe_ap(bss)) return -1; -#ifndef ANDROID_P2P if (drv->device_ap_sme && !drv->use_monitor) if (nl80211_mgmt_subscribe_ap_dev_sme(bss)) return -1; @@ -6157,15 +6261,17 @@ static int nl80211_setup_ap(struct i802_bss *bss) if (!drv->device_ap_sme && drv->use_monitor && nl80211_create_monitor_interface(drv) && !drv->device_ap_sme) -#else - if (drv->device_ap_sme) + return -1; + +#ifdef ANDROID_P2P + if (drv->device_ap_sme && drv->use_monitor) if (nl80211_mgmt_subscribe_ap_dev_sme(bss)) return -1; if (drv->use_monitor && nl80211_create_monitor_interface(drv)) -#endif return -1; +#endif if (drv->device_ap_sme && wpa_driver_nl80211_probe_req_report(bss, 1) < 0) { @@ -6568,6 +6674,12 @@ static int wpa_driver_nl80211_connect( wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); } + if (params->bg_scan_period >= 0) { + wpa_printf(MSG_DEBUG, " * bg scan period=%d", + params->bg_scan_period); + NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD, + params->bg_scan_period); + } if (params->ssid) { wpa_hexdump_ascii(MSG_DEBUG, " * SSID", params->ssid, params->ssid_len); @@ -6765,6 +6877,12 @@ static int wpa_driver_nl80211_associate( drv->assoc_freq = params->freq; } else drv->assoc_freq = 0; + if (params->bg_scan_period >= 0) { + wpa_printf(MSG_DEBUG, " * bg scan period=%d", + params->bg_scan_period); + NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD, + params->bg_scan_period); + } if (params->ssid) { wpa_hexdump_ascii(MSG_DEBUG, " * SSID", params->ssid, params->ssid_len); @@ -6980,7 +7098,7 @@ done: nl80211_mgmt_unsubscribe(bss, "mode change"); } - if (!is_ap_interface(nlmode) && + if (!bss->in_deinit && !is_ap_interface(nlmode) && nl80211_mgmt_subscribe_non_ap(bss) < 0) wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " "frame processing - ignore for now"); @@ -7402,8 +7520,12 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason) { struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt mgmt; + if (drv->device_ap_sme) + return wpa_driver_nl80211_sta_remove(bss, addr); + memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH); @@ -7421,8 +7543,12 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, int reason) { struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt mgmt; + if (drv->device_ap_sme) + return wpa_driver_nl80211_sta_remove(bss, addr); + memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DISASSOC); @@ -7967,6 +8093,9 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, if (!msg) return -1; + wpa_printf(MSG_DEBUG, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d " + "no_ack=%d offchanok=%d", + freq, wait, no_cck, no_ack, offchanok); nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); @@ -8269,6 +8398,16 @@ static int wpa_driver_nl80211_deinit_ap(void *priv) } +static int wpa_driver_nl80211_deinit_p2p_cli(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT) + return -1; + return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); +} + + static void wpa_driver_nl80211_resume(void *priv) { struct i802_bss *bss = priv; @@ -8407,11 +8546,9 @@ static int wpa_driver_nl80211_shared_freq(void *priv) MACSTR, driver->phyname, driver->first_bss.ifname, MAC2STR(driver->first_bss.addr)); -#ifdef ANDROID_P2P - if(is_ap_interface(driver->nlmode)) + if (is_ap_interface(driver->nlmode)) freq = driver->first_bss.freq; else -#endif freq = nl80211_get_assoc_freq(driver); wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d", drv->phyname, freq); @@ -9016,6 +9153,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { wpa_driver_nl80211_cancel_remain_on_channel, .probe_req_report = wpa_driver_nl80211_probe_req_report, .deinit_ap = wpa_driver_nl80211_deinit_ap, + .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli, .resume = wpa_driver_nl80211_resume, .send_ft_action = nl80211_send_ft_action, .signal_monitor = nl80211_signal_monitor, diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c index 81a328a9..c8916f06 100644 --- a/src/drivers/driver_privsep.c +++ b/src/drivers/driver_privsep.c @@ -651,7 +651,7 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param) os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("privsep-set-params priv-sock: bind(PF_UNIX)"); close(drv->priv_socket); drv->priv_socket = -1; unlink(drv->own_socket_path); @@ -676,7 +676,7 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param) os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path)); if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("privsep-set-params cmd-sock: bind(PF_UNIX)"); close(drv->cmd_socket); drv->cmd_socket = -1; unlink(drv->own_cmd_path); diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c index 61b75b13..0a9078a4 100644 --- a/src/drivers/driver_roboswitch.c +++ b/src/drivers/driver_roboswitch.c @@ -2,14 +2,8 @@ * WPA Supplicant - roboswitch driver interface * Copyright (c) 2008-2009 Jouke Witteveen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c index 43b30e99..f011651f 100644 --- a/src/drivers/driver_test.c +++ b/src/drivers/driver_test.c @@ -1275,7 +1275,7 @@ static void * test_driver_init(struct hostapd_data *hapd, alen = sizeof(addr_un); } if (bind(drv->test_socket, addr, alen) < 0) { - perror("bind(PF_UNIX)"); + perror("test-driver-init: bind(PF_UNIX)"); close(drv->test_socket); if (drv->own_socket_path) unlink(drv->own_socket_path); @@ -2252,7 +2252,7 @@ static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); if (bind(drv->test_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("test-driver-attach: bind(PF_UNIX)"); close(drv->test_socket); unlink(drv->own_socket_path); os_free(drv->own_socket_path); @@ -2867,7 +2867,8 @@ static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr, if (!drv->p2p) return -1; return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent, - own_interface_addr, force_freq, persistent_group); + own_interface_addr, force_freq, persistent_group, + NULL, 0, 0); } @@ -2912,7 +2913,7 @@ static int wpa_driver_test_p2p_set_params(void *priv, static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, unsigned int num_req_dev_types, - const u8 *req_dev_types, const u8 *dev_id) + const u8 *req_dev_types, const u8 *dev_id, u16 pw_id) { struct wpa_driver_test_data *drv = ctx; struct wpa_driver_scan_params params; @@ -2933,8 +2934,8 @@ static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, #if 0 /* TODO: WPS IE */ wpa_s->wps->dev.p2p = 1; - wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid, - WPS_REQ_ENROLLEE); + wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev, + wpa_s->wps->uuid, WPS_REQ_ENROLLEE); #else wps_ie = wpabuf_alloc(1); #endif @@ -2961,11 +2962,6 @@ static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, break; case P2P_SCAN_FULL: break; - case P2P_SCAN_SPECIFIC: - social_channels[0] = freq; - social_channels[1] = 0; - params.freqs = social_channels; - break; case P2P_SCAN_SOCIAL_PLUS_ONE: social_channels[3] = freq; params.freqs = social_channels; diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index 204de34a..fd904388 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -584,10 +584,28 @@ static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv, del ? "removed" : "added"); if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { - if (del) + if (del) { + if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "WEXT: if_removed " + "already set - ignore event"); + return; + } drv->if_removed = 1; - else + } else { + if (if_nametoindex(drv->ifname) == 0) { + wpa_printf(MSG_DEBUG, "WEXT: Interface %s " + "does not exist - ignore " + "RTM_NEWLINK", + drv->ifname); + return; + } + if (!drv->if_removed) { + wpa_printf(MSG_DEBUG, "WEXT: if_removed " + "already cleared - ignore event"); + return; + } drv->if_removed = 0; + } } wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); @@ -643,6 +661,7 @@ static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, struct wpa_driver_wext_data *drv = ctx; int attrlen, rta_len; struct rtattr *attr; + char namebuf[IFNAMSIZ]; if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) { wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", @@ -665,9 +684,25 @@ static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, } if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { - wpa_printf(MSG_DEBUG, "WEXT: Interface up"); - drv->if_disabled = 0; - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL); + if (if_indextoname(ifi->ifi_index, namebuf) && + linux_iface_up(drv->ioctl_sock, drv->ifname) == 0) { + wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up " + "event since interface %s is down", + namebuf); + } else if (if_nametoindex(drv->ifname) == 0) { + wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up " + "event since interface %s does not exist", + drv->ifname); + } else if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up " + "event since interface %s is marked " + "removed", drv->ifname); + } else { + wpa_printf(MSG_DEBUG, "WEXT: Interface up"); + drv->if_disabled = 0; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, + NULL); + } } /* @@ -1566,6 +1601,7 @@ static int wpa_driver_wext_get_range(void *priv) } drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104; + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP128; if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c index 618db264..e0f0f228 100644 --- a/src/drivers/driver_wired.c +++ b/src/drivers/driver_wired.c @@ -3,14 +3,8 @@ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> * Copyright (c) 2004, Gunter Burchardt <tira@isx.de> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 4f98fae1..74cc55c1 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -156,21 +156,25 @@ * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX * or %NL80211_ATTR_MAC. * - * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a - * %NL80222_CMD_NEW_BEACON message) - * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface - * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD, - * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes. - * Following attributes are provided for drivers that generate full Beacon - * and Probe Response frames internally: %NL80211_ATTR_SSID, + * @NL80211_CMD_GET_BEACON: (not used) + * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface + * using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL + * attributes. For drivers that generate the beacon and probe responses + * internally, the following attributes must be provided: %NL80211_ATTR_IE, + * %NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP. + * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters + * are like for %NL80211_CMD_SET_BEACON, and additionally parameters that + * do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL, + * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID, * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, - * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_IE, %NL80211_ATTR_IE_PROBE_RESP, - * %NL80211_ATTR_IE_ASSOC_RESP. - * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface, - * parameters are like for %NL80211_CMD_SET_BEACON. - * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it + * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. + * The channel to use can be set on the interface or be given using the + * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_CHANNEL_TYPE attrs. + * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP + * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface + * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP * * @NL80211_CMD_GET_STATION: Get station attributes for station identified by * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. @@ -273,6 +277,12 @@ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) * + * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC + * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC + * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. + * * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain * has been changed and provides details of the request information * that caused the change such as who initiated the regulatory request @@ -367,6 +377,11 @@ * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. + * Background scan period can optionally be + * specified in %NL80211_ATTR_BG_SCAN_PERIOD, + * if not specified default background scan configuration + * in driver is used and if period value is 0, bg scan will be disabled. + * This attribute is ignored if driver does not support roam scan. * It is also sent as an event, with the BSSID and response IEs when the * connection is established or failed to be established. This can be * determined by the STATUS_CODE attribute. @@ -447,6 +462,10 @@ * the frame. * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for * backward compatibility. + * + * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE + * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE + * * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command * is used to configure connection quality monitoring notification trigger * levels. @@ -541,6 +560,11 @@ * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether * No Acknowledgement Policy should be applied. * + * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels + * independently of the userspace SME, send this event indicating + * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -565,8 +589,10 @@ enum nl80211_commands { NL80211_CMD_GET_BEACON, NL80211_CMD_SET_BEACON, - NL80211_CMD_NEW_BEACON, - NL80211_CMD_DEL_BEACON, + NL80211_CMD_START_AP, + NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP, + NL80211_CMD_STOP_AP, + NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP, NL80211_CMD_GET_STATION, NL80211_CMD_SET_STATION, @@ -680,6 +706,8 @@ enum nl80211_commands { NL80211_CMD_SET_NOACK_MAP, + NL80211_CMD_CH_SWITCH_NOTIFY, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -753,6 +781,13 @@ enum nl80211_commands { * section 7.3.2.25.1, e.g. 0x000FAC04) * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and * CCMP keys, each six bytes in little endian + * @NL80211_ATTR_KEY_DEFAULT: Flag attribute indicating the key is default key + * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the + * default management key + * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or + * other commands, indicates which pairwise cipher suites are used + * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or + * other commands, indicates which group cipher suite is used * * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing @@ -988,6 +1023,8 @@ enum nl80211_commands { * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * acknowledged by the recipient. * + * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values. + * * @NL80211_ATTR_CQM: connection quality monitor configuration in a * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. * @@ -1045,7 +1082,7 @@ enum nl80211_commands { * flag isn't set, the frame will be rejected. This is also used as an * nl80211 capability flag. * - * @NL80211_ATTR_BSS_HTOPMODE: HT operation mode (u16) + * @NL80211_ATTR_BSS_HT_OPMODE: HT operation mode (u16) * * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags * attributes, specifying what a key should be set as default as. @@ -1069,10 +1106,10 @@ enum nl80211_commands { * indicate which WoW triggers should be enabled. This is also * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN * triggers. - + * * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan * cycles, in msecs. - + * * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more * sets of attributes to match during scheduled scans. Only BSSs * that match any of the sets will be reported. These are @@ -1099,7 +1136,7 @@ enum nl80211_commands { * are managed in software: interfaces of these types aren't subject to * any restrictions in their number or combinations. * - * @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information + * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data. * * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan, @@ -1166,7 +1203,6 @@ enum nl80211_commands { * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from * &enum nl80211_feature_flags and is advertised in wiphy information. * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe - * * requests while operating in AP-mode. * This attribute holds a bitmap of the supported protocols for * offloading (see &enum nl80211_probe_resp_offload_support_attr). @@ -1193,6 +1229,19 @@ enum nl80211_commands { * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of * up to 16 TIDs. * + * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be + * used by the drivers which has MLME in firmware and does not have support + * to report per station tx/rx activity to free up the staion entry from + * the list. This needs to be used when the driver advertises the + * capability to timeout the stations. + * + * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int); + * this attribute is (depending on the driver capabilities) added to + * received frames indicated with %NL80211_CMD_FRAME. + * + * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds + * or 0 to disable background scan. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1438,6 +1487,12 @@ enum nl80211_attrs { NL80211_ATTR_NOACK_MAP, + NL80211_ATTR_INACTIVITY_TIMEOUT, + + NL80211_ATTR_RX_SIGNAL_DBM, + + NL80211_ATTR_BG_SCAN_PERIOD, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1475,6 +1530,7 @@ enum nl80211_attrs { #define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS #define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_HT_RATES 77 #define NL80211_MAX_SUPP_REG_RULES 32 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 @@ -1484,6 +1540,11 @@ enum nl80211_attrs { #define NL80211_MAX_NR_CIPHER_SUITES 5 #define NL80211_MAX_NR_AKM_SUITES 2 +#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10 + +/* default RSSI threshold for scan results if none specified. */ +#define NL80211_SCAN_RSSI_THOLD_OFF -300 + /** * enum nl80211_iftype - (virtual) interface types * @@ -1558,6 +1619,8 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 }; +#define NL80211_STA_FLAG_MAX_OLD_API NL80211_STA_FLAG_TDLS_PEER + /** * struct nl80211_sta_flag_update - station flags mask/set * @mask: mask of station flags to set @@ -1656,6 +1719,7 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update. * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32) + * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1679,6 +1743,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_CONNECTED_TIME, NL80211_STA_INFO_STA_FLAGS, NL80211_STA_INFO_BEACON_LOSS, + NL80211_STA_INFO_T_OFFSET, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -1748,6 +1813,9 @@ enum nl80211_mpath_info { * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n + * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as + * defined in 802.11ac + * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined * @__NL80211_BAND_ATTR_AFTER_LAST: internal use */ @@ -1761,6 +1829,9 @@ enum nl80211_band_attr { NL80211_BAND_ATTR_HT_AMPDU_FACTOR, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + NL80211_BAND_ATTR_VHT_MCS_SET, + NL80211_BAND_ATTR_VHT_CAPA, + /* keep last */ __NL80211_BAND_ATTR_AFTER_LAST, NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 @@ -1912,6 +1983,8 @@ enum nl80211_reg_rule_attr { * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, * only report BSS with matching SSID. + * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a + * BSS in scan results. Filtering is turned off if not specified. * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use @@ -1919,7 +1992,8 @@ enum nl80211_reg_rule_attr { enum nl80211_sched_scan_match_attr { __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID, - NL80211_ATTR_SCHED_SCAN_MATCH_SSID, + NL80211_SCHED_SCAN_MATCH_ATTR_SSID, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, /* keep last */ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, @@ -1927,6 +2001,9 @@ enum nl80211_sched_scan_match_attr { __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1 }; +/* only for backward compatibility */ +#define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID + /** * enum nl80211_reg_rule_flags - regulatory rule flags * @@ -1956,9 +2033,9 @@ enum nl80211_reg_rule_flags { * enum nl80211_dfs_regions - regulatory DFS regions * * @NL80211_DFS_UNSET: Country has no DFS master region specified - * @NL80211_DFS_FCC_: Country follows DFS master rules from FCC - * @NL80211_DFS_FCC_: Country follows DFS master rules from ETSI - * @NL80211_DFS_JP_: Country follows DFS master rules from JP/MKK/Telec + * @NL80211_DFS_FCC: Country follows DFS master rules from FCC + * @NL80211_DFS_ETSI: Country follows DFS master rules from ETSI + * @NL80211_DFS_JP: Country follows DFS master rules from JP/MKK/Telec */ enum nl80211_dfs_regions { NL80211_DFS_UNSET = 0, @@ -2046,69 +2123,91 @@ enum nl80211_mntr_flags { * @__NL80211_MESHCONF_INVALID: internal use * * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in - * millisecond units, used by the Peer Link Open message + * millisecond units, used by the Peer Link Open message * * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in - * millisecond units, used by the peer link management to close a peer link + * millisecond units, used by the peer link management to close a peer link * * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in - * millisecond units + * millisecond units * * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed - * on this mesh interface + * on this mesh interface * * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link - * open retries that can be sent to establish a new peer link instance in a - * mesh + * open retries that can be sent to establish a new peer link instance in a + * mesh * * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh - * point. + * point. * * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically - * open peer links when we detect compatible mesh peers. + * open peer links when we detect compatible mesh peers. * * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames - * containing a PREQ that an MP can send to a particular destination (path - * target) + * containing a PREQ that an MP can send to a particular destination (path + * target) * * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths - * (in milliseconds) + * (in milliseconds) * * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait - * until giving up on a path discovery (in milliseconds) + * until giving up on a path discovery (in milliseconds) * * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh - * points receiving a PREQ shall consider the forwarding information from the - * root to be valid. (TU = time unit) + * points receiving a PREQ shall consider the forwarding information from + * the root to be valid. (TU = time unit) * * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in - * TUs) during which an MP can send only one action frame containing a PREQ - * reference element + * TUs) during which an MP can send only one action frame containing a PREQ + * reference element * * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) - * that it takes for an HWMP information element to propagate across the mesh + * that it takes for an HWMP information element to propagate across the + * mesh * * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not * * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a - * source mesh point for path selection elements. + * source mesh point for path selection elements. * * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between - * root announcements are transmitted. + * root announcements are transmitted. * * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has - * access to a broader network beyond the MBSS. This is done via Root - * Announcement frames. + * access to a broader network beyond the MBSS. This is done via Root + * Announcement frames. * * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in - * TUs) during which a mesh STA can send only one Action frame containing a - * PERR element. + * TUs) during which a mesh STA can send only one Action frame containing a + * PERR element. * * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding - * or forwarding entity (default is TRUE - forwarding entity) + * or forwarding entity (default is TRUE - forwarding entity) + * + * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the + * threshold for average signal strength of candidate station to establish + * a peer link. + * + * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors + * to synchronize to for 11s default synchronization method + * (see 11C.12.2.2) + * + * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode. * * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute * + * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for + * which mesh STAs receiving a proactive PREQ shall consider the forwarding + * information to the root mesh STA to be valid. + * + * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between + * proactive PREQs are transmitted. + * + * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time + * (in TUs) during which a mesh STA can send only one Action frame + * containing a PREQ element for root path confirmation. + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -2132,6 +2231,12 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_GATE_ANNOUNCEMENTS, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, NL80211_MESHCONF_FORWARDING, + NL80211_MESHCONF_RSSI_THRESHOLD, + NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, + NL80211_MESHCONF_HT_OPMODE, + NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, + NL80211_MESHCONF_HWMP_ROOT_INTERVAL, + NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, @@ -2147,30 +2252,37 @@ enum nl80211_meshconf_params { * @__NL80211_MESH_SETUP_INVALID: Internal use * * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a - * vendor specific path selection algorithm or disable it to use the default - * HWMP. + * vendor specific path selection algorithm or disable it to use the + * default HWMP. * * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a - * vendor specific path metric or disable it to use the default Airtime - * metric. + * vendor specific path metric or disable it to use the default Airtime + * metric. * * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a - * robust security network ie, or a vendor specific information element that - * vendors will use to identify the path selection methods and metrics in use. + * robust security network ie, or a vendor specific information element + * that vendors will use to identify the path selection methods and + * metrics in use. * * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication - * daemon will be authenticating mesh candidates. + * daemon will be authenticating mesh candidates. * * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication - * daemon will be securing peer link frames. AMPE is a secured version of Mesh - * Peering Management (MPM) and is implemented with the assistance of a - * userspace daemon. When this flag is set, the kernel will send peer - * management frames to a userspace daemon that will implement AMPE - * functionality (security capabilities selection, key confirmation, and key - * management). When the flag is unset (default), the kernel can autonomously - * complete (unsecured) mesh peering without the need of a userspace daemon. + * daemon will be securing peer link frames. AMPE is a secured version of + * Mesh Peering Management (MPM) and is implemented with the assistance of + * a userspace daemon. When this flag is set, the kernel will send peer + * management frames to a userspace daemon that will implement AMPE + * functionality (security capabilities selection, key confirmation, and + * key management). When the flag is unset (default), the kernel can + * autonomously complete (unsecured) mesh peering without the need of a + * userspace daemon. + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a + * vendor specific synchronization method or disable it to use the default + * neighbor offset synchronization * * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number + * * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use */ enum nl80211_mesh_setup_params { @@ -2180,6 +2292,7 @@ enum nl80211_mesh_setup_params { NL80211_MESH_SETUP_IE, NL80211_MESH_SETUP_USERSPACE_AUTH, NL80211_MESH_SETUP_USERSPACE_AMPE, + NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, /* keep last */ __NL80211_MESH_SETUP_ATTR_AFTER_LAST, @@ -2189,7 +2302,7 @@ enum nl80211_mesh_setup_params { /** * enum nl80211_txq_attr - TX queue parameter attributes * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved - * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*) + * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*) * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning * disabled * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form @@ -2202,7 +2315,7 @@ enum nl80211_mesh_setup_params { */ enum nl80211_txq_attr { __NL80211_TXQ_ATTR_INVALID, - NL80211_TXQ_ATTR_QUEUE, + NL80211_TXQ_ATTR_AC, NL80211_TXQ_ATTR_TXOP, NL80211_TXQ_ATTR_CWMIN, NL80211_TXQ_ATTR_CWMAX, @@ -2213,13 +2326,21 @@ enum nl80211_txq_attr { NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 }; -enum nl80211_txq_q { - NL80211_TXQ_Q_VO, - NL80211_TXQ_Q_VI, - NL80211_TXQ_Q_BE, - NL80211_TXQ_Q_BK +enum nl80211_ac { + NL80211_AC_VO, + NL80211_AC_VI, + NL80211_AC_BE, + NL80211_AC_BK, + NL80211_NUM_ACS }; +/* backward compat */ +#define NL80211_TXQ_ATTR_QUEUE NL80211_TXQ_ATTR_AC +#define NL80211_TXQ_Q_VO NL80211_AC_VO +#define NL80211_TXQ_Q_VI NL80211_AC_VI +#define NL80211_TXQ_Q_BE NL80211_AC_BE +#define NL80211_TXQ_Q_BK NL80211_AC_BK + enum nl80211_channel_type { NL80211_CHAN_NO_HT, NL80211_CHAN_HT20, @@ -2405,12 +2526,15 @@ enum nl80211_key_attributes { * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with * 1 = 500 kbps) but without the IE length restriction (at most * %NL80211_MAX_SUPP_RATES in a single array). + * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection + * in an array of MCS numbers. * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ enum nl80211_tx_rate_attributes { __NL80211_TXRATE_INVALID, NL80211_TXRATE_LEGACY, + NL80211_TXRATE_MCS, /* keep last */ __NL80211_TXRATE_AFTER_LAST, @@ -2421,12 +2545,19 @@ enum nl80211_tx_rate_attributes { * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) + * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) */ enum nl80211_band { NL80211_BAND_2GHZ, NL80211_BAND_5GHZ, + NL80211_BAND_60GHZ, }; +/** + * enum nl80211_ps_state - powersave state + * @NL80211_PS_DISABLED: powersave is disabled + * @NL80211_PS_ENABLED: powersave is enabled + */ enum nl80211_ps_state { NL80211_PS_DISABLED, NL80211_PS_ENABLED, @@ -2465,10 +2596,14 @@ enum nl80211_attr_cqm { * configured threshold * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the * configured threshold + * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss. + * (Note that deauth/disassoc will still follow if the AP is not + * available. This event might get used as roaming event, etc.) */ enum nl80211_cqm_rssi_threshold_event { NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + NL80211_CQM_RSSI_BEACON_LOSS_EVENT, }; @@ -2796,10 +2931,13 @@ enum nl80211_ap_sme_features { * TX status to the socket error queue when requested with the * socket option. * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. + * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up + * the connected inactive stations in AP mode. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, NL80211_FEATURE_HT_IBSS = 1 << 1, + NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, }; /** diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h index af0a29a2..360193a9 100644 --- a/src/eap_common/eap_defs.h +++ b/src/eap_common/eap_defs.h @@ -60,7 +60,7 @@ typedef enum { EAP_TYPE_PSK = 47 /* RFC 4764 */, EAP_TYPE_SAKE = 48 /* RFC 4763 */, EAP_TYPE_IKEV2 = 49 /* RFC 5106 */, - EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */, + EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */, EAP_TYPE_GPSK = 51 /* RFC 5433 */, EAP_TYPE_PWD = 52 /* RFC 5931 */, EAP_TYPE_EXPANDED = 254 /* RFC 3748 */ diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c index 5b6bf3cf..7d6e6b88 100644 --- a/src/eap_common/eap_pwd_common.c +++ b/src/eap_common/eap_pwd_common.c @@ -8,70 +8,76 @@ #include "includes.h" #include "common.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" #include "eap_defs.h" #include "eap_pwd_common.h" /* The random function H(x) = HMAC-SHA256(0^32, x) */ -void H_Init(HMAC_CTX *ctx) +struct crypto_hash * eap_pwd_h_init(void) { - u8 allzero[SHA256_DIGEST_LENGTH]; - - os_memset(allzero, 0, SHA256_DIGEST_LENGTH); - HMAC_Init(ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256()); + u8 allzero[SHA256_MAC_LEN]; + os_memset(allzero, 0, SHA256_MAC_LEN); + return crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, allzero, + SHA256_MAC_LEN); } -void H_Update(HMAC_CTX *ctx, const u8 *data, int len) +void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len) { - HMAC_Update(ctx, data, len); + crypto_hash_update(hash, data, len); } -void H_Final(HMAC_CTX *ctx, u8 *digest) +void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest) { - unsigned int mdlen = SHA256_DIGEST_LENGTH; - - HMAC_Final(ctx, digest, &mdlen); - HMAC_CTX_cleanup(ctx); + size_t len = SHA256_MAC_LEN; + crypto_hash_finish(hash, digest, &len); } /* a counter-based KDF based on NIST SP800-108 */ -void eap_pwd_kdf(u8 *key, int keylen, u8 *label, int labellen, - u8 *result, int resultbitlen) +static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, + size_t labellen, u8 *result, size_t resultbitlen) { - HMAC_CTX hctx; - unsigned char digest[SHA256_DIGEST_LENGTH]; + struct crypto_hash *hash; + u8 digest[SHA256_MAC_LEN]; u16 i, ctr, L; - int resultbytelen, len = 0; - unsigned int mdlen = SHA256_DIGEST_LENGTH; - unsigned char mask = 0xff; + size_t resultbytelen, len = 0, mdlen; - resultbytelen = (resultbitlen + 7)/8; + resultbytelen = (resultbitlen + 7) / 8; ctr = 0; L = htons(resultbitlen); while (len < resultbytelen) { - ctr++; i = htons(ctr); - HMAC_Init(&hctx, key, keylen, EVP_sha256()); + ctr++; + i = htons(ctr); + hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, + key, keylen); + if (hash == NULL) + return -1; if (ctr > 1) - HMAC_Update(&hctx, digest, mdlen); - HMAC_Update(&hctx, (u8 *) &i, sizeof(u16)); - HMAC_Update(&hctx, label, labellen); - HMAC_Update(&hctx, (u8 *) &L, sizeof(u16)); - HMAC_Final(&hctx, digest, &mdlen); - if ((len + (int) mdlen) > resultbytelen) + crypto_hash_update(hash, digest, SHA256_MAC_LEN); + crypto_hash_update(hash, (u8 *) &i, sizeof(u16)); + crypto_hash_update(hash, label, labellen); + crypto_hash_update(hash, (u8 *) &L, sizeof(u16)); + mdlen = SHA256_MAC_LEN; + if (crypto_hash_finish(hash, digest, &mdlen) < 0) + return -1; + if ((len + mdlen) > resultbytelen) os_memcpy(result + len, digest, resultbytelen - len); else os_memcpy(result + len, digest, mdlen); len += mdlen; - HMAC_CTX_cleanup(&hctx); } /* since we're expanding to a bit length, mask off the excess */ if (resultbitlen % 8) { + u8 mask = 0xff; mask <<= (8 - (resultbitlen % 8)); result[resultbytelen - 1] &= mask; } + + return 0; } @@ -85,9 +91,10 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, u8 *id_peer, int id_peer_len, u8 *token) { BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; - HMAC_CTX ctx; - unsigned char pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, ctr; - int nid, is_odd, primebitlen, primebytelen, ret = 0; + struct crypto_hash *hash; + unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; + int nid, is_odd, ret = 0; + size_t primebytelen, primebitlen; switch (num) { /* from IANA registry for IKE D-H groups */ case 19: @@ -154,7 +161,7 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, os_memset(prfbuf, 0, primebytelen); ctr = 0; while (1) { - if (ctr > 10) { + if (ctr > 30) { wpa_printf(MSG_INFO, "EAP-pwd: unable to find random " "point on curve for group %d, something's " "fishy", num); @@ -167,20 +174,23 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, * pwd-seed = H(token | peer-id | server-id | password | * counter) */ - H_Init(&ctx); - H_Update(&ctx, token, sizeof(u32)); - H_Update(&ctx, id_peer, id_peer_len); - H_Update(&ctx, id_server, id_server_len); - H_Update(&ctx, password, password_len); - H_Update(&ctx, &ctr, sizeof(ctr)); - H_Final(&ctx, pwe_digest); - - BN_bin2bn(pwe_digest, SHA256_DIGEST_LENGTH, rnd); - - eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH, - (unsigned char *) "EAP-pwd Hunting And Pecking", - os_strlen("EAP-pwd Hunting And Pecking"), - prfbuf, primebitlen); + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fail; + eap_pwd_h_update(hash, token, sizeof(u32)); + eap_pwd_h_update(hash, id_peer, id_peer_len); + eap_pwd_h_update(hash, id_server, id_server_len); + eap_pwd_h_update(hash, password, password_len); + eap_pwd_h_update(hash, &ctr, sizeof(ctr)); + eap_pwd_h_final(hash, pwe_digest); + + BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd); + + if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN, + (u8 *) "EAP-pwd Hunting And Pecking", + os_strlen("EAP-pwd Hunting And Pecking"), + prfbuf, primebitlen) < 0) + goto fail; BN_bin2bn(prfbuf, primebytelen, x_candidate); @@ -252,11 +262,13 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, if (0) { fail: EC_GROUP_free(grp->group); + grp->group = NULL; EC_POINT_free(grp->pwe); + grp->pwe = NULL; BN_free(grp->order); + grp->order = NULL; BN_free(grp->prime); - os_free(grp); - grp = NULL; + grp->prime = NULL; ret = 1; } /* cleanliness and order.... */ @@ -274,9 +286,9 @@ int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, u8 *confirm_peer, u8 *confirm_server, u32 *ciphersuite, u8 *msk, u8 *emsk) { - HMAC_CTX ctx; - u8 mk[SHA256_DIGEST_LENGTH], *cruft; - u8 session_id[SHA256_DIGEST_LENGTH + 1]; + struct crypto_hash *hash; + u8 mk[SHA256_MAC_LEN], *cruft; + u8 session_id[SHA256_MAC_LEN + 1]; u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN]; int offset; @@ -288,37 +300,46 @@ int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, * scal_s) */ session_id[0] = EAP_TYPE_PWD; - H_Init(&ctx); - H_Update(&ctx, (u8 *)ciphersuite, sizeof(u32)); + hash = eap_pwd_h_init(); + if (hash == NULL) { + os_free(cruft); + return -1; + } + eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32)); offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar); os_memset(cruft, 0, BN_num_bytes(grp->prime)); BN_bn2bin(peer_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar); os_memset(cruft, 0, BN_num_bytes(grp->prime)); BN_bn2bin(server_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(grp->order)); - H_Final(&ctx, &session_id[1]); + eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); + eap_pwd_h_final(hash, &session_id[1]); /* then compute MK = H(k | confirm-peer | confirm-server) */ - H_Init(&ctx); + hash = eap_pwd_h_init(); + if (hash == NULL) { + os_free(cruft); + return -1; + } offset = BN_num_bytes(grp->prime) - BN_num_bytes(k); os_memset(cruft, 0, BN_num_bytes(grp->prime)); BN_bn2bin(k, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(grp->prime)); - H_Update(&ctx, confirm_peer, SHA256_DIGEST_LENGTH); - H_Update(&ctx, confirm_server, SHA256_DIGEST_LENGTH); - H_Final(&ctx, mk); + eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime)); + os_free(cruft); + eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN); + eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN); + eap_pwd_h_final(hash, mk); /* stretch the mk with the session-id to get MSK | EMSK */ - eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH, - session_id, SHA256_DIGEST_LENGTH+1, - msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8); + if (eap_pwd_kdf(mk, SHA256_MAC_LEN, + session_id, SHA256_MAC_LEN + 1, + msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) { + return -1; + } os_memcpy(msk, msk_emsk, EAP_MSK_LEN); os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN); - os_free(cruft); - return 1; } diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h index 358d008e..816e58cc 100644 --- a/src/eap_common/eap_pwd_common.h +++ b/src/eap_common/eap_pwd_common.h @@ -10,10 +10,8 @@ #define EAP_PWD_COMMON_H #include <openssl/bn.h> -#include <openssl/sha.h> #include <openssl/ec.h> #include <openssl/evp.h> -#include <openssl/hmac.h> /* * definition of a finite cyclic group @@ -62,8 +60,8 @@ int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *, int, u8 *); int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *, u8 *, u8 *, u32 *, u8 *, u8 *); -void H_Init(HMAC_CTX *); -void H_Update(HMAC_CTX *, const u8 *, int); -void H_Final(HMAC_CTX *, u8 *); +struct crypto_hash * eap_pwd_h_init(void); +void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len); +void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest); #endif /* EAP_PWD_COMMON_H */ diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index 6a885895..8b43be43 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -1,6 +1,6 @@ /* * EAP peer state machines (RFC 4137) - * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -81,6 +81,16 @@ static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm) } +static void eap_notify_status(struct eap_sm *sm, const char *status, + const char *parameter) +{ + wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)", + status, parameter); + if (sm->eapol_cb->notify_status) + sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter); +} + + static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) { if (sm->m == NULL || sm->eap_method_priv == NULL) @@ -213,6 +223,7 @@ SM_STATE(EAP, GET_METHOD) { int reinit; EapType method; + const struct eap_method *eap_method; SM_ENTRY(EAP, GET_METHOD); @@ -221,18 +232,24 @@ SM_STATE(EAP, GET_METHOD) else method = sm->reqMethod; + eap_method = eap_peer_get_eap_method(sm->reqVendor, method); + if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) { wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed", sm->reqVendor, method); wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "vendor=%u method=%u -> NAK", sm->reqVendor, method); + eap_notify_status(sm, "refuse proposed method", + eap_method ? eap_method->name : "unknown"); goto nak; } wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "vendor=%u method=%u", sm->reqVendor, method); + eap_notify_status(sm, "accept proposed method", + eap_method ? eap_method->name : "unknown"); /* * RFC 4137 does not define specific operation for fast * re-authentication (session resumption). The design here is to allow @@ -256,7 +273,7 @@ SM_STATE(EAP, GET_METHOD) sm->selectedMethod = sm->reqMethod; if (sm->m == NULL) - sm->m = eap_peer_get_eap_method(sm->reqVendor, method); + sm->m = eap_method; if (!sm->m) { wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: " "vendor %d method %d", @@ -938,7 +955,7 @@ static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi, static int eap_sm_imsi_identity(struct eap_sm *sm, struct eap_peer_config *conf) { - int aka = 0; + enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM; char imsi[100]; size_t imsi_len; struct eap_method_type *m = conf->eap_methods; @@ -966,8 +983,14 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF || m[i].method != EAP_TYPE_NONE); i++) { if (m[i].vendor == EAP_VENDOR_IETF && + m[i].method == EAP_TYPE_AKA_PRIME) { + method = EAP_SM_AKA_PRIME; + break; + } + + if (m[i].vendor == EAP_VENDOR_IETF && m[i].method == EAP_TYPE_AKA) { - aka = 1; + method = EAP_SM_AKA; break; } } @@ -980,7 +1003,17 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, return -1; } - conf->identity[0] = aka ? '0' : '1'; + switch (method) { + case EAP_SM_SIM: + conf->identity[0] = '1'; + break; + case EAP_SM_AKA: + conf->identity[0] = '0'; + break; + case EAP_SM_AKA_PRIME: + conf->identity[0] = '6'; + break; + } os_memcpy(conf->identity + 1, imsi, imsi_len); conf->identity_len = 1 + imsi_len; @@ -1219,10 +1252,12 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) break; case EAP_CODE_SUCCESS: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success"); + eap_notify_status(sm, "completion", "success"); sm->rxSuccess = TRUE; break; case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); + eap_notify_status(sm, "completion", "failure"); sm->rxFailure = TRUE; break; default: @@ -1240,6 +1275,10 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, char *hash_hex = NULL; switch (ev) { + case TLS_CERT_CHAIN_SUCCESS: + eap_notify_status(sm, "remote certificate verification", + "success"); + break; case TLS_CERT_CHAIN_FAILURE: wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR "reason=%d depth=%d subject='%s' err='%s'", @@ -1247,6 +1286,8 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, data->cert_fail.depth, data->cert_fail.subject, data->cert_fail.reason_txt); + eap_notify_status(sm, "remote certificate verification", + data->cert_fail.reason_txt); break; case TLS_PEER_CERTIFICATE: if (!sm->eapol_cb->notify_cert) @@ -1267,6 +1308,14 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, data->peer_cert.subject, hash_hex, data->peer_cert.cert); break; + case TLS_ALERT: + if (data->alert.is_local) + eap_notify_status(sm, "local TLS alert", + data->alert.description); + else + eap_notify_status(sm, "remote TLS alert", + data->alert.description); + break; } os_free(hash_hex); @@ -1321,6 +1370,13 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, return NULL; } + sm->ssl_ctx2 = tls_init(&tlsconf); + if (sm->ssl_ctx2 == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS " + "context (2)."); + /* Run without separate TLS context within TLS tunnel */ + } + return sm; } @@ -1338,6 +1394,8 @@ void eap_peer_sm_deinit(struct eap_sm *sm) return; eap_deinit_prev_method(sm, "EAP deinit"); eap_sm_abort(sm); + if (sm->ssl_ctx2) + tls_deinit(sm->ssl_ctx2); tls_deinit(sm->ssl_ctx); os_free(sm); } diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index b95a285e..6e87475d 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -226,6 +226,15 @@ struct eapol_callbacks { */ void (*notify_cert)(void *ctx, int depth, const char *subject, const char *cert_hash, const struct wpabuf *cert); + + /** + * notify_status - Notification of the current EAP state + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @status: Step in the process of EAP authentication + * @parameter: Step-specific parameter, e.g., EAP method name + */ + void (*notify_status)(void *ctx, const char *status, + const char *parameter); }; /** diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index 7e37e44c..1cec4d8a 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -1,6 +1,6 @@ /* - * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -229,19 +229,19 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) static void eap_aka_clear_identities(struct eap_aka_data *data, int id) { - if (id & CLEAR_PSEUDONYM) { + if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym"); os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; } - if (id & CLEAR_REAUTH_ID) { + if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id"); os_free(data->reauth_id); data->reauth_id = NULL; data->reauth_id_len = 0; } - if (id & CLEAR_EAP_ID) { + if ((id & CLEAR_EAP_ID) && data->last_eap_identity) { wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id"); os_free(data->last_eap_identity); data->last_eap_identity = NULL; diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index 06d6db62..3318b811 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -317,6 +317,7 @@ struct eap_sm { void *msg_ctx; void *scard_ctx; void *ssl_ctx; + void *ssl_ctx2; unsigned int workaround; diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c index 37e92348..267d0a5c 100644 --- a/src/eap_peer/eap_pwd.c +++ b/src/eap_peer/eap_pwd.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "crypto/sha256.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -459,10 +460,10 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { BIGNUM *x = NULL, *y = NULL; - HMAC_CTX ctx; + struct crypto_hash *hash; u32 cs; u16 grp; - u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr; + u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; int offset; /* @@ -489,7 +490,9 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, * server's commit is H(k | server_element | server_scalar | * peer_element | peer_scalar | ciphersuite) */ - H_Init(&ctx); + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; /* * zero the memory each time because this is mod prime math and some @@ -498,7 +501,7 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); BN_bn2bin(data->k, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -511,18 +514,18 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->server_scalar); BN_bn2bin(data->server_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* my element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -536,27 +539,27 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* my scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* the ciphersuite */ - H_Update(&ctx, (u8 *) &cs, sizeof(u32)); + eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* random function fin */ - H_Final(&ctx, conf); + eap_pwd_h_final(hash, conf); ptr = (u8 *) payload; - if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) { + if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify"); goto fin; } @@ -568,13 +571,15 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, * H(k | peer_element | peer_scalar | server_element | server_scalar | * ciphersuite) */ - H_Init(&ctx); + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; /* k */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); BN_bn2bin(data->k, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* my element */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -587,18 +592,18 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* my scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* server element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -611,24 +616,24 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->server_scalar); BN_bn2bin(data->server_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* the ciphersuite */ - H_Update(&ctx, (u8 *) &cs, sizeof(u32)); + eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* all done */ - H_Final(&ctx, conf); + eap_pwd_h_final(hash, conf); if (compute_keys(data->grp, data->bnctx, data->k, data->my_scalar, data->server_scalar, conf, ptr, @@ -638,11 +643,11 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - data->outbuf = wpabuf_alloc(SHA256_DIGEST_LENGTH); + data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); if (data->outbuf == NULL) goto fin; - wpabuf_put_data(data->outbuf, conf, SHA256_DIGEST_LENGTH); + wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: os_free(cruft); @@ -725,6 +730,7 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, */ if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { wpabuf_free(data->outbuf); + data->outbuf = NULL; data->out_frag_pos = 0; } wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes", @@ -856,8 +862,11 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, /* * if we're not fragmenting then there's no need to carry this around */ - if (data->out_frag_pos == 0) + if (data->out_frag_pos == 0) { wpabuf_free(data->outbuf); + data->outbuf = NULL; + data->out_frag_pos = 0; + } return resp; } diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index a3067fa6..ed52fb69 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-TLS (RFC 2716) - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -21,6 +21,7 @@ static void eap_tls_deinit(struct eap_sm *sm, void *priv); struct eap_tls_data { struct eap_ssl_data ssl; u8 *key_data; + void *ssl_ctx; }; @@ -40,6 +41,9 @@ static void * eap_tls_init(struct eap_sm *sm) if (data == NULL) return NULL; + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; + if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_deinit(sm, data); @@ -165,7 +169,7 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, return eap_tls_failure(sm, data, ret, res, resp, id); } - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) + if (tls_connection_established(data->ssl_ctx, data->ssl.conn)) eap_tls_success(sm, data, ret); if (res == 1) { @@ -180,7 +184,7 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; - return tls_connection_established(sm->ssl_ctx, data->ssl.conn); + return tls_connection_established(data->ssl_ctx, data->ssl.conn); } diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index 52549f44..69e83d9b 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-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -136,14 +136,14 @@ static int eap_tls_init_connection(struct eap_sm *sm, { int res; - data->conn = tls_connection_init(sm->ssl_ctx); + data->conn = tls_connection_init(data->ssl_ctx); if (data->conn == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " "connection"); return -1; } - res = tls_connection_set_params(sm->ssl_ctx, data->conn, params); + res = tls_connection_set_params(data->ssl_ctx, data->conn, params); if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) { /* * At this point with the pkcs11 engine the PIN might be wrong. @@ -162,13 +162,13 @@ static int eap_tls_init_connection(struct eap_sm *sm, config->pin = NULL; eap_sm_request_pin(sm); sm->ignore = TRUE; - tls_connection_deinit(sm->ssl_ctx, data->conn); + tls_connection_deinit(data->ssl_ctx, data->conn); data->conn = NULL; return -1; } else if (res) { wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection " "parameters"); - tls_connection_deinit(sm->ssl_ctx, data->conn); + tls_connection_deinit(data->ssl_ctx, data->conn); data->conn = NULL; return -1; } @@ -197,6 +197,8 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, data->eap = sm; data->phase2 = sm->init_phase2; + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; if (eap_tls_params_from_conf(sm, data, ¶ms, config, data->phase2) < 0) return -1; @@ -234,7 +236,7 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, */ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) { - tls_connection_deinit(sm->ssl_ctx, data->conn); + tls_connection_deinit(data->ssl_ctx, data->conn); eap_peer_tls_reset_input(data); eap_peer_tls_reset_output(data); } @@ -265,8 +267,8 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, return NULL; /* First, try to use TLS library function for PRF, if available. */ - if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) == - 0) + if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len) + == 0) return out; /* @@ -274,7 +276,7 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, * session parameters and use an internal implementation of TLS PRF to * derive the key. */ - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys)) goto fail; if (keys.client_random == NULL || keys.server_random == NULL || @@ -441,14 +443,14 @@ static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, WPA_ASSERT(data->tls_out == NULL); } appl_data = NULL; - data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn, + data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn, msg, &appl_data); eap_peer_tls_reset_input(data); if (appl_data && - tls_connection_established(sm->ssl_ctx, data->conn) && - !tls_connection_get_failed(sm->ssl_ctx, data->conn)) { + tls_connection_established(data->ssl_ctx, data->conn) && + !tls_connection_get_failed(data->ssl_ctx, data->conn)) { wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data", appl_data); *out_data = appl_data; @@ -616,7 +618,7 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, return -1; } - if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) { + if (tls_connection_get_failed(data->ssl_ctx, data->conn)) { /* TLS processing has failed - return error */ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " "report error"); @@ -675,7 +677,7 @@ int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) { eap_peer_tls_reset_input(data); eap_peer_tls_reset_output(data); - return tls_connection_shutdown(sm->ssl_ctx, data->conn); + return tls_connection_shutdown(data->ssl_ctx, data->conn); } @@ -694,7 +696,8 @@ int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char name[128]; int len = 0, ret; - if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) { + if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0) + { ret = os_snprintf(buf + len, buflen - len, "EAP TLS cipher=%s\n", name); if (ret < 0 || (size_t) ret >= buflen - len) @@ -741,7 +744,7 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm, size_t left; unsigned int tls_msg_len; - if (tls_get_errors(sm->ssl_ctx)) { + if (tls_get_errors(data->ssl_ctx)) { wpa_printf(MSG_INFO, "SSL: TLS errors detected"); ret->ignore = TRUE; return NULL; @@ -849,7 +852,7 @@ int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, if (msg == NULL) return need_more_input ? 1 : -1; - *in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg); + *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg); eap_peer_tls_reset_input(data); if (*in_decrypted == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data"); @@ -877,8 +880,8 @@ int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, { if (in_data) { eap_peer_tls_reset_output(data); - data->tls_out = tls_connection_encrypt(sm->ssl_ctx, data->conn, - in_data); + data->tls_out = tls_connection_encrypt(data->ssl_ctx, + data->conn, in_data); if (data->tls_out == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 " "data (in_len=%lu)", diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index 7426467c..771385bc 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -1,6 +1,6 @@ /* * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2009, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -63,6 +63,11 @@ struct eap_ssl_data { * eap - EAP state machine allocated with eap_peer_sm_init() */ struct eap_sm *eap; + + /** + * ssl_ctx - TLS library context to use for the connection + */ + void *ssl_ctx; }; diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index 0204ba2e..e09f5e50 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -435,7 +435,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, "implicit challenge"); return -1; } - peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, RADIUS_VENDOR_ID_MICROSOFT, 1, @@ -448,7 +447,14 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; *pos++ = data->ident; *pos++ = 0; /* Flags */ - os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) { + os_free(challenge); + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get " + "random data for peer challenge"); + return -1; + } + peer_challenge = pos; pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; os_memset(pos, 0, 8); /* Reserved, must be zero */ pos += 8; @@ -456,6 +462,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, password_len, pwhash, challenge, peer_challenge, pos, data->auth_response, data->master_key)) { + os_free(challenge); wpabuf_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " "response"); diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index 4473b906..d007a570 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -1,6 +1,6 @@ /* * EAP-WSC peer for Wi-Fi Protected Setup - * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2009, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -137,6 +137,8 @@ static void * eap_wsc_init(struct eap_sm *sm) struct wps_context *wps; struct wps_credential new_ap_settings; int res; + u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN]; + int nfc = 0; wps = sm->wps; if (wps == NULL) { @@ -184,13 +186,26 @@ static void * eap_wsc_init(struct eap_sm *sm) while (*pos != '\0' && *pos != ' ') pos++; cfg.pin_len = pos - (const char *) cfg.pin; + if (cfg.pin_len >= WPS_OOB_DEVICE_PASSWORD_MIN_LEN * 2 && + cfg.pin_len <= WPS_OOB_DEVICE_PASSWORD_LEN * 2 && + hexstr2bin((const char *) cfg.pin, dev_pw, + cfg.pin_len / 2) == 0) { + /* Convert OOB Device Password to binary */ + cfg.pin = dev_pw; + cfg.pin_len /= 2; + } + if (cfg.pin_len == 6 && os_strncmp(pos, "nfc-pw", 6) == 0) { + cfg.pin = NULL; + cfg.pin_len = 0; + nfc = 1; + } } else { pos = os_strstr(phase1, "pbc=1"); if (pos) cfg.pbc = 1; } - if (cfg.pin == NULL && !cfg.pbc) { + if (cfg.pin == NULL && !cfg.pbc && !nfc) { wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 " "configuration data"); os_free(data); diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c index 135c02b1..9cd55093 100644 --- a/src/eap_server/eap_server_aka.c +++ b/src/eap_server/eap_server_aka.c @@ -1,6 +1,6 @@ /* - * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) - * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi> + * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) + * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -266,8 +266,18 @@ static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, EAP_AKA_SUBTYPE_IDENTITY); if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, sm->identity_len)) { - wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); - eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); + if (sm->identity_len > 0 && + (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX || + sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) { + /* Reauth id may have expired - try fullauth */ + wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, + NULL, 0); + } else { + wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, + NULL, 0); + } } else { /* * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is @@ -292,12 +302,23 @@ static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, const u8 *nonce_s) { os_free(data->next_pseudonym); - data->next_pseudonym = - eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1); + if (nonce_s == NULL) { + data->next_pseudonym = + eap_sim_db_get_next_pseudonym( + sm->eap_sim_db_priv, + data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA); + } else { + /* Do not update pseudonym during re-authentication */ + data->next_pseudonym = NULL; + } os_free(data->next_reauth_id); if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) { data->next_reauth_id = - eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 1); + eap_sim_db_get_next_reauth_id( + sm->eap_sim_db_priv, + data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA); } else { wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication " "count exceeded - force full authentication"); @@ -615,7 +636,8 @@ static void eap_aka_determine_identity(struct eap_sm *sm, identity = data->reauth->identity; identity_len = data->reauth->identity_len; } else if (sm->identity && sm->identity_len > 0 && - sm->identity[0] == EAP_AKA_PERMANENT_PREFIX) { + (sm->identity[0] == EAP_AKA_PERMANENT_PREFIX || + sm->identity[0] == EAP_AKA_PRIME_PERMANENT_PREFIX)) { identity = sm->identity; identity_len = sm->identity_len; } else { @@ -731,7 +753,7 @@ static void eap_aka_determine_identity(struct eap_sm *sm, sm->identity, identity_len); if (data->eap_method == EAP_TYPE_AKA_PRIME) { - eap_aka_prime_derive_keys(identity, identity_len, data->ik, + eap_aka_prime_derive_keys(sm->identity, identity_len, data->ik, data->ck, data->k_encr, data->k_aut, data->k_re, data->msk, data->emsk); } else { @@ -1017,11 +1039,6 @@ static void eap_aka_process_reauth(struct eap_sm *sm, identity_len = id2_len; } - if (data->next_pseudonym) { - eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, - identity_len, data->next_pseudonym); - data->next_pseudonym = NULL; - } if (data->next_reauth_id) { if (data->eap_method == EAP_TYPE_AKA_PRIME) { #ifdef EAP_SERVER_AKA_PRIME diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c index 6c47dee2..b61061bc 100644 --- a/src/eap_server/eap_server_pwd.c +++ b/src/eap_server/eap_server_pwd.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "crypto/sha256.h" #include "eap_server/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -40,7 +41,7 @@ struct eap_pwd_data { EC_POINT *my_element; EC_POINT *peer_element; - u8 my_confirm[SHA256_DIGEST_LENGTH]; + u8 my_confirm[SHA256_MAC_LEN]; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; @@ -288,8 +289,8 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { BIGNUM *x = NULL, *y = NULL; - HMAC_CTX ctx; - u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr; + struct crypto_hash *hash; + u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; u16 grp; int offset; @@ -313,7 +314,9 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, * commit is H(k | server_element | server_scalar | peer_element | * peer_scalar | ciphersuite) */ - H_Init(&ctx); + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; /* * Zero the memory each time because this is mod prime math and some @@ -324,7 +327,7 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); BN_bn2bin(data->k, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -338,18 +341,18 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* peer element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -363,18 +366,18 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* peer scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->peer_scalar); BN_bn2bin(data->peer_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* ciphersuite */ grp = htons(data->group_num); @@ -386,17 +389,17 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, ptr += sizeof(u8); *ptr = EAP_PWD_DEFAULT_PRF; ptr += sizeof(u8); - H_Update(&ctx, cruft, ptr-cruft); + eap_pwd_h_update(hash, cruft, ptr - cruft); /* all done with the random function */ - H_Final(&ctx, conf); - os_memcpy(data->my_confirm, conf, SHA256_DIGEST_LENGTH); + eap_pwd_h_final(hash, conf); + os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN); - data->outbuf = wpabuf_alloc(SHA256_DIGEST_LENGTH); + data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); if (data->outbuf == NULL) goto fin; - wpabuf_put_data(data->outbuf, conf, SHA256_DIGEST_LENGTH); + wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: os_free(cruft); @@ -404,8 +407,6 @@ fin: BN_free(y); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); - - return; } @@ -735,10 +736,10 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { BIGNUM *x = NULL, *y = NULL; - HMAC_CTX ctx; + struct crypto_hash *hash; u32 cs; u16 grp; - u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr; + u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; int offset; /* build up the ciphersuite: group | random_function | prf */ @@ -761,13 +762,15 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, * commit is H(k | peer_element | peer_scalar | server_element | * server_scalar | ciphersuite) */ - H_Init(&ctx); + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; /* k */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); BN_bn2bin(data->k, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* peer element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -780,18 +783,18 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* peer scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->peer_scalar); BN_bn2bin(data->peer_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* server element: x, y */ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, @@ -805,28 +808,28 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); BN_bn2bin(x, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); BN_bn2bin(y, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); /* server scalar */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); offset = BN_num_bytes(data->grp->order) - BN_num_bytes(data->my_scalar); BN_bn2bin(data->my_scalar, cruft + offset); - H_Update(&ctx, cruft, BN_num_bytes(data->grp->order)); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); /* ciphersuite */ os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - H_Update(&ctx, (u8 *)&cs, sizeof(u32)); + eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* all done */ - H_Final(&ctx, conf); + eap_pwd_h_final(hash, conf); ptr = (u8 *) payload; - if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) { + if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not " "verify"); goto fin; diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c index f49f70ea..6658d9c1 100644 --- a/src/eap_server/eap_server_sim.c +++ b/src/eap_server/eap_server_sim.c @@ -107,8 +107,17 @@ static struct wpabuf * eap_sim_build_start(struct eap_sm *sm, EAP_SIM_SUBTYPE_START); if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, sm->identity_len)) { - wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); - eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); + if (sm->identity_len > 0 && + sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) { + /* Reauth id may have expired - try fullauth */ + wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, + NULL, 0); + } else { + wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, + NULL, 0); + } } else { /* * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is @@ -131,12 +140,19 @@ static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data, const u8 *nonce_s) { os_free(data->next_pseudonym); - data->next_pseudonym = - eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0); + if (nonce_s == NULL) { + data->next_pseudonym = + eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, + EAP_SIM_DB_SIM); + } else { + /* Do not update pseudonym during re-authentication */ + data->next_pseudonym = NULL; + } os_free(data->next_reauth_id); if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) { data->next_reauth_id = - eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 0); + eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, + EAP_SIM_DB_SIM); } else { wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication " "count exceeded - force full authentication"); @@ -616,11 +632,6 @@ static void eap_sim_process_reauth(struct eap_sm *sm, identity_len = id2_len; } - if (data->next_pseudonym) { - eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, - identity_len, data->next_pseudonym); - data->next_pseudonym = NULL; - } if (data->next_reauth_id) { eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, identity_len, data->next_reauth_id, diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c index 9db26dc6..ce3238c9 100644 --- a/src/eap_server/eap_sim_db.c +++ b/src/eap_server/eap_sim_db.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-SIM database/authenticator gateway - * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2010, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -647,7 +647,8 @@ eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity, if (identity_len == 0 || (identity[0] != EAP_SIM_PSEUDONYM_PREFIX && - identity[0] != EAP_AKA_PSEUDONYM_PREFIX)) + identity[0] != EAP_AKA_PSEUDONYM_PREFIX && + identity[0] != EAP_AKA_PRIME_PSEUDONYM_PREFIX)) return NULL; /* Remove possible realm from identity */ @@ -685,7 +686,8 @@ eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity, if (identity_len == 0 || (identity[0] != EAP_SIM_PERMANENT_PREFIX && - identity[0] != EAP_AKA_PERMANENT_PREFIX)) + identity[0] != EAP_AKA_PERMANENT_PREFIX && + identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) return NULL; p = data->pseudonyms; @@ -710,7 +712,8 @@ eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity, if (identity_len == 0 || (identity[0] != EAP_SIM_REAUTH_ID_PREFIX && - identity[0] != EAP_AKA_REAUTH_ID_PREFIX)) + identity[0] != EAP_AKA_REAUTH_ID_PREFIX && + identity[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX)) return NULL; /* Remove possible realm from identity */ @@ -777,8 +780,9 @@ eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity, * @identity_len: Length of identity in bytes * Returns: 0 if the user is found or -1 on failure * - * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the - * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id. + * In most cases, the user name is ['0','1','6'] | IMSI, i.e., 1 followed by + * the IMSI in ASCII format for EAP-SIM, ['2','3','7'] | pseudonym, or + * ['4','5','7'] | reauth_id. */ int eap_sim_db_identity_known(void *priv, const u8 *identity, size_t identity_len) @@ -789,21 +793,24 @@ int eap_sim_db_identity_known(void *priv, const u8 *identity, return -1; if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX || - identity[0] == EAP_AKA_PSEUDONYM_PREFIX) { + identity[0] == EAP_AKA_PSEUDONYM_PREFIX || + identity[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) { struct eap_sim_pseudonym *p = eap_sim_db_get_pseudonym(data, identity, identity_len); return p ? 0 : -1; } if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX || - identity[0] == EAP_AKA_REAUTH_ID_PREFIX) { + identity[0] == EAP_AKA_REAUTH_ID_PREFIX || + identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) { struct eap_sim_reauth *r = eap_sim_db_get_reauth(data, identity, identity_len); return r ? 0 : -1; } if (identity[0] != EAP_SIM_PERMANENT_PREFIX && - identity[0] != EAP_AKA_PERMANENT_PREFIX) { + identity[0] != EAP_AKA_PERMANENT_PREFIX && + identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) { /* Unknown identity prefix */ return -1; } @@ -843,7 +850,7 @@ static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) /** * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym * @priv: Private data pointer from eap_sim_db_init() - * @aka: Using EAP-AKA instead of EAP-SIM + * @method: EAP method (SIM/AKA/AKA') * Returns: Next pseudonym (allocated string) or %NULL on failure * * This function is used to generate a pseudonym for EAP-SIM. The returned @@ -851,18 +858,31 @@ static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) * with eap_sim_db_add_pseudonym() once the authentication has been completed * successfully. Caller is responsible for freeing the returned buffer. */ -char * eap_sim_db_get_next_pseudonym(void *priv, int aka) +char * eap_sim_db_get_next_pseudonym(void *priv, enum eap_sim_db_method method) { struct eap_sim_db_data *data = priv; - return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX : - EAP_SIM_PSEUDONYM_PREFIX); + char prefix = EAP_SIM_REAUTH_ID_PREFIX; + + switch (method) { + case EAP_SIM_DB_SIM: + prefix = EAP_SIM_PSEUDONYM_PREFIX; + break; + case EAP_SIM_DB_AKA: + prefix = EAP_AKA_PSEUDONYM_PREFIX; + break; + case EAP_SIM_DB_AKA_PRIME: + prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX; + break; + } + + return eap_sim_db_get_next(data, prefix); } /** * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id * @priv: Private data pointer from eap_sim_db_init() - * @aka: Using EAP-AKA instead of EAP-SIM + * @method: EAP method (SIM/AKA/AKA') * Returns: Next reauth_id (allocated string) or %NULL on failure * * This function is used to generate a fast re-authentication identity for @@ -871,11 +891,24 @@ char * eap_sim_db_get_next_pseudonym(void *priv, int aka) * has been completed successfully. Caller is responsible for freeing the * returned buffer. */ -char * eap_sim_db_get_next_reauth_id(void *priv, int aka) +char * eap_sim_db_get_next_reauth_id(void *priv, enum eap_sim_db_method method) { struct eap_sim_db_data *data = priv; - return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX : - EAP_SIM_REAUTH_ID_PREFIX); + char prefix = EAP_SIM_REAUTH_ID_PREFIX; + + switch (method) { + case EAP_SIM_DB_SIM: + prefix = EAP_SIM_REAUTH_ID_PREFIX; + break; + case EAP_SIM_DB_AKA: + prefix = EAP_AKA_REAUTH_ID_PREFIX; + break; + case EAP_SIM_DB_AKA_PRIME: + prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX; + break; + } + + return eap_sim_db_get_next(data, prefix); } @@ -1156,7 +1189,7 @@ void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth) * called once the results become available. * * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in - * ASCII format. + * ASCII format for EAP-AKA and '6' | IMSI for EAP-AKA'. * * When using an external server for AKA authentication, this function can * always start a request and return EAP_SIM_DB_PENDING immediately if @@ -1178,7 +1211,8 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, char msg[40]; if (identity_len < 2 || identity == NULL || - identity[0] != EAP_AKA_PERMANENT_PREFIX) { + (identity[0] != EAP_AKA_PERMANENT_PREFIX && + identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", identity, identity_len); return EAP_SIM_DB_FAILURE; @@ -1281,7 +1315,8 @@ int eap_sim_db_resynchronize(void *priv, const u8 *identity, size_t i; if (identity_len < 2 || identity == NULL || - identity[0] != EAP_AKA_PERMANENT_PREFIX) { + (identity[0] != EAP_AKA_PERMANENT_PREFIX && + identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", identity, identity_len); return -1; diff --git a/src/eap_server/eap_sim_db.h b/src/eap_server/eap_sim_db.h index abe185ec..1f6375aa 100644 --- a/src/eap_server/eap_sim_db.h +++ b/src/eap_server/eap_sim_db.h @@ -1,6 +1,6 @@ /* * hostapd / EAP-SIM database/authenticator gateway - * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2008, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -18,6 +18,15 @@ #define EAP_AKA_PERMANENT_PREFIX '0' #define EAP_AKA_PSEUDONYM_PREFIX '2' #define EAP_AKA_REAUTH_ID_PREFIX '4' +#define EAP_AKA_PRIME_PERMANENT_PREFIX '6' +#define EAP_AKA_PRIME_PSEUDONYM_PREFIX '7' +#define EAP_AKA_PRIME_REAUTH_ID_PREFIX '8' + +enum eap_sim_db_method { + EAP_SIM_DB_SIM, + EAP_SIM_DB_AKA, + EAP_SIM_DB_AKA_PRIME +}; void * eap_sim_db_init(const char *config, void (*get_complete_cb)(void *ctx, void *session_ctx), @@ -36,9 +45,11 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, int eap_sim_db_identity_known(void *priv, const u8 *identity, size_t identity_len); -char * eap_sim_db_get_next_pseudonym(void *priv, int aka); +char * eap_sim_db_get_next_pseudonym(void *priv, + enum eap_sim_db_method method); -char * eap_sim_db_get_next_reauth_id(void *priv, int aka); +char * eap_sim_db_get_next_reauth_id(void *priv, + enum eap_sim_db_method method); int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, size_t identity_len, char *pseudonym); diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h index d0b7641f..d7f893a1 100644 --- a/src/eapol_auth/eapol_auth_sm_i.h +++ b/src/eapol_auth/eapol_auth_sm_i.h @@ -157,6 +157,7 @@ struct eapol_state_machine { * Authentication server */ u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */ struct radius_class_data radius_class; + struct wpabuf *radius_cui; /* Chargeable-User-Identity */ /* Keys for encrypting and signing EAPOL-Key frames */ u8 *eapol_key_sign; diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index c83709f8..f0cae700 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -1847,6 +1847,17 @@ static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject, cert_hash, cert); } + +static void eapol_sm_notify_status(void *ctx, const char *status, + const char *parameter) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->status_cb) + sm->ctx->status_cb(sm->ctx->ctx, status, parameter); +} + + static struct eapol_callbacks eapol_cb = { eapol_sm_get_config, @@ -1859,7 +1870,8 @@ static struct eapol_callbacks eapol_cb = eapol_sm_get_config_blob, eapol_sm_notify_pending, eapol_sm_eap_param_needed, - eapol_sm_notify_cert + eapol_sm_notify_cert, + eapol_sm_notify_status }; diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index 3260c614..1a20e4b9 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -230,6 +230,15 @@ struct eapol_ctx { * cert_in_cb - Include server certificates in callback */ int cert_in_cb; + + /** + * status_cb - Notification of a change in EAP status + * @ctx: Callback context (ctx) + * @status: Step in the process of EAP authentication + * @parameter: Step-specific parameter, e.g., EAP method name + */ + void (*status_cb)(void *ctx, const char *status, + const char *parameter); }; diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c index e24277c2..2e9a04c8 100644 --- a/src/l2_packet/l2_packet_freebsd.c +++ b/src/l2_packet/l2_packet_freebsd.c @@ -3,14 +3,8 @@ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * Copyright (c) 2005, Sam Leffler <sam@errno.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c index aacd76bb..6b117ca2 100644 --- a/src/l2_packet/l2_packet_privsep.c +++ b/src/l2_packet/l2_packet_privsep.c @@ -173,7 +173,7 @@ struct l2_packet_data * l2_packet_init( addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path)); if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("l2-pkt-privsep: bind(PF_UNIX)"); goto fail; } diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index aeeaccb3..58ada3b7 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -119,7 +119,7 @@ static void p2p_expire_peers(struct p2p_data *p2p) continue; #endif - wpa_msg(p2p->cfg->msg_ctx, MSG_ERROR, "P2P: Expiring old peer " + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer " "entry " MACSTR, MAC2STR(dev->info.p2p_device_addr)); #ifdef ANDROID_P2P /* SD_FAIR_POLICY: Update the current sd_dev_list pointer to next device */ @@ -193,14 +193,14 @@ u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr) } -void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *iface_addr) +void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev = NULL; - if (!iface_addr || !p2p) + if (!addr || !p2p) return; - dev = p2p_get_device_interface(p2p, iface_addr); + dev = p2p_get_device(p2p, addr); if (dev) dev->wps_prov_info = 0; } @@ -315,7 +315,7 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) p2p->pending_listen_usec = (timeout % 1000) * 1000; if (p2p->p2p_scan_running) { - if (p2p->start_after_scan == P2P_AFTER_SCAN_NOTHING) { + if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan running - connect is already " "pending - skip listen"); @@ -484,13 +484,25 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, continue; /* ignore our own entry */ dev = p2p_get_device(p2p, cli->p2p_device_addr); if (dev) { - /* - * Update information only if we have not received this - * directly from the client. - */ if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY | - P2P_DEV_PROBE_REQ_ONLY)) + P2P_DEV_PROBE_REQ_ONLY)) { + /* + * Update information since we have not + * received this directly from the client. + */ p2p_copy_client_info(dev, cli); + } else { + /* + * Need to update P2P Client Discoverability + * flag since it is valid only in P2P Group + * Info attribute. + */ + dev->info.dev_capab &= + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + dev->info.dev_capab |= + cli->dev_capab & + P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + } if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; } @@ -573,7 +585,13 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, } if (msg->capability) { - dev->info.dev_capab = msg->capability[0]; + /* + * P2P Client Discoverability bit is reserved in all frames + * that use this function, so do not change its value here. + */ + dev->info.dev_capab &= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + dev->info.dev_capab |= msg->capability[0] & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; dev->info.group_capab = msg->capability[1]; } @@ -582,6 +600,7 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, dev->ext_listen_interval = WPA_GET_LE16(msg->ext_listen_timing + 2); } + if (!probe_req) { dev->info.config_methods = msg->config_methods ? msg->config_methods : msg->wps_config_methods; @@ -590,7 +609,7 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, /** - * p2p_add_device - Add peer entries based on scan results + * p2p_add_device - Add peer entries based on scan results or P2P frames * @p2p: P2P module context from p2p_init() * @addr: Source address of Beacon or Probe Response frame (may be either * P2P Device Address or P2P Interface Address) @@ -598,6 +617,7 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, * @freq: Frequency on which the Beacon or Probe Response frame was received * @ies: IEs from the Beacon or Probe Response frame * @ies_len: Length of ies buffer in octets + * @scan_res: Whether this was based on scan results * Returns: 0 on success, -1 on failure * * If the scan result is for a GO, the clients in the group will also be added @@ -606,7 +626,7 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, * Info attributes. */ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level, - const u8 *ies, size_t ies_len) + const u8 *ies, size_t ies_len, int scan_res) { struct p2p_device *dev; struct p2p_message msg; @@ -675,16 +695,18 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level, } } - if (dev->listen_freq && dev->listen_freq != freq) { + if (dev->listen_freq && dev->listen_freq != freq && scan_res) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update Listen frequency based on scan " "results (" MACSTR " %d -> %d MHz (DS param %d)", MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, freq, msg.ds_params ? *msg.ds_params : -1); } - dev->listen_freq = freq; - if (msg.group_info) - dev->oper_freq = freq; + if (scan_res) { + dev->listen_freq = freq; + if (msg.group_info) + dev->oper_freq = freq; + } dev->info.level = level; p2p_copy_wps_info(dev, 0, &msg); @@ -703,8 +725,10 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level, break; } - p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info, - msg.group_info_len); + if (scan_res) { + p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, + msg.group_info, msg.group_info_len); + } p2p_parse_free(&msg); @@ -821,6 +845,7 @@ static void p2p_search(struct p2p_data *p2p) { int freq = 0; enum p2p_scan_type type; + u16 pw_id = DEV_PW_DEFAULT; if (p2p->drv_in_listen) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still " @@ -830,30 +855,8 @@ static void p2p_search(struct p2p_data *p2p) } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); - if (p2p->go_neg_peer) { - /* - * Only scan the known listen frequency of the peer - * during GO Negotiation start. - */ - freq = p2p->go_neg_peer->listen_freq; - if (freq <= 0) - freq = p2p->go_neg_peer->oper_freq; - type = P2P_SCAN_SPECIFIC; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search " - "for freq %u (GO Neg)", freq); - } else if (p2p->invite_peer) { - /* - * Only scan the known listen frequency of the peer - * during Invite start. - */ - freq = p2p->invite_peer->listen_freq; - if (freq <= 0) - freq = p2p->invite_peer->oper_freq; - type = P2P_SCAN_SPECIFIC; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search " - "for freq %u (Invite)", freq); - } else if (p2p->find_type == P2P_FIND_PROGRESSIVE && - (freq = p2p_get_next_prog_freq(p2p)) > 0) { + if (p2p->find_type == P2P_FIND_PROGRESSIVE && + (freq = p2p_get_next_prog_freq(p2p)) > 0) { type = P2P_SCAN_SOCIAL_PLUS_ONE; wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search " "(+ freq %u)", freq); @@ -864,7 +867,7 @@ static void p2p_search(struct p2p_data *p2p) if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq, p2p->num_req_dev_types, p2p->req_dev_types, - p2p->find_dev_id)) { + p2p->find_dev_id, pw_id)) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Scan request failed"); p2p_continue_find(p2p); @@ -1016,12 +1019,14 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, case P2P_FIND_PROGRESSIVE: res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0, p2p->num_req_dev_types, - p2p->req_dev_types, dev_id); + p2p->req_dev_types, dev_id, + DEV_PW_DEFAULT); break; case P2P_FIND_ONLY_SOCIAL: res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0, p2p->num_req_dev_types, - p2p->req_dev_types, dev_id); + p2p->req_dev_types, dev_id, + DEV_PW_DEFAULT); break; default: return -1; @@ -1090,11 +1095,21 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq) p2p->go_neg_peer = NULL; p2p->sd_peer = NULL; p2p->invite_peer = NULL; + p2p_stop_listen_for_freq(p2p, freq); +} + + +void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) +{ if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip stop_listen " "since we are on correct channel for response"); return; } + if (p2p->in_listen) { + p2p->in_listen = 0; + p2p_clear_timeout(p2p); + } if (p2p->drv_in_listen) { /* * The driver may not deliver callback to p2p_listen_end() @@ -1217,21 +1232,22 @@ static void p2p_set_dev_persistent(struct p2p_device *dev, int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, - unsigned int force_freq, int persistent_group) + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + int pd_before_go_neg) { struct p2p_device *dev; wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Request to start group negotiation - peer=" MACSTR " GO Intent=%d Intended Interface Address=" MACSTR - " wps_method=%d persistent_group=%d", + " wps_method=%d persistent_group=%d pd_before_go_neg=%d", MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), - wps_method, persistent_group); + wps_method, persistent_group, pd_before_go_neg); if (p2p_prepare_channel(p2p, force_freq) < 0) return -1; - p2p->ssid_set = 0; dev = p2p_get_device(p2p, peer_addr); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, @@ -1264,10 +1280,23 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, */ } + p2p->ssid_set = 0; + if (force_ssid) { + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID", + force_ssid, force_ssid_len); + os_memcpy(p2p->ssid, force_ssid, force_ssid_len); + p2p->ssid_len = force_ssid_len; + p2p->ssid_set = 1; + } + dev->flags &= ~P2P_DEV_NOT_YET_READY; dev->flags &= ~P2P_DEV_USER_REJECTED; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + if (pd_before_go_neg) + dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG; + else + dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; dev->connect_reqs = 0; dev->go_neg_req_sent = 0; dev->go_state = UNKNOWN_GO; @@ -1315,7 +1344,8 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, - unsigned int force_freq, int persistent_group) + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len) { struct p2p_device *dev; @@ -1337,6 +1367,15 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, return -1; } + p2p->ssid_set = 0; + if (force_ssid) { + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID", + force_ssid, force_ssid_len); + os_memcpy(p2p->ssid, force_ssid, force_ssid_len); + p2p->ssid_len = force_ssid_len; + p2p->ssid_set = 1; + } + dev->flags &= ~P2P_DEV_NOT_YET_READY; dev->flags &= ~P2P_DEV_USER_REJECTED; dev->go_neg_req_sent = 0; @@ -1821,16 +1860,23 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) { struct wpabuf *buf; u8 *len; + int pw_id = -1; buf = wpabuf_alloc(1000); if (buf == NULL) return NULL; - p2p_build_wps_ie(p2p, buf, DEV_PW_DEFAULT, 1); + if (p2p->go_neg_peer) { + /* Advertise immediate availability of WPS credential */ + pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method); + } + + p2p_build_wps_ie(p2p, buf, pw_id, 1); /* P2P IE */ len = p2p_buf_add_ie_hdr(buf); - p2p_buf_add_capability(buf, p2p->dev_capab, 0); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); if (p2p->ext_listen_interval) p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period, p2p->ext_listen_interval); @@ -1874,9 +1920,9 @@ static int supp_rates_11b_only(struct ieee802_11_elems *elems) } -static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, - const u8 *dst, const u8 *bssid, const u8 *ie, - size_t ie_len) +static enum p2p_probe_req_status +p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len) { struct ieee802_11_elems elems; struct wpabuf *buf; @@ -1886,55 +1932,55 @@ static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, if (!p2p->in_listen || !p2p->drv_in_listen) { /* not in Listen state - ignore Probe Request */ - return; + return P2P_PREQ_NOT_LISTEN; } if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { /* Ignore invalid Probe Request frames */ - return; + return P2P_PREQ_MALFORMED; } if (elems.p2p == NULL) { /* not a P2P probe - ignore it */ - return; + return P2P_PREQ_NOT_P2P; } if (dst && !is_broadcast_ether_addr(dst) && os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) { /* Not sent to the broadcast address or our P2P Device Address */ - return; + return P2P_PREQ_NOT_PROCESSED; } if (bssid && !is_broadcast_ether_addr(bssid)) { /* Not sent to the Wildcard BSSID */ - return; + return P2P_PREQ_NOT_PROCESSED; } if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN || os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0) { /* not using P2P Wildcard SSID - ignore */ - return; + return P2P_PREQ_NOT_PROCESSED; } if (supp_rates_11b_only(&elems)) { /* Indicates support for 11b rates only */ - return; + return P2P_PREQ_NOT_P2P; } os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg) < 0) { /* Could not parse P2P attributes */ - return; + return P2P_PREQ_NOT_P2P; } if (msg.device_id && - os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN != 0)) { + os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) { /* Device ID did not match */ p2p_parse_free(&msg); - return; + return P2P_PREQ_NOT_PROCESSED; } /* Check Requested Device Type match */ @@ -1942,12 +1988,14 @@ static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, !p2p_match_dev_type(p2p, msg.wps_attributes)) { /* No match with Requested Device Type */ p2p_parse_free(&msg); - return; + return P2P_PREQ_NOT_PROCESSED; } p2p_parse_free(&msg); - if (!p2p->cfg->send_probe_resp) - return; /* Response generated elsewhere */ + if (!p2p->cfg->send_probe_resp) { + /* Response generated elsewhere */ + return P2P_PREQ_NOT_PROCESSED; + } wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Reply to P2P Probe Request in Listen state"); @@ -1960,12 +2008,12 @@ static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, */ ies = p2p_build_probe_resp_ies(p2p); if (ies == NULL) - return; + return P2P_PREQ_NOT_PROCESSED; buf = wpabuf_alloc(200 + wpabuf_len(ies)); if (buf == NULL) { wpabuf_free(ies); - return; + return P2P_PREQ_NOT_PROCESSED; } resp = NULL; @@ -2008,15 +2056,20 @@ static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf); wpabuf_free(buf); + + return P2P_PREQ_NOT_PROCESSED; } -int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len) +enum p2p_probe_req_status +p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len) { + enum p2p_probe_req_status res; + p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); - p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); + res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) && p2p->go_neg_peer && @@ -2027,7 +2080,7 @@ int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, "P2P: Found GO Negotiation peer - try to start GO " "negotiation from timeout"); eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); - return 1; + return P2P_PREQ_PROCESSED; } if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && @@ -2039,10 +2092,10 @@ int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, "P2P: Found Invite peer - try to start Invite from " "timeout"); eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); - return 1; + return P2P_PREQ_PROCESSED; } - return 0; + return res; } @@ -2156,29 +2209,37 @@ int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end) } -int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr) +int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) { - struct wpabuf *p2p_ie; struct p2p_message msg; - p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, - P2P_IE_VENDOR_TYPE); - if (p2p_ie == NULL) - return -1; os_memset(&msg, 0, sizeof(msg)); - if (p2p_parse_p2p_ie(p2p_ie, &msg)) { - wpabuf_free(p2p_ie); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) return -1; - } - if (msg.p2p_device_addr == NULL) { - wpabuf_free(p2p_ie); - return -1; + if (msg.p2p_device_addr) { + os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); + return 0; + } else if (msg.device_id) { + os_memcpy(dev_addr, msg.device_id, ETH_ALEN); + return 0; } + return -1; +} - os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); + +int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr) +{ + struct wpabuf *p2p_ie; + int ret; + + p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, + P2P_IE_VENDOR_TYPE); + if (p2p_ie == NULL) + return -1; + ret = p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr); wpabuf_free(p2p_ie); - return 0; + return ret; } @@ -2256,6 +2317,17 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) p2p->cfg->model_number = os_strdup(cfg->model_number); if (cfg->serial_number) p2p->cfg->serial_number = os_strdup(cfg->serial_number); + if (cfg->pref_chan) { + p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan * + sizeof(struct p2p_channel)); + if (p2p->cfg->pref_chan) { + os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan, + cfg->num_pref_chan * + sizeof(struct p2p_channel)); + } else + p2p->cfg->num_pref_chan = 0; + } + #ifdef ANDROID_P2P /* 100ms listen time is too less to receive the response frames in some scenarios * increasing min listen time to 200ms. @@ -2298,6 +2370,7 @@ void p2p_deinit(struct p2p_data *p2p) os_free(p2p->cfg->model_name); os_free(p2p->cfg->model_number); os_free(p2p->cfg->serial_number); + os_free(p2p->cfg->pref_chan); os_free(p2p->groups); wpabuf_free(p2p->sd_resp); os_free(p2p->after_scan_tx); @@ -2309,16 +2382,7 @@ void p2p_deinit(struct p2p_data *p2p) void p2p_flush(struct p2p_data *p2p) { struct p2p_device *dev, *prev; - p2p_clear_timeout(p2p); -#ifdef ANDROID_P2P - if (p2p->state == P2P_SEARCH) - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - P2P_EVENT_FIND_STOPPED); -#endif - p2p_set_state(p2p, P2P_IDLE); - p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; - p2p->go_neg_peer = NULL; - eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + p2p_stop_find(p2p); dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device, list) { dl_list_del(&dev->list); @@ -2671,17 +2735,7 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, int level, const u8 *ies, size_t ies_len) { - p2p_add_device(p2p, bssid, freq, level, ies, ies_len); - - if (p2p->go_neg_peer && p2p->state == P2P_SEARCH && - os_memcmp(p2p->go_neg_peer->info.p2p_device_addr, bssid, ETH_ALEN) - == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Found GO Negotiation peer - try to start GO " - "negotiation"); - p2p_connect_send(p2p, p2p->go_neg_peer); - return 1; - } + p2p_add_device(p2p, bssid, freq, level, ies, ies_len, 1); return 0; } @@ -2706,7 +2760,8 @@ void p2p_scan_res_handled(struct p2p_data *p2p) void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) { u8 *len = p2p_buf_add_ie_hdr(ies); - p2p_buf_add_capability(ies, p2p->dev_capab, 0); + p2p_buf_add_capability(ies, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); if (dev_id) p2p_buf_add_device_id(ies, dev_id); if (p2p->cfg->reg_class && p2p->cfg->channel) @@ -2748,19 +2803,14 @@ static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) } if (success) { -#ifndef ANDROID_P2P - dev->go_neg_req_sent++; -#endif if (dev->flags & P2P_DEV_USER_REJECTED) { p2p_set_state(p2p, P2P_IDLE); return; } - } -#ifdef ANDROID_P2P - else { + } else if (dev->go_neg_req_sent) { + /* Cancel the increment from p2p_connect_send() on failure */ dev->go_neg_req_sent--; } -#endif if (!success && (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) && @@ -2978,6 +3028,18 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) "new one"); return 1; } + if (p2p->pending_listen_freq) { + /* + * Better wait a bit if the driver is unable to start + * offchannel operation for some reason. p2p_search() + * will be started from internal timeout. + */ + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Listen " + "operation did not seem to start - delay " + "search phase to avoid busy loop"); + p2p_set_timeout(p2p, 0, 100000); + return 1; + } p2p_search(p2p); return 1; } @@ -2989,6 +3051,14 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) static void p2p_timeout_connect(struct p2p_data *p2p) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + if (p2p->go_neg_peer && + (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Wait for GO " + "Negotiation Confirm timed out - assume GO " + "Negotiation failed"); + p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + return; + } p2p_set_state(p2p, P2P_CONNECT_LISTEN); p2p_listen_in_find(p2p); } @@ -3802,6 +3872,28 @@ int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, } +int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, + const struct p2p_channel *pref_chan) +{ + struct p2p_channel *n; + + if (pref_chan) { + n = os_malloc(num_pref_chan * sizeof(struct p2p_channel)); + if (n == NULL) + return -1; + os_memcpy(n, pref_chan, + num_pref_chan * sizeof(struct p2p_channel)); + } else + n = NULL; + + os_free(p2p->cfg->pref_chan); + p2p->cfg->pref_chan = n; + p2p->cfg->num_pref_chan = num_pref_chan; + + return 0; +} + + int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, u8 *iface_addr) { diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index fe98f5eb..70067074 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -131,7 +131,6 @@ struct p2p_data; enum p2p_scan_type { P2P_SCAN_SOCIAL, P2P_SCAN_FULL, - P2P_SCAN_SPECIFIC, P2P_SCAN_SOCIAL_PLUS_ONE }; @@ -218,6 +217,11 @@ enum p2p_prov_disc_status { P2P_PROV_DISC_REJECTED, }; +struct p2p_channel { + u8 op_class; + u8 chan; +}; + /** * struct p2p_config - P2P configuration * @@ -265,6 +269,16 @@ struct p2p_config { struct p2p_channels channels; /** + * num_pref_chan - Number of pref_chan entries + */ + unsigned int num_pref_chan; + + /** + * pref_chan - Preferred channels for GO Negotiation + */ + struct p2p_channel *pref_chan; + + /** * pri_dev_type - Primary Device Type (see WPS) */ u8 pri_dev_type[8]; @@ -354,14 +368,14 @@ struct p2p_config { * @num_req_dev_types: Number of requested device types * @req_dev_types: Array containing requested device types * @dev_id: Device ID to search for or %NULL to find all devices + * @pw_id: Device Password ID * Returns: 0 on success, -1 on failure * * This callback function is used to request a P2P scan or search * operation to be completed. Type type argument specifies which type * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL - * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC - * request a scan of a single channel specified by freq. + * indicates that all channels are to be scanned. * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels * plus one extra channel specified by freq. * @@ -377,7 +391,7 @@ struct p2p_config { */ int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq, unsigned int num_req_dev_types, - const u8 *req_dev_types, const u8 *dev_id); + const u8 *req_dev_types, const u8 *dev_id, u16 pw_id); /** * send_probe_resp - Transmit a Probe Response frame @@ -861,12 +875,20 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout); * @persistent_group: Whether to create a persistent group (0 = no, 1 = * persistent group without persistent reconnect, 2 = persistent group with * persistent reconnect) + * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate + * a new SSID + * @force_ssid_len: Length of $force_ssid buffer + * @pd_before_go_neg: Whether to send Provision Discovery prior to GO + * Negotiation as an interoperability workaround when initiating group + * formation * Returns: 0 on success, -1 on failure */ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, - unsigned int force_freq, int persistent_group); + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + int pd_before_go_neg); /** * p2p_authorize - Authorize P2P group formation (GO negotiation) @@ -879,6 +901,9 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, * @persistent_group: Whether to create a persistent group (0 = no, 1 = * persistent group without persistent reconnect, 2 = persistent group with * persistent reconnect) + * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate + * a new SSID + * @force_ssid_len: Length of $force_ssid buffer * Returns: 0 on success, -1 on failure * * This is like p2p_connect(), but the actual group negotiation is not @@ -887,7 +912,8 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, enum p2p_wps_method wps_method, int go_intent, const u8 *own_interface_addr, - unsigned int force_freq, int persistent_group); + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len); /** * p2p_reject - Reject peer device (explicitly block connection attempts) @@ -1060,17 +1086,34 @@ u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr); /** * p2p_clear_provisioning_info - Clear any stored provisioning info * @p2p: P2P module context from p2p_init() - * @iface_addr: Peer P2P Interface Address + * @iface_addr: Peer P2P Device Address * * This function is used to clear stored WPS provisioning info for the given * peer. */ -void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *iface_addr); +void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr); /* Event notifications from lower layer driver operations */ /** + * enum p2p_probe_req_status + * + * @P2P_PREQ_MALFORMED: frame was not well-formed + * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored + * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request + * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed + * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P + */ +enum p2p_probe_req_status { + P2P_PREQ_MALFORMED, + P2P_PREQ_NOT_LISTEN, + P2P_PREQ_NOT_P2P, + P2P_PREQ_NOT_PROCESSED, + P2P_PREQ_PROCESSED +}; + +/** * p2p_probe_req_rx - Report reception of a Probe Request frame * @p2p: P2P module context from p2p_init() * @addr: Source MAC address @@ -1078,10 +1121,11 @@ void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *iface_addr); * @bssid: BSSID if available or %NULL * @ie: Information elements from the Probe Request frame body * @ie_len: Length of ie buffer in octets - * Returns: 0 to indicate the frame was not processed or 1 if it was + * Returns: value indicating the type and status of the probe request */ -int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, - const u8 *bssid, const u8 *ie, size_t ie_len); +enum p2p_probe_req_status +p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len); /** * p2p_rx_action - Report received Action frame @@ -1220,6 +1264,16 @@ struct p2p_group_config { unsigned int max_clients; /** + * ssid - Group SSID + */ + u8 ssid[32]; + + /** + * ssid_len - Length of SSID + */ + size_t ssid_len; + + /** * cb_ctx - Context to use with callback functions */ void *cb_ctx; @@ -1371,6 +1425,15 @@ int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end); int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end); /** + * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated + * P2P IE + * @p2p_ie: P2P IE + * @dev_addr: Buffer for returning P2P Device Address + * Returns: 0 on success or -1 if P2P Device Address could not be parsed + */ +int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr); + +/** * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s) * @ies: Information elements from scan results * @ies_len: ies buffer length in octets @@ -1608,6 +1671,16 @@ int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, int cfg_op_channel); /** + * p2p_set_pref_chan - Set P2P preferred channel list + * @p2p: P2P module context from p2p_init() + * @num_pref_chan: Number of entries in pref_chan list + * @pref_chan: Preferred channels or %NULL to remove preferences + * Returns: 0 on success, -1 on failure + */ +int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, + const struct p2p_channel *pref_chan); + +/** * p2p_in_progress - Check whether a P2P operation is progress * @p2p: P2P module context from p2p_init() * Returns: 0 if P2P module is idle or 1 if an operation is in progress diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c index 2106964e..def422de 100644 --- a/src/p2p/p2p_build.c +++ b/src/p2p/p2p_build.c @@ -351,7 +351,7 @@ static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, } -void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id, +void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, int all_attr) { u8 *len; @@ -369,11 +369,14 @@ void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id, wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED); } - /* Device Password ID */ - wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID); - wpabuf_put_be16(buf, 2); - wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d", pw_id); - wpabuf_put_be16(buf, pw_id); + if (pw_id >= 0) { + /* Device Password ID */ + wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID); + wpabuf_put_be16(buf, 2); + wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d", + pw_id); + wpabuf_put_be16(buf, pw_id); + } if (all_attr) { wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE); diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 6e818c23..248b2a06 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -98,7 +98,7 @@ static int p2p_peer_channels(struct p2p_data *p2p, struct p2p_device *dev, } -static u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method) +u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method) { switch (wps_method) { case WPS_PIN_DISPLAY: @@ -155,7 +155,9 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; - p2p_buf_add_capability(buf, p2p->dev_capab, group_capab); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | p2p->next_tie_breaker); p2p->next_tie_breaker = !p2p->next_tie_breaker; @@ -184,6 +186,23 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) struct wpabuf *req; int freq; + if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { + u16 config_method; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Use PD-before-GO-Neg workaround for " MACSTR, + MAC2STR(dev->info.p2p_device_addr)); + if (dev->wps_method == WPS_PIN_DISPLAY) + config_method = WPS_CONFIG_KEYPAD; + else if (dev->wps_method == WPS_PIN_KEYPAD) + config_method = WPS_CONFIG_DISPLAY; + else if (dev->wps_method == WPS_PBC) + config_method = WPS_CONFIG_PUSHBUTTON; + else + return -1; + return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr, + config_method, 0, 0); + } + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; if (freq <= 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, @@ -203,9 +222,6 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) p2p->go_neg_peer = dev; dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->connect_reqs++; -#ifdef ANDROID_P2P - dev->go_neg_req_sent++; -#endif if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { @@ -213,7 +229,8 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) "P2P: Failed to send Action frame"); /* Use P2P find to recover and retry */ p2p_set_timeout(p2p, 0, 0); - } + } else + dev->go_neg_req_sent++; wpabuf_free(req); @@ -253,7 +270,9 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; } - p2p_buf_add_capability(buf, p2p->dev_capab, group_capab); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); p2p_buf_add_config_timeout(buf, 100, 20); if (peer && peer->go_state == REMOTE_GO) { @@ -299,6 +318,7 @@ static void p2p_reselect_channel(struct p2p_data *p2p, struct p2p_reg_class *cl; int freq; u8 op_reg_class, op_channel; + unsigned int i; wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating " "channel (reg_class %u channel %u) not acceptable to the " @@ -331,6 +351,21 @@ static void p2p_reselect_channel(struct p2p_data *p2p, return; } + /* Select channel with highest preference if the peer supports it */ + for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) { + if (p2p_channels_includes(intersection, + p2p->cfg->pref_chan[i].op_class, + p2p->cfg->pref_chan[i].chan)) { + p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class; + p2p->op_channel = p2p->cfg->pref_chan[i].chan; + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick " + "highest preferred chnnel (op_class %u " + "channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + } + /* * Fall back to whatever is included in the channel intersection since * no better options seems to be available. @@ -641,6 +676,17 @@ fail: if (status == P2P_SC_SUCCESS) { p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE; dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM; + if (os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) < 0) { + /* + * Peer has smaller address, so the GO Negotiation + * Response from us is expected to complete + * negotiation. Ignore a GO Negotiation Response from + * the peer if it happens to be received after this + * point due to a race condition in GO Negotiation + * Request transmission and processing. + */ + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; + } } else p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE_FAILURE; @@ -688,7 +734,9 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; } - p2p_buf_add_capability(buf, p2p->dev_capab, group_capab); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); if (go || resp_chan == NULL) p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, @@ -992,7 +1040,7 @@ fail: else freq = dev->listen_freq; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, - wpabuf_head(conf), wpabuf_len(conf), 200) < 0) { + wpabuf_head(conf), wpabuf_len(conf), 0) < 0) { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to send Action frame"); p2p_go_neg_failed(p2p, dev, -1); diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index fafd1353..8d4a3cbe 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -135,11 +135,10 @@ static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m) static void p2p_group_add_common_ies(struct p2p_group *group, struct wpabuf *ie) { - u8 dev_capab = 0, group_capab = 0; + u8 dev_capab = group->p2p->dev_capab, group_capab = 0; /* P2P Capability */ - dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; - dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE; + dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; if (group->cfg->persistent_group) { group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; @@ -731,3 +730,15 @@ int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr) return 0; } + + +int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, + size_t group_id_len) +{ + if (group_id_len != ETH_ALEN + group->cfg->ssid_len) + return 0; + if (os_memcmp(group_id, group->p2p->cfg->dev_addr, ETH_ALEN) != 0) + return 0; + return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid, + group->cfg->ssid_len) == 0; +} diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 4ccd38d7..5e21249e 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -90,6 +90,7 @@ struct p2p_device { #define P2P_DEV_PD_FOR_JOIN BIT(14) #define P2P_DEV_REPORTED_ONCE BIT(15) #define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16) +#define P2P_DEV_PD_BEFORE_GO_NEG BIT(17) unsigned int flags; int status; /* enum p2p_status_code */ @@ -557,6 +558,8 @@ const u8 * p2p_group_get_interface_addr(struct p2p_group *group); u8 p2p_group_presence_req(struct p2p_group *group, const u8 *client_interface_addr, const u8 *noa, size_t noa_len); +int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, + size_t group_id_len); void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); @@ -588,7 +591,7 @@ void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow, void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, u16 interval); void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p); -void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id, +void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, int all_attr); /* p2p_sd.c */ @@ -616,6 +619,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); +u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method); /* p2p_pd.c */ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, @@ -660,7 +664,7 @@ struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, struct p2p_device *dev, struct p2p_message *msg); int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level, - const u8 *ies, size_t ies_len); + const u8 *ies, size_t ies_len, int scan_res); struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr); struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, const u8 *addr); @@ -675,5 +679,6 @@ void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len); int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time); +void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq); #endif /* P2P_I_H */ diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index 417f1e7e..59255495 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -121,7 +121,8 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, "P2P: Invitation Request from unknown peer " MACSTR, MAC2STR(sa)); - if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) { + if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1, 0)) + { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Invitation Request add device failed " MACSTR, MAC2STR(sa)); @@ -344,6 +345,8 @@ int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, req = p2p_build_invitation_req(p2p, dev, go_dev_addr); if (req == NULL) return -1; + if (p2p->state != P2P_IDLE) + p2p_stop_listen_for_freq(p2p, freq); wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Sending Invitation Request"); p2p_set_state(p2p, P2P_INVITE); diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index 55a3b908..38a9dd81 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -54,7 +54,8 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); len = p2p_buf_add_ie_hdr(buf); - p2p_buf_add_capability(buf, p2p->dev_capab, 0); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); p2p_buf_add_device_info(buf, p2p, NULL); if (go) { p2p_buf_add_group_id(buf, go->info.p2p_device_addr, @@ -110,7 +111,8 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery Request from " "unknown peer " MACSTR, MAC2STR(sa)); - if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) { + if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1, 0)) + { wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery Request add device " "failed " MACSTR, MAC2STR(sa)); @@ -125,6 +127,21 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, goto out; } + if (msg.group_id) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + if (p2p_group_is_group_id_match(p2p->groups[i], + msg.group_id, + msg.group_id_len)) + break; + } + if (i == p2p->num_groups) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: PD " + "request for unknown P2P Group ID - reject"); + goto out; + } + } + if (dev) dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | P2P_DEV_PD_PEER_KEYPAD); @@ -198,6 +215,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, struct p2p_message msg; struct p2p_device *dev; u16 report_config_methods = 0; + int success = 0; if (p2p_parse(data, len, &msg)) return; @@ -266,11 +284,21 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, dev->wps_prov_info = msg.wps_config_methods; p2p_parse_free(&msg); + success = 1; out: dev->req_config_methods = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); - if (p2p->cfg->prov_disc_resp) + if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: Start GO Neg after the PD-before-GO-Neg " + "workaround with " MACSTR, + MAC2STR(dev->info.p2p_device_addr)); + dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; + p2p_connect_send(p2p, dev); + return; + } + if (success && p2p->cfg->prov_disc_resp) p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, report_config_methods); } @@ -316,6 +344,8 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, if (req == NULL) return -1; + if (p2p->state != P2P_IDLE) + p2p_stop_listen_for_freq(p2p, freq); p2p->pending_action_state = P2P_PENDING_PD; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c index 37b3f7bd..05097677 100644 --- a/src/p2p/p2p_sd.c +++ b/src/p2p/p2p_sd.c @@ -364,9 +364,14 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, "previous SD response"); wpabuf_free(p2p->sd_resp); } + p2p->sd_resp = wpabuf_dup(resp_tlvs); + if (p2p->sd_resp == NULL) { + wpa_msg(p2p->cfg->msg_ctx, MSG_ERROR, "P2P: Failed to " + "allocate SD response fragmentation area"); + return; + } os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN); p2p->sd_resp_dialog_token = dialog_token; - p2p->sd_resp = wpabuf_dup(resp_tlvs); p2p->sd_resp_pos = 0; p2p->sd_frag_id = 0; resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, @@ -404,9 +409,18 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, u16 slen; u16 update_indic; +#ifdef ANDROID_P2P + if (p2p->state != P2P_SD_DURING_FIND) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: #### Not ignoring unexpected GAS Initial Response from " + MACSTR " state %d", MAC2STR(sa), p2p->state); + } + if (p2p->sd_peer == NULL || +#else if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || +#endif os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_ERROR, + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, "P2P: Ignore unexpected GAS Initial Response from " MACSTR, MAC2STR(sa)); return; @@ -645,9 +659,18 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len); +#ifdef ANDROID_P2P + if (p2p->state != P2P_SD_DURING_FIND) { + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + "P2P: #### Not ignoring unexpected GAS Comeback Response from " + MACSTR " state %d", MAC2STR(sa), p2p->state); + } + if (p2p->sd_peer == NULL || +#else if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || +#endif os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, + wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, "P2P: Ignore unexpected GAS Comeback Response from " MACSTR, MAC2STR(sa)); return; @@ -842,7 +865,7 @@ void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, { struct p2p_sd_query *q; #ifdef ANDROID_P2P - /* Currently, supplicant doesn't support more than one pending broadcast SD request. + /* Currently, supplicant doesn't support more than one pending broadcast SD request. * So reject if application is registering another one before cancelling the existing one. */ for (q = p2p->sd_queries; q; q = q->next) { @@ -872,7 +895,7 @@ void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, q->next = p2p->sd_queries; p2p->sd_queries = q; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p for_all_peers %d", q, q->for_all_peers); + wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q); if (dst == NULL) { struct p2p_device *dev; @@ -893,24 +916,10 @@ void p2p_sd_service_update(struct p2p_data *p2p) int p2p_sd_cancel_request(struct p2p_data *p2p, void *req) { if (p2p_unlink_sd_query(p2p, req)) { -#ifdef ANDROID_P2P - struct p2p_device *dev; - struct p2p_sd_query *q = (struct p2p_sd_query *)req; -#endif wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Cancel pending SD query %p", req); #ifdef ANDROID_P2P - /* If the request is a bcast query, then clear the - * P2P_DEV_SD_INFO flag so that when new sd query is registered, - * we will send the SD request frames to peer devices. - */ - if(q->for_all_peers) { - p2p->sd_dev_list = NULL; - dl_list_for_each(dev, &p2p->devices, - struct p2p_device, list) { - dev->flags &= ~P2P_DEV_SD_INFO; - } - } + p2p->sd_dev_list = NULL; #endif p2p_free_sd_query(req); return 0; diff --git a/src/radius/radius.c b/src/radius/radius.c index 0dd6b127..2b9cbcaa 100644 --- a/src/radius/radius.c +++ b/src/radius/radius.c @@ -1,6 +1,6 @@ /* * RADIUS message processing - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, 2011-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -147,6 +147,12 @@ static const char *radius_code_string(u8 code) case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; case RADIUS_CODE_RESERVED: return "Reserved"; + case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request"; + case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK"; + case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK"; + case RADIUS_CODE_COA_REQUEST: return "CoA-Request"; + case RADIUS_CODE_COA_ACK: return "CoA-ACK"; + case RADIUS_CODE_COA_NAK: return "CoA-NAK"; default: return "?Unknown?"; } } @@ -222,9 +228,10 @@ static struct radius_attr_type radius_attrs[] = RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity", + { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, + { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 } }; #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0])) @@ -406,6 +413,45 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, } +int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, + size_t secret_len, + const struct radius_hdr *req_hdr) +{ + const u8 *addr[2]; + size_t len[2]; + u8 auth[MD5_MAC_LEN]; + struct radius_attr_hdr *attr; + + os_memset(auth, 0, MD5_MAC_LEN); + attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + auth, MD5_MAC_LEN); + if (attr == NULL) { + wpa_printf(MSG_WARNING, "Could not add Message-Authenticator"); + return -1; + } + + msg->hdr->length = htons(wpabuf_len(msg->buf)); + os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16); + hmac_md5(secret, secret_len, wpabuf_head(msg->buf), + wpabuf_len(msg->buf), (u8 *) (attr + 1)); + + /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ + addr[0] = wpabuf_head_u8(msg->buf); + len[0] = wpabuf_len(msg->buf); + addr[1] = secret; + len[1] = secret_len; + if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) + return -1; + + if (wpabuf_len(msg->buf) > 0xffff) { + wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", + (unsigned long) wpabuf_len(msg->buf)); + return -1; + } + return 0; +} + + void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, size_t secret_len) { @@ -427,6 +473,88 @@ void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, } +int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, + size_t secret_len) +{ + const u8 *addr[4]; + size_t len[4]; + u8 zero[MD5_MAC_LEN]; + u8 hash[MD5_MAC_LEN]; + + os_memset(zero, 0, sizeof(zero)); + addr[0] = (u8 *) msg->hdr; + len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; + addr[1] = zero; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, hash); + return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0; +} + + +int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, + size_t secret_len) +{ + const u8 *addr[4]; + size_t len[4]; + u8 zero[MD5_MAC_LEN]; + u8 hash[MD5_MAC_LEN]; + u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; + u8 orig_authenticator[16]; + + struct radius_attr_hdr *attr = NULL, *tmp; + size_t i; + + os_memset(zero, 0, sizeof(zero)); + addr[0] = (u8 *) msg->hdr; + len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; + addr[1] = zero; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, hash); + if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0) + return 1; + + for (i = 0; i < msg->attr_used; i++) { + tmp = radius_get_attr_hdr(msg, i); + if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { + if (attr != NULL) { + wpa_printf(MSG_WARNING, "Multiple " + "Message-Authenticator attributes " + "in RADIUS message"); + return 1; + } + attr = tmp; + } + } + + if (attr == NULL) { + /* Message-Authenticator is MAY; not required */ + return 0; + } + + os_memcpy(orig, attr + 1, MD5_MAC_LEN); + os_memset(attr + 1, 0, MD5_MAC_LEN); + os_memcpy(orig_authenticator, msg->hdr->authenticator, + sizeof(orig_authenticator)); + os_memset(msg->hdr->authenticator, 0, + sizeof(msg->hdr->authenticator)); + hmac_md5(secret, secret_len, wpabuf_head(msg->buf), + wpabuf_len(msg->buf), auth); + os_memcpy(attr + 1, orig, MD5_MAC_LEN); + os_memcpy(msg->hdr->authenticator, orig_authenticator, + sizeof(orig_authenticator)); + + return os_memcmp(orig, auth, MD5_MAC_LEN) != 0; +} + + static int radius_msg_add_attr_to_array(struct radius_msg *msg, struct radius_attr_hdr *attr) { @@ -1424,3 +1552,24 @@ int radius_copy_class(struct radius_class_data *dst, return 0; } + + +u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs) +{ + size_t i, j; + struct radius_attr_hdr *attr; + + for (i = 0; i < msg->attr_used; i++) { + attr = radius_get_attr_hdr(msg, i); + + for (j = 0; attrs[j]; j++) { + if (attr->type == attrs[j]) + break; + } + + if (attrs[j] == 0) + return attr->type; /* unlisted attr */ + } + + return 0; +} diff --git a/src/radius/radius.h b/src/radius/radius.h index 44123bdb..2d059df1 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -1,6 +1,6 @@ /* * RADIUS message processing - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -31,6 +31,12 @@ enum { RADIUS_CODE_ACCESS_REQUEST = 1, RADIUS_CODE_ACCESS_CHALLENGE = 11, RADIUS_CODE_STATUS_SERVER = 12, RADIUS_CODE_STATUS_CLIENT = 13, + RADIUS_CODE_DISCONNECT_REQUEST = 40, + RADIUS_CODE_DISCONNECT_ACK = 41, + RADIUS_CODE_DISCONNECT_NAK = 42, + RADIUS_CODE_COA_REQUEST = 43, + RADIUS_CODE_COA_ACK = 44, + RADIUS_CODE_COA_NAK = 45, RADIUS_CODE_RESERVED = 255 }; @@ -83,7 +89,8 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81, RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89, - RADIUS_ATTR_NAS_IPV6_ADDRESS = 95 + RADIUS_ATTR_NAS_IPV6_ADDRESS = 95, + RADIUS_ATTR_ERROR_CAUSE = 101 }; @@ -192,8 +199,15 @@ int radius_msg_finish(struct radius_msg *msg, const u8 *secret, size_t secret_len); int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_authenticator); +int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, + size_t secret_len, + const struct radius_hdr *req_hdr); void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, size_t secret_len); +int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, + size_t secret_len); +int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, + size_t secret_len); struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type, const u8 *data, size_t data_len); struct radius_msg * radius_msg_parse(const u8 *data, size_t len); @@ -268,4 +282,6 @@ void radius_free_class(struct radius_class_data *c); int radius_copy_class(struct radius_class_data *dst, const struct radius_class_data *src); +u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs); + #endif /* RADIUS_H */ diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index bcd471b7..1ee71316 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -505,7 +505,7 @@ static void radius_client_update_timeout(struct radius_client_data *radius) NULL); hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" - " %ld seconds\n", (long int) (first - now.sec)); + " %ld seconds", (long int) (first - now.sec)); } @@ -678,7 +678,7 @@ int radius_client_send(struct radius_client_data *radius, radius_client_list_add(radius, msg, msg_type, shared_secret, shared_secret_len, addr); - return res; + return 0; } diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c new file mode 100644 index 00000000..8ecfffc9 --- /dev/null +++ b/src/radius/radius_das.c @@ -0,0 +1,356 @@ +/* + * RADIUS Dynamic Authorization Server (DAS) (RFC 5176) + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <net/if.h> + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/ip_addr.h" +#include "radius.h" +#include "radius_das.h" + + +extern int wpa_debug_level; + + +struct radius_das_data { + int sock; + u8 *shared_secret; + size_t shared_secret_len; + struct hostapd_ip_addr client_addr; + unsigned int time_window; + int require_event_timestamp; + void *ctx; + enum radius_das_res (*disconnect)(void *ctx, + struct radius_das_attrs *attr); +}; + + +static struct radius_msg * radius_das_disconnect(struct radius_das_data *das, + struct radius_msg *msg, + const char *abuf, + int from_port) +{ + struct radius_hdr *hdr; + struct radius_msg *reply; + u8 allowed[] = { + RADIUS_ATTR_USER_NAME, + RADIUS_ATTR_CALLING_STATION_ID, + RADIUS_ATTR_ACCT_SESSION_ID, + RADIUS_ATTR_EVENT_TIMESTAMP, + RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + 0 + }; + int error = 405; + u8 attr; + enum radius_das_res res; + struct radius_das_attrs attrs; + u8 *buf; + size_t len; + char tmp[100]; + u8 sta_addr[ETH_ALEN]; + + hdr = radius_msg_get_hdr(msg); + + attr = radius_msg_find_unlisted_attr(msg, allowed); + if (attr) { + wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in " + "Disconnect-Request from %s:%d", attr, + abuf, from_port); + error = 401; + goto fail; + } + + os_memset(&attrs, 0, sizeof(attrs)); + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, + &buf, &len, NULL) == 0) { + if (len >= sizeof(tmp)) + len = sizeof(tmp) - 1; + os_memcpy(tmp, buf, len); + tmp[len] = '\0'; + if (hwaddr_aton2(tmp, sta_addr) < 0) { + wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id " + "'%s' from %s:%d", tmp, abuf, from_port); + error = 407; + goto fail; + } + attrs.sta_addr = sta_addr; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, + &buf, &len, NULL) == 0) { + attrs.user_name = buf; + attrs.user_name_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + &buf, &len, NULL) == 0) { + attrs.acct_session_id = buf; + attrs.acct_session_id_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) == 0) { + attrs.cui = buf; + attrs.cui_len = len; + } + + res = das->disconnect(das->ctx, &attrs); + switch (res) { + case RADIUS_DAS_NAS_MISMATCH: + wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d", + abuf, from_port); + error = 403; + break; + case RADIUS_DAS_SESSION_NOT_FOUND: + wpa_printf(MSG_INFO, "DAS: Session not found for request from " + "%s:%d", abuf, from_port); + error = 503; + break; + case RADIUS_DAS_SUCCESS: + error = 0; + break; + } + +fail: + reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK : + RADIUS_CODE_DISCONNECT_ACK, hdr->identifier); + if (reply == NULL) + return NULL; + + if (error) { + radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, + error); + } + + return reply; +} + + +static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct radius_das_data *das = eloop_ctx; + u8 buf[1500]; + union { + struct sockaddr_storage ss; + struct sockaddr_in sin; +#ifdef CONFIG_IPV6 + struct sockaddr_in6 sin6; +#endif /* CONFIG_IPV6 */ + } from; + char abuf[50]; + int from_port = 0; + socklen_t fromlen; + int len; + struct radius_msg *msg, *reply = NULL; + struct radius_hdr *hdr; + struct wpabuf *rbuf; + u32 val; + int res; + struct os_time now; + + fromlen = sizeof(from); + len = recvfrom(sock, buf, sizeof(buf), 0, + (struct sockaddr *) &from.ss, &fromlen); + if (len < 0) { + wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno)); + return; + } + + os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); + from_port = ntohs(from.sin.sin_port); + + wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d", + len, abuf, from_port); + if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) { + wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client"); + return; + } + + msg = radius_msg_parse(buf, len); + if (msg == NULL) { + wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet " + "from %s:%d failed", abuf, from_port); + return; + } + + if (wpa_debug_level <= MSG_MSGDUMP) + radius_msg_dump(msg); + + if (radius_msg_verify_das_req(msg, das->shared_secret, + das->shared_secret_len)) { + wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet " + "from %s:%d - drop", abuf, from_port); + goto fail; + } + + os_get_time(&now); + res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP, + (u8 *) &val, 4); + if (res == 4) { + u32 timestamp = ntohl(val); + if (abs(now.sec - timestamp) > das->time_window) { + wpa_printf(MSG_DEBUG, "DAS: Unacceptable " + "Event-Timestamp (%u; local time %u) in " + "packet from %s:%d - drop", + timestamp, (unsigned int) now.sec, + abuf, from_port); + goto fail; + } + } else if (das->require_event_timestamp) { + wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet " + "from %s:%d - drop", abuf, from_port); + goto fail; + } + + hdr = radius_msg_get_hdr(msg); + + switch (hdr->code) { + case RADIUS_CODE_DISCONNECT_REQUEST: + reply = radius_das_disconnect(das, msg, abuf, from_port); + break; + case RADIUS_CODE_COA_REQUEST: + /* TODO */ + reply = radius_msg_new(RADIUS_CODE_COA_NAK, + hdr->identifier); + if (reply == NULL) + break; + + /* Unsupported Service */ + radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, 405); + break; + default: + wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in " + "packet from %s:%d", + hdr->code, abuf, from_port); + } + + if (reply) { + wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port); + + if (!radius_msg_add_attr_int32(reply, + RADIUS_ATTR_EVENT_TIMESTAMP, + now.sec)) { + wpa_printf(MSG_DEBUG, "DAS: Failed to add " + "Event-Timestamp attribute"); + } + + if (radius_msg_finish_das_resp(reply, das->shared_secret, + das->shared_secret_len, hdr) < + 0) { + wpa_printf(MSG_DEBUG, "DAS: Failed to add " + "Message-Authenticator attribute"); + } + + if (wpa_debug_level <= MSG_MSGDUMP) + radius_msg_dump(reply); + + rbuf = radius_msg_get_buf(reply); + res = sendto(das->sock, wpabuf_head(rbuf), + wpabuf_len(rbuf), 0, + (struct sockaddr *) &from.ss, fromlen); + if (res < 0) { + wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s", + abuf, from_port, strerror(errno)); + } + } + +fail: + radius_msg_free(msg); + radius_msg_free(reply); +} + + +static int radius_das_open_socket(int port) +{ + int s; + struct sockaddr_in addr; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + close(s); + return -1; + } + + return s; +} + + +struct radius_das_data * +radius_das_init(struct radius_das_conf *conf) +{ + struct radius_das_data *das; + + if (conf->port == 0 || conf->shared_secret == NULL || + conf->client_addr == NULL) + return NULL; + + das = os_zalloc(sizeof(*das)); + if (das == NULL) + return NULL; + + das->time_window = conf->time_window; + das->require_event_timestamp = conf->require_event_timestamp; + das->ctx = conf->ctx; + das->disconnect = conf->disconnect; + + os_memcpy(&das->client_addr, conf->client_addr, + sizeof(das->client_addr)); + + das->shared_secret = os_malloc(conf->shared_secret_len); + if (das->shared_secret == NULL) { + radius_das_deinit(das); + return NULL; + } + os_memcpy(das->shared_secret, conf->shared_secret, + conf->shared_secret_len); + das->shared_secret_len = conf->shared_secret_len; + + das->sock = radius_das_open_socket(conf->port); + if (das->sock < 0) { + wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS " + "DAS"); + radius_das_deinit(das); + return NULL; + } + + if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL)) + { + radius_das_deinit(das); + return NULL; + } + + return das; +} + + +void radius_das_deinit(struct radius_das_data *das) +{ + if (das == NULL) + return; + + if (das->sock >= 0) { + eloop_unregister_read_sock(das->sock); + close(das->sock); + } + + os_free(das->shared_secret); + os_free(das); +} diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h new file mode 100644 index 00000000..738b18b0 --- /dev/null +++ b/src/radius/radius_das.h @@ -0,0 +1,47 @@ +/* + * RADIUS Dynamic Authorization Server (DAS) + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RADIUS_DAS_H +#define RADIUS_DAS_H + +struct radius_das_data; + +enum radius_das_res { + RADIUS_DAS_SUCCESS, + RADIUS_DAS_NAS_MISMATCH, + RADIUS_DAS_SESSION_NOT_FOUND +}; + +struct radius_das_attrs { + const u8 *sta_addr; + const u8 *user_name; + size_t user_name_len; + const u8 *acct_session_id; + size_t acct_session_id_len; + const u8 *cui; + size_t cui_len; +}; + +struct radius_das_conf { + int port; + const u8 *shared_secret; + size_t shared_secret_len; + const struct hostapd_ip_addr *client_addr; + unsigned int time_window; + int require_event_timestamp; + void *ctx; + enum radius_das_res (*disconnect)(void *ctx, + struct radius_das_attrs *attr); +}; + +struct radius_das_data * +radius_das_init(struct radius_das_conf *conf); + +void radius_das_deinit(struct radius_das_data *data); + +#endif /* RADIUS_DAS_H */ diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 6ebc37a9..7646ca88 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -2202,7 +2202,9 @@ int wpa_tdls_init(struct wpa_sm *sm) if (sm == NULL) return -1; - sm->l2_tdls = l2_packet_init(sm->ifname, sm->own_addr, + sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname : + sm->ifname, + sm->own_addr, ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls, sm, 0); if (sm->l2_tdls == NULL) { diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c index 7c9857f5..741b442c 100644 --- a/src/tls/libtommath.c +++ b/src/tls/libtommath.c @@ -66,11 +66,19 @@ #define OPT_CAST(x) +#ifdef __x86_64__ +typedef unsigned long mp_digit; +typedef unsigned long mp_word __attribute__((mode(TI))); + +#define DIGIT_BIT 60 +#define MP_64BIT +#else typedef unsigned long mp_digit; typedef u64 mp_word; #define DIGIT_BIT 28 #define MP_28BIT +#endif #define XMALLOC os_malloc diff --git a/src/utils/build_config.h b/src/utils/build_config.h index 04b8d98e..f9473885 100644 --- a/src/utils/build_config.h +++ b/src/utils/build_config.h @@ -47,28 +47,6 @@ #endif /* USE_INTERNAL_CRYPTO */ #endif /* CONFIG_WIN32_DEFAULTS */ -#ifdef __SYMBIAN32__ -#define OS_NO_C_LIB_DEFINES -#define CONFIG_ANSI_C_EXTRA -#define CONFIG_NO_WPA_MSG -#define CONFIG_NO_HOSTAPD_LOGGER -#define CONFIG_NO_STDOUT_DEBUG -#define CONFIG_BACKEND_FILE -#define CONFIG_INTERNAL_LIBTOMMATH -#define CONFIG_CRYPTO_INTERNAL -#define IEEE8021X_EAPOL -#define PKCS12_FUNCS -#define EAP_MD5 -#define EAP_TLS -#define EAP_MSCHAPv2 -#define EAP_PEAP -#define EAP_TTLS -#define EAP_GTC -#define EAP_OTP -#define EAP_LEAP -#define EAP_FAST -#endif /* __SYMBIAN32__ */ - #ifdef CONFIG_XCODE_DEFAULTS #define CONFIG_DRIVER_OSX #define CONFIG_BACKEND_FILE diff --git a/src/utils/common.h b/src/utils/common.h index 43ceb543..7f115efe 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -63,12 +63,6 @@ static inline unsigned int bswap_32(unsigned int v) #endif #endif /* CONFIG_TI_COMPILER */ -#ifdef __SYMBIAN32__ -#define __BIG_ENDIAN 4321 -#define __LITTLE_ENDIAN 1234 -#define __BYTE_ORDER __LITTLE_ENDIAN -#endif /* __SYMBIAN32__ */ - #ifdef CONFIG_NATIVE_WINDOWS #include <winsock.h> @@ -132,16 +126,6 @@ typedef unsigned char u8; #define WPA_TYPES_DEFINED #endif /* CONFIG_TI_COMPILER */ -#ifdef __SYMBIAN32__ -#define __REMOVE_PLATSEC_DIAGNOSTICS__ -#include <e32def.h> -typedef TUint64 u64; -typedef TUint32 u32; -typedef TUint16 u16; -typedef TUint8 u8; -#define WPA_TYPES_DEFINED -#endif /* __SYMBIAN32__ */ - #ifndef WPA_TYPES_DEFINED #ifdef CONFIG_USE_INTTYPES_H #include <inttypes.h> diff --git a/src/utils/eloop.c b/src/utils/eloop.c index 5691f154..bb324011 100644 --- a/src/utils/eloop.c +++ b/src/utils/eloop.c @@ -278,7 +278,7 @@ static int eloop_sock_table_set_fds(struct eloop_sock_table *readers, pollfds_map[fd] = pfd; nxt++; } - pfd->events |= POLLIN; + pfd->events |= POLLOUT; } } @@ -350,7 +350,8 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *readers, int max_pollfd_map) { if (eloop_sock_table_dispatch_table(readers, pollfds_map, - max_pollfd_map, POLLIN)) + max_pollfd_map, POLLIN | POLLERR | + POLLHUP)) return; /* pollfds may be invalid at this point */ if (eloop_sock_table_dispatch_table(writers, pollfds_map, diff --git a/src/utils/includes.h b/src/utils/includes.h index 787be186..6c6ec87d 100644 --- a/src/utils/includes.h +++ b/src/utils/includes.h @@ -41,9 +41,7 @@ #include <netinet/in.h> #include <arpa/inet.h> #ifndef __vxworks -#ifndef __SYMBIAN32__ #include <sys/uio.h> -#endif /* __SYMBIAN32__ */ #include <sys/time.h> #endif /* __vxworks */ #endif /* CONFIG_TI_COMPILER */ diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c index a97f9fa2..08510d01 100644 --- a/src/utils/pcsc_funcs.c +++ b/src/utils/pcsc_funcs.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2007, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -70,6 +70,9 @@ #define USIM_TLV_TOTAL_FILE_SIZE 0x81 #define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6 #define USIM_TLV_SHORT_FILE_ID 0x88 +#define USIM_TLV_SECURITY_ATTR_8B 0x8B +#define USIM_TLV_SECURITY_ATTR_8C 0x8C +#define USIM_TLV_SECURITY_ATTR_AB 0xAB #define USIM_PS_DO_TAG 0x90 @@ -81,6 +84,27 @@ #define CK_LEN 16 +/* GSM files + * File type in first octet: + * 3F = Master File + * 7F = Dedicated File + * 2F = Elementary File under the Master File + * 6F = Elementary File under a Dedicated File + */ +#define SCARD_FILE_MF 0x3F00 +#define SCARD_FILE_GSM_DF 0x7F20 +#define SCARD_FILE_UMTS_DF 0x7F50 +#define SCARD_FILE_GSM_EF_IMSI 0x6F07 +#define SCARD_FILE_GSM_EF_AD 0x6FAD +#define SCARD_FILE_EF_DIR 0x2F00 +#define SCARD_FILE_EF_ICCID 0x2FE2 +#define SCARD_FILE_EF_CK 0x6FE1 +#define SCARD_FILE_EF_IK 0x6FE2 + +#define SCARD_CHV1_OFFSET 13 +#define SCARD_CHV1_FLAG 0x80 + + typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types; struct scard_data { @@ -234,37 +258,60 @@ static int scard_read_record(struct scard_data *scard, static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, int *ps_do, int *file_len) { - unsigned char *pos, *end; - - if (ps_do) - *ps_do = -1; - if (file_len) - *file_len = -1; - - pos = buf; - end = pos + buf_len; - if (*pos != USIM_FSP_TEMPL_TAG) { - wpa_printf(MSG_DEBUG, "SCARD: file header did not " - "start with FSP template tag"); - return -1; - } - pos++; - if (pos >= end) - return -1; - if ((pos + pos[0]) < end) - end = pos + 1 + pos[0]; - pos++; - wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", - pos, end - pos); - - while (pos + 1 < end) { - wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV " - "0x%02x len=%d", pos[0], pos[1]); - if (pos + 2 + pos[1] > end) - break; + unsigned char *pos, *end; + + if (ps_do) + *ps_do = -1; + if (file_len) + *file_len = -1; + + pos = buf; + end = pos + buf_len; + if (*pos != USIM_FSP_TEMPL_TAG) { + wpa_printf(MSG_DEBUG, "SCARD: file header did not " + "start with FSP template tag"); + return -1; + } + pos++; + if (pos >= end) + return -1; + if ((pos + pos[0]) < end) + end = pos + 1 + pos[0]; + pos++; + wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", + pos, end - pos); + + while (pos + 1 < end) { + wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d", + pos[0], pos[1]); + if (pos + 2 + pos[1] > end) + break; - if (pos[0] == USIM_TLV_FILE_SIZE && - (pos[1] == 1 || pos[1] == 2) && file_len) { + switch (pos[0]) { + case USIM_TLV_FILE_DESC: + wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV", + pos + 2, pos[1]); + break; + case USIM_TLV_FILE_ID: + wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV", + pos + 2, pos[1]); + break; + case USIM_TLV_DF_NAME: + wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV", + pos + 2, pos[1]); + break; + case USIM_TLV_PROPR_INFO: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary " + "information TLV", pos + 2, pos[1]); + break; + case USIM_TLV_LIFE_CYCLE_STATUS: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status " + "Integer TLV", pos + 2, pos[1]); + break; + case USIM_TLV_FILE_SIZE: + wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV", + pos + 2, pos[1]); + if ((pos[1] == 1 || pos[1] == 2) && file_len) { if (pos[1] == 1) *file_len = (int) pos[2]; else @@ -273,21 +320,43 @@ static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, wpa_printf(MSG_DEBUG, "SCARD: file_size=%d", *file_len); } - - if (pos[0] == USIM_TLV_PIN_STATUS_TEMPLATE && - pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG && + break; + case USIM_TLV_TOTAL_FILE_SIZE: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV", + pos + 2, pos[1]); + break; + case USIM_TLV_PIN_STATUS_TEMPLATE: + wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template " + "DO TLV", pos + 2, pos[1]); + if (pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG && pos[3] >= 1 && ps_do) { wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x", pos[4]); *ps_do = (int) pos[4]; } + break; + case USIM_TLV_SHORT_FILE_ID: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File " + "Identifier (SFI) TLV", pos + 2, pos[1]); + break; + case USIM_TLV_SECURITY_ATTR_8B: + case USIM_TLV_SECURITY_ATTR_8C: + case USIM_TLV_SECURITY_ATTR_AB: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute " + "TLV", pos + 2, pos[1]); + break; + default: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV", + pos, 2 + pos[1]); + break; + } - pos += 2 + pos[1]; + pos += 2 + pos[1]; - if (pos == end) - return 0; - } - return -1; + if (pos == end) + return 0; + } + return -1; } @@ -328,7 +397,7 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, unsigned char rid[5]; unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ } *efdir; - unsigned char buf[100]; + unsigned char buf[127]; size_t blen; efdir = (struct efdir *) buf; @@ -417,6 +486,7 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, /** * scard_init - Initialize SIM/USIM connection using PC/SC * @sim_type: Allowed SIM types (SIM, USIM, or both) + * @reader: Reader name prefix to search for * Returns: Pointer to private data structure, or %NULL on failure * * This function is used to initialize SIM/USIM connection. PC/SC is used to @@ -425,10 +495,10 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, * access some of the card functions. Once the connection is not needed * anymore, scard_deinit() can be used to close it. */ -struct scard_data * scard_init(scard_sim_type sim_type) +struct scard_data * scard_init(scard_sim_type sim_type, const char *reader) { long ret; - unsigned long len; + unsigned long len, pos; struct scard_data *scard; #ifdef CONFIG_NATIVE_WINDOWS TCHAR *readers = NULL; @@ -482,18 +552,41 @@ struct scard_data * scard_init(scard_sim_type sim_type) "available."); goto failed; } - /* readers is a list of available reader. Last entry is terminated with - * double NUL. - * TODO: add support for selecting the reader; now just use the first - * one.. */ + wpa_hexdump_ascii(MSG_DEBUG, "SCARD: Readers", (u8 *) readers, len); + /* + * readers is a list of available readers. The last entry is terminated + * with double null. + */ + pos = 0; +#ifdef UNICODE + /* TODO */ +#else /* UNICODE */ + while (pos < len) { + if (reader == NULL || + os_strncmp(&readers[pos], reader, os_strlen(reader)) == 0) + break; + while (pos < len && readers[pos]) + pos++; + pos++; /* skip separating null */ + if (pos < len && readers[pos] == '\0') + pos = len; /* double null terminates list */ + } +#endif /* UNICODE */ + if (pos >= len) { + wpa_printf(MSG_WARNING, "SCARD: No reader with prefix '%s' " + "found", reader); + goto failed; + } + #ifdef UNICODE - wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", readers); + wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]); #else /* UNICODE */ - wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers); + wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]); #endif /* UNICODE */ - ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED, - SCARD_PROTOCOL_T0, &scard->card, &scard->protocol); + ret = SCardConnect(scard->ctx, &readers[pos], SCARD_SHARE_SHARED, + SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, + &scard->card, &scard->protocol); if (ret != SCARD_S_SUCCESS) { if (ret == (long) SCARD_E_NO_SMARTCARD) wpa_printf(MSG_INFO, "No smart card inserted."); @@ -582,7 +675,8 @@ struct scard_data * scard_init(scard_sim_type sim_type) } if (pin_needed) { scard->pin1_required = 1; - wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access"); + wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry " + "counter=%d)", scard_get_pin_retry_counter(scard)); } ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); @@ -939,6 +1033,46 @@ static int scard_verify_pin(struct scard_data *scard, const char *pin) } +int scard_get_pin_retry_counter(struct scard_data *scard) +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 }; + size_t len; + u16 val; + + wpa_printf(MSG_DEBUG, "SCARD: fetching PIN retry counter"); + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[4] = 0; /* Empty data */ + + len = sizeof(resp); + ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if (len != 2) { + wpa_printf(MSG_WARNING, "SCARD: failed to fetch PIN retry " + "counter"); + return -1; + } + + val = WPA_GET_BE16(resp); + if (val == 0x63c0 || val == 0x6983) { + wpa_printf(MSG_DEBUG, "SCARD: PIN has been blocked"); + return 0; + } + + if (val >= 0x63c0 && val <= 0x63cf) + return val & 0x000f; + + wpa_printf(MSG_DEBUG, "SCARD: Unexpected PIN retry counter response " + "value 0x%x", val); + return 0; +} + + /** * scard_get_imsi - Read IMSI from SIM/USIM card * @scard: Pointer to private data from scard_init() @@ -1285,3 +1419,9 @@ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response"); return -1; } + + +int scard_supports_umts(struct scard_data *scard) +{ + return scard->sim_type == SCARD_USIM; +} diff --git a/src/utils/pcsc_funcs.h b/src/utils/pcsc_funcs.h index 2fd36105..b4ebc998 100644 --- a/src/utils/pcsc_funcs.h +++ b/src/utils/pcsc_funcs.h @@ -1,6 +1,6 @@ /* * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2006, 2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,26 +9,6 @@ #ifndef PCSC_FUNCS_H #define PCSC_FUNCS_H -/* GSM files - * File type in first octet: - * 3F = Master File - * 7F = Dedicated File - * 2F = Elementary File under the Master File - * 6F = Elementary File under a Dedicated File - */ -#define SCARD_FILE_MF 0x3F00 -#define SCARD_FILE_GSM_DF 0x7F20 -#define SCARD_FILE_UMTS_DF 0x7F50 -#define SCARD_FILE_GSM_EF_IMSI 0x6F07 -#define SCARD_FILE_GSM_EF_AD 0x6FAD -#define SCARD_FILE_EF_DIR 0x2F00 -#define SCARD_FILE_EF_ICCID 0x2FE2 -#define SCARD_FILE_EF_CK 0x6FE1 -#define SCARD_FILE_EF_IK 0x6FE2 - -#define SCARD_CHV1_OFFSET 13 -#define SCARD_CHV1_FLAG 0x80 - typedef enum { SCARD_GSM_SIM_ONLY, SCARD_USIM_ONLY, @@ -37,7 +17,7 @@ typedef enum { #ifdef PCSC_FUNCS -struct scard_data * scard_init(scard_sim_type sim_type); +struct scard_data * scard_init(scard_sim_type sim_type, const char *reader); void scard_deinit(struct scard_data *scard); int scard_set_pin(struct scard_data *scard, const char *pin); @@ -49,15 +29,20 @@ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, const unsigned char *autn, unsigned char *res, size_t *res_len, unsigned char *ik, unsigned char *ck, unsigned char *auts); +int scard_get_pin_retry_counter(struct scard_data *scard); +int scard_supports_umts(struct scard_data *scard); #else /* PCSC_FUNCS */ -#define scard_init(s) NULL +#define scard_init(s, r) NULL #define scard_deinit(s) do { } while (0) #define scard_set_pin(s, p) -1 #define scard_get_imsi(s, i, l) -1 +#define scard_get_mnc_len(s) -1 #define scard_gsm_auth(s, r, s2, k) -1 #define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1 +#define scard_get_pin_retry_counter(s) -1 +#define scard_supports_umts(s) 0 #endif /* PCSC_FUNCS */ diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c index a8b9106b..5511ef19 100644 --- a/src/utils/wpa_debug.c +++ b/src/utils/wpa_debug.c @@ -16,6 +16,18 @@ static int wpa_debug_syslog = 0; #endif /* CONFIG_DEBUG_SYSLOG */ +#ifdef CONFIG_DEBUG_LINUX_TRACING +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#include <stdio.h> + +static FILE *wpa_debug_tracing_file = NULL; + +#define WPAS_TRACE_PFX "wpas <%d>: " +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + int wpa_debug_level = MSG_INFO; int wpa_debug_show_keys = 0; @@ -30,25 +42,18 @@ int wpa_debug_timestamp = 0; #define ANDROID_LOG_NAME "wpa_supplicant" #endif /* ANDROID_LOG_NAME */ -void android_printf(int level, char *format, ...) +static int wpa_to_android_level(int level) { - if (level >= wpa_debug_level) { - va_list ap; - if (level == MSG_ERROR) - level = ANDROID_LOG_ERROR; - else if (level == MSG_WARNING) - level = ANDROID_LOG_WARN; - else if (level == MSG_INFO) - level = ANDROID_LOG_INFO; - else - level = ANDROID_LOG_DEBUG; - va_start(ap, format); - __android_log_vprint(level, ANDROID_LOG_NAME, format, ap); - va_end(ap); - } + if (level == MSG_ERROR) + return ANDROID_LOG_ERROR; + if (level == MSG_WARNING) + return ANDROID_LOG_WARN; + if (level == MSG_INFO) + return ANDROID_LOG_INFO; + return ANDROID_LOG_DEBUG; } -#else /* CONFIG_ANDROID_LOG */ +#endif /* CONFIG_ANDROID_LOG */ #ifndef CONFIG_NO_STDOUT_DEBUG @@ -59,6 +64,7 @@ static FILE *out_file = NULL; void wpa_debug_print_timestamp(void) { +#ifndef CONFIG_ANDROID_LOG struct os_time tv; if (!wpa_debug_timestamp) @@ -72,6 +78,7 @@ void wpa_debug_print_timestamp(void) } else #endif /* CONFIG_DEBUG_FILE */ printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec); +#endif /* CONFIG_ANDROID_LOG */ } @@ -112,6 +119,77 @@ static int syslog_priority(int level) #endif /* CONFIG_DEBUG_SYSLOG */ +#ifdef CONFIG_DEBUG_LINUX_TRACING + +int wpa_debug_open_linux_tracing(void) +{ + int mounts, trace_fd; + char buf[4096] = {}; + ssize_t buflen; + char *line, *tmp1, *path = NULL; + + mounts = open("/proc/mounts", O_RDONLY); + if (mounts < 0) { + printf("no /proc/mounts\n"); + return -1; + } + + buflen = read(mounts, buf, sizeof(buf) - 1); + close(mounts); + if (buflen < 0) { + printf("failed to read /proc/mounts\n"); + return -1; + } + + line = strtok_r(buf, "\n", &tmp1); + while (line) { + char *tmp2, *tmp_path, *fstype; + /* "<dev> <mountpoint> <fs type> ..." */ + strtok_r(line, " ", &tmp2); + tmp_path = strtok_r(NULL, " ", &tmp2); + fstype = strtok_r(NULL, " ", &tmp2); + if (strcmp(fstype, "debugfs") == 0) { + path = tmp_path; + break; + } + + line = strtok_r(NULL, "\n", &tmp1); + } + + if (path == NULL) { + printf("debugfs mountpoint not found\n"); + return -1; + } + + snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path); + + trace_fd = open(buf, O_WRONLY); + if (trace_fd < 0) { + printf("failed to open trace_marker file\n"); + return -1; + } + wpa_debug_tracing_file = fdopen(trace_fd, "w"); + if (wpa_debug_tracing_file == NULL) { + close(trace_fd); + printf("failed to fdopen()\n"); + return -1; + } + + return 0; +} + + +void wpa_debug_close_linux_tracing(void) +{ + if (wpa_debug_tracing_file == NULL) + return; + fclose(wpa_debug_tracing_file); + wpa_debug_tracing_file = NULL; +} + +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + + /** * wpa_printf - conditional printf * @level: priority level (MSG_*) of the message @@ -129,6 +207,10 @@ void wpa_printf(int level, const char *fmt, ...) va_start(ap, fmt); if (level >= wpa_debug_level) { +#ifdef CONFIG_ANDROID_LOG + __android_log_vprint(wpa_to_android_level(level), + ANDROID_LOG_NAME, fmt, ap); +#else /* CONFIG_ANDROID_LOG */ #ifdef CONFIG_DEBUG_SYSLOG if (wpa_debug_syslog) { vsyslog(syslog_priority(level), fmt, ap); @@ -149,8 +231,20 @@ void wpa_printf(int level, const char *fmt, ...) #ifdef CONFIG_DEBUG_SYSLOG } #endif /* CONFIG_DEBUG_SYSLOG */ +#endif /* CONFIG_ANDROID_LOG */ } va_end(ap); + +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (wpa_debug_tracing_file != NULL) { + va_start(ap, fmt); + fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level); + vfprintf(wpa_debug_tracing_file, fmt, ap); + fprintf(wpa_debug_tracing_file, "\n"); + fflush(wpa_debug_tracing_file); + va_end(ap); + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ } @@ -158,8 +252,65 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, size_t len, int show) { size_t i; + +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (wpa_debug_tracing_file != NULL) { + fprintf(wpa_debug_tracing_file, + WPAS_TRACE_PFX "%s - hexdump(len=%lu):", + level, title, (unsigned long) len); + if (buf == NULL) { + fprintf(wpa_debug_tracing_file, " [NULL]\n"); + } else if (!show) { + fprintf(wpa_debug_tracing_file, " [REMOVED]\n"); + } else { + for (i = 0; i < len; i++) + fprintf(wpa_debug_tracing_file, + " %02x", buf[i]); + } + fflush(wpa_debug_tracing_file); + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + if (level < wpa_debug_level) return; +#ifdef CONFIG_ANDROID_LOG + { + const char *display; + char *strbuf = NULL; + size_t slen = len; + if (buf == NULL) { + display = " [NULL]"; + } else if (len == 0) { + display = ""; + } else if (show && len) { + /* Limit debug message length for Android log */ + if (slen > 32) + slen = 32; + strbuf = os_malloc(1 + 3 * slen); + if (strbuf == NULL) { + wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to " + "allocate message buffer"); + return; + } + + for (i = 0; i < slen; i++) + os_snprintf(&strbuf[i * 3], 4, " %02x", + buf[i]); + + display = strbuf; + } else { + display = " [REMOVED]"; + } + + __android_log_print(wpa_to_android_level(level), + ANDROID_LOG_NAME, + "%s - hexdump(len=%lu):%s%s", + title, (long unsigned int) len, display, + len > slen ? " ..." : ""); + os_free(strbuf); + return; + } +#else /* CONFIG_ANDROID_LOG */ #ifdef CONFIG_DEBUG_SYSLOG if (wpa_debug_syslog) { const char *display; @@ -187,7 +338,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, } syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s", - title, len, display); + title, (unsigned long) len, display); os_free(strbuf); return; } @@ -221,6 +372,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, #ifdef CONFIG_DEBUG_FILE } #endif /* CONFIG_DEBUG_FILE */ +#endif /* CONFIG_ANDROID_LOG */ } void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len) @@ -242,8 +394,30 @@ static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, const u8 *pos = buf; const size_t line_len = 16; +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (wpa_debug_tracing_file != NULL) { + fprintf(wpa_debug_tracing_file, + WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):", + level, title, (unsigned long) len); + if (buf == NULL) { + fprintf(wpa_debug_tracing_file, " [NULL]\n"); + } else if (!show) { + fprintf(wpa_debug_tracing_file, " [REMOVED]\n"); + } else { + /* can do ascii processing in userspace */ + for (i = 0; i < len; i++) + fprintf(wpa_debug_tracing_file, + " %02x", buf[i]); + } + fflush(wpa_debug_tracing_file); + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + if (level < wpa_debug_level) return; +#ifdef CONFIG_ANDROID_LOG + _wpa_hexdump(level, title, buf, len, show); +#else /* CONFIG_ANDROID_LOG */ wpa_debug_print_timestamp(); #ifdef CONFIG_DEBUG_FILE if (out_file) { @@ -317,6 +491,7 @@ static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, #ifdef CONFIG_DEBUG_FILE } #endif /* CONFIG_DEBUG_FILE */ +#endif /* CONFIG_ANDROID_LOG */ } @@ -398,7 +573,6 @@ void wpa_debug_close_file(void) #endif /* CONFIG_NO_STDOUT_DEBUG */ -#endif /* CONFIG_ANDROID_LOG */ #ifndef CONFIG_NO_WPA_MSG static wpa_msg_cb_func wpa_msg_cb = NULL; diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h index bbef49b3..339c749c 100644 --- a/src/utils/wpa_debug.h +++ b/src/utils/wpa_debug.h @@ -18,32 +18,6 @@ enum { MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; -#ifdef CONFIG_ANDROID_LOG - -#define wpa_debug_print_timestamp() do {} while (0) -#define wpa_hexdump(...) do {} while (0) -#define wpa_hexdump_key(...) do {} while (0) -#define wpa_hexdump_buf(l,t,b) do {} while (0) -#define wpa_hexdump_buf_key(l,t,b) do {} while (0) -#define wpa_hexdump_ascii(...) do {} while (0) -#define wpa_hexdump_ascii_key(...) do {} while (0) -#define wpa_debug_open_file(...) do {} while (0) -#define wpa_debug_close_file() do {} while (0) -#define wpa_dbg(...) do {} while (0) - -static inline int wpa_debug_reopen_file(void) -{ - return 0; -} - - -void android_printf(int level, char *format, ...) -PRINTF_FORMAT(2, 3); - -#define wpa_printf android_printf - -#else /* CONFIG_ANDROID_LOG */ - #ifdef CONFIG_NO_STDOUT_DEBUG #define wpa_debug_print_timestamp() do { } while (0) @@ -177,8 +151,6 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, #endif /* CONFIG_NO_STDOUT_DEBUG */ -#endif /* CONFIG_ANDROID_LOG */ - #ifdef CONFIG_NO_WPA_MSG #define wpa_msg(args...) do { } while (0) @@ -283,6 +255,24 @@ static inline void wpa_debug_close_syslog(void) #endif /* CONFIG_DEBUG_SYSLOG */ +#ifdef CONFIG_DEBUG_LINUX_TRACING + +int wpa_debug_open_linux_tracing(void); +void wpa_debug_close_linux_tracing(void); + +#else /* CONFIG_DEBUG_LINUX_TRACING */ + +static inline int wpa_debug_open_linux_tracing(void) +{ + return 0; +} + +static inline void wpa_debug_close_linux_tracing(void) +{ +} + +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + #ifdef EAPOL_TEST #define WPA_ASSERT(a) \ diff --git a/src/wps/ndef.c b/src/wps/ndef.c index 9baec7f4..7630ecbc 100644 --- a/src/wps/ndef.c +++ b/src/wps/ndef.c @@ -1,16 +1,10 @@ /* * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup * Reference is "NFCForum-TS-NDEF_1.0 2006-07-24". - * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> + * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -26,9 +20,9 @@ #define FLAG_TNF_RFC2046 (0x02) struct ndef_record { - u8 *type; - u8 *id; - u8 *payload; + const u8 *type; + const u8 *id; + const u8 *payload; u8 type_length; u8 id_length; u32 payload_length; @@ -37,9 +31,10 @@ struct ndef_record { static char wifi_handover_type[] = "application/vnd.wfa.wsc"; -static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record) +static int ndef_parse_record(const u8 *data, u32 size, + struct ndef_record *record) { - u8 *pos = data + 1; + const u8 *pos = data + 1; if (size < 2) return -1; @@ -78,12 +73,12 @@ static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record) } -static struct wpabuf * ndef_parse_records(struct wpabuf *buf, +static struct wpabuf * ndef_parse_records(const struct wpabuf *buf, int (*filter)(struct ndef_record *)) { struct ndef_record record; int len = wpabuf_len(buf); - u8 *data = wpabuf_mhead(buf); + const u8 *data = wpabuf_head(buf); while (len > 0) { if (ndef_parse_record(data, len, &record) < 0) { @@ -103,13 +98,14 @@ static struct wpabuf * ndef_parse_records(struct wpabuf *buf, static struct wpabuf * ndef_build_record(u8 flags, void *type, u8 type_length, void *id, - u8 id_length, void *payload, - u32 payload_length) + u8 id_length, + const struct wpabuf *payload) { struct wpabuf *record; size_t total_len; int short_record; u8 local_flag; + size_t payload_length = wpabuf_len(payload); short_record = payload_length < 256 ? 1 : 0; @@ -144,7 +140,7 @@ static struct wpabuf * ndef_build_record(u8 flags, void *type, wpabuf_put_u8(record, id_length); wpabuf_put_data(record, type, type_length); wpabuf_put_data(record, id, id_length); - wpabuf_put_data(record, payload, payload_length); + wpabuf_put_buf(record, payload); return record; } @@ -160,16 +156,15 @@ static int wifi_filter(struct ndef_record *record) } -struct wpabuf * ndef_parse_wifi(struct wpabuf *buf) +struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf) { return ndef_parse_records(buf, wifi_filter); } -struct wpabuf * ndef_build_wifi(struct wpabuf *buf) +struct wpabuf * ndef_build_wifi(const struct wpabuf *buf) { return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | FLAG_TNF_RFC2046, wifi_handover_type, - os_strlen(wifi_handover_type), NULL, 0, - wpabuf_mhead(buf), wpabuf_len(buf)); + os_strlen(wifi_handover_type), NULL, 0, buf); } diff --git a/src/wps/wps.c b/src/wps/wps.c index 9422c713..5453962a 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -56,6 +56,23 @@ struct wps_data * wps_init(const struct wps_config *cfg) data->dev_password_len = cfg->pin_len; } +#ifdef CONFIG_WPS_NFC + if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) { + data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id; + os_free(data->dev_password); + data->dev_password = + os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw)); + if (data->dev_password == NULL) { + os_free(data); + return NULL; + } + os_memcpy(data->dev_password, + wpabuf_head(cfg->wps->ap_nfc_dev_pw), + wpabuf_len(cfg->wps->ap_nfc_dev_pw)); + data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw); + } +#endif /* CONFIG_WPS_NFC */ + data->pbc = cfg->pbc; if (cfg->pbc) { /* Use special PIN '00000000' for PBC */ @@ -118,6 +135,12 @@ struct wps_data * wps_init(const struct wps_config *cfg) */ void wps_deinit(struct wps_data *data) { +#ifdef CONFIG_WPS_NFC + if (data->registrar && data->nfc_pw_token) + wps_registrar_remove_nfc_pw_token(data->wps->registrar, + data->nfc_pw_token); +#endif /* CONFIG_WPS_NFC */ + if (data->wps_pin_revealed) { wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and " "negotiation failed"); @@ -136,6 +159,7 @@ void wps_deinit(struct wps_data *data) wps_device_data_free(&data->peer_dev); os_free(data->new_ap_settings); dh5_free(data->dh_ctx); + os_free(data->nfc_pw_token); os_free(data); } @@ -431,7 +455,8 @@ struct wpabuf * wps_build_assoc_resp_ie(void) /** * wps_build_probe_req_ie - Build WPS IE for Probe Request - * @pbc: Whether searching for PBC mode APs + * @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for + * most other use cases) * @dev: Device attributes * @uuid: Own UUID * @req_type: Value for Request Type attribute @@ -442,7 +467,7 @@ struct wpabuf * wps_build_assoc_resp_ie(void) * * The caller is responsible for freeing the buffer. */ -struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, +struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, const u8 *uuid, enum wps_request_type req_type, unsigned int num_req_dev_types, @@ -464,8 +489,7 @@ struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, wps_build_rf_bands(dev, ie) || wps_build_assoc_state(NULL, ie) || wps_build_config_error(ie, WPS_CFG_NO_ERROR) || - wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON : - DEV_PW_DEFAULT) || + wps_build_dev_password_id(ie, pw_id) || #ifdef CONFIG_WPS2 wps_build_manufacturer(dev, ie) || wps_build_model_name(dev, ie) || diff --git a/src/wps/wps.h b/src/wps/wps.h index 22e029fb..c45b68cf 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,6 +27,7 @@ enum wsc_op_code { struct wps_registrar; struct upnp_wps_device_sm; struct wps_er; +struct wps_parse_attr; /** * struct wps_credential - WPS Credential @@ -94,6 +95,7 @@ struct wps_device_data { u32 os_version; u8 rf_bands; u16 config_methods; + struct wpabuf *vendor_ext_m1; struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; int p2p; @@ -238,7 +240,7 @@ int wps_is_20(const struct wpabuf *msg); struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type); struct wpabuf * wps_build_assoc_resp_ie(void); -struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, +struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, const u8 *uuid, enum wps_request_type req_type, unsigned int num_req_dev_types, @@ -294,12 +296,15 @@ struct wps_registrar_config { * @ctx: Higher layer context data (cb_ctx) * @mac_addr: MAC address of the Enrollee * @uuid_e: UUID-E of the Enrollee + * @dev_pw: Device Password (PIN) used during registration + * @dev_pw_len: Length of dev_pw in octets * * This callback is called whenever an Enrollee completes registration * successfully. */ void (*reg_success_cb)(void *ctx, const u8 *mac_addr, - const u8 *uuid_e); + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len); /** * set_sel_reg_cb - Callback for reporting selected registrar changes @@ -752,6 +757,11 @@ struct wps_context { /* Pending messages from UPnP PutWLANResponse */ struct upnp_pending_message *upnp_msgs; + + u16 ap_nfc_dev_pw_id; + struct wpabuf *ap_nfc_dh_pubkey; + struct wpabuf *ap_nfc_dh_privkey; + struct wpabuf *ap_nfc_dev_pw; }; struct oob_device_data { @@ -783,7 +793,8 @@ int wps_registrar_wps_cancel(struct wps_registrar *reg); int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid); int wps_registrar_button_pushed(struct wps_registrar *reg, const u8 *p2p_dev_addr); -void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e); +void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e, + const u8 *dev_pw, size_t dev_pw_len); void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, const struct wpabuf *wps_data, int p2p_wildcard); @@ -792,6 +803,12 @@ int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, char *buf, size_t buflen); int wps_registrar_config_ap(struct wps_registrar *reg, struct wps_credential *cred); +int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, + const u8 *pubkey_hash, u16 pw_id, + const u8 *dev_pw, size_t dev_pw_len); +int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, + const u8 *oob_dev_pw, + size_t oob_dev_pw_len); int wps_build_credential_wrap(struct wpabuf *msg, const struct wps_credential *cred); @@ -799,6 +816,7 @@ int wps_build_credential_wrap(struct wpabuf *msg, unsigned int wps_pin_checksum(unsigned int pin); unsigned int wps_pin_valid(unsigned int pin); unsigned int wps_generate_pin(void); +int wps_pin_str_valid(const char *pin); void wps_free_pending_msgs(struct upnp_pending_message *msgs); struct oob_device_data * wps_get_oob_device(char *device_type); @@ -806,6 +824,8 @@ struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name); int wps_get_oob_method(char *method); int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev, int registrar); +struct wpabuf * wps_get_oob_cred(struct wps_context *wps); +int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr); int wps_attr_text(struct wpabuf *data, char *buf, char *end); struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname, @@ -821,12 +841,23 @@ int wps_er_set_config(struct wps_er *er, const u8 *uuid, const struct wps_credential *cred); int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, size_t pin_len, const struct wps_credential *cred); +struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid); int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]); char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf, size_t buf_len); void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid); u16 wps_config_methods_str2bin(const char *str); +struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, + const struct wpabuf *pubkey, + const struct wpabuf *dev_pw); +struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, + struct wpabuf **privkey, + struct wpabuf **dev_pw); + +/* ndef.c */ +struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf); +struct wpabuf * ndef_build_wifi(const struct wpabuf *buf); #ifdef CONFIG_WPS_STRICT int wps_validate_beacon(const struct wpabuf *wps_ie); diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c index 753083d7..9be30b98 100644 --- a/src/wps/wps_attr_build.c +++ b/src/wps/wps_attr_build.c @@ -30,6 +30,14 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) wps->dh_ctx = wps->wps->dh_ctx; wps->wps->dh_ctx = NULL; pubkey = wpabuf_dup(wps->wps->dh_pubkey); +#ifdef CONFIG_WPS_NFC + } else if (wps->dev_pw_id >= 0x10 && wps->wps->ap && + wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) { + wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys"); + wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey); + pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey); + wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey); +#endif /* CONFIG_WPS_NFC */ } else { wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys"); wps->dh_privkey = NULL; @@ -340,19 +348,34 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, #ifdef CONFIG_WPS_OOB -int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps) +int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, + const struct wpabuf *pubkey, const u8 *dev_pw, + size_t dev_pw_len) { size_t hash_len; const u8 *addr[1]; u8 pubkey_hash[WPS_HASH_LEN]; + + addr[0] = wpabuf_head(pubkey); + hash_len = wpabuf_len(pubkey); + sha256_vector(1, addr, &hash_len, pubkey_hash); + + wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD); + wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len); + wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); + wpabuf_put_be16(msg, dev_pw_id); + wpabuf_put_data(msg, dev_pw, dev_pw_len); + + return 0; +} + + +int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps) +{ u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN]; wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password"); - addr[0] = wpabuf_head(wps->dh_pubkey); - hash_len = wpabuf_len(wps->dh_pubkey); - sha256_vector(1, addr, &hash_len, pubkey_hash); - if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) { wpa_printf(MSG_ERROR, "WPS: device password id " "generation error"); @@ -367,19 +390,15 @@ int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps) return -1; } - wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD); - wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN); - wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); - wpabuf_put_be16(msg, wps->oob_dev_pw_id); - wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN); - wpa_snprintf_hex_uppercase( wpabuf_put(wps->oob_conf.dev_password, wpabuf_size(wps->oob_conf.dev_password)), wpabuf_size(wps->oob_conf.dev_password), dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN); - return 0; + return wps_build_oob_dev_pw(msg, wps->oob_dev_pw_id, wps->dh_pubkey, + dev_password_bin, + WPS_OOB_DEVICE_PASSWORD_LEN); } #endif /* CONFIG_WPS_OOB */ diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c index f5ad4037..5aa9b004 100644 --- a/src/wps/wps_attr_parse.c +++ b/src/wps/wps_attr_parse.c @@ -9,7 +9,8 @@ #include "includes.h" #include "common.h" -#include "wps_i.h" +#include "wps_defs.h" +#include "wps_attr_parse.h" #ifndef CONFIG_WPS_STRICT #define WPS_WORKAROUNDS @@ -262,12 +263,16 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, attr->dev_password_id = pos; break; case ATTR_OOB_DEVICE_PASSWORD: - if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) { + if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_MIN_LEN || + len > WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN) { wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device " "Password length %u", len); return -1; } attr->oob_dev_password = pos; + attr->oob_dev_password_len = len; break; case ATTR_OS_VERSION: if (len != 4) { diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h new file mode 100644 index 00000000..332e966a --- /dev/null +++ b/src/wps/wps_attr_parse.h @@ -0,0 +1,107 @@ +/* + * Wi-Fi Protected Setup - attribute parsing + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPS_ATTR_PARSE_H +#define WPS_ATTR_PARSE_H + +#include "wps.h" + +struct wps_parse_attr { + /* fixed length fields */ + const u8 *version; /* 1 octet */ + const u8 *version2; /* 1 octet */ + const u8 *msg_type; /* 1 octet */ + const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */ + const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */ + const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */ + const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */ + const u8 *auth_type_flags; /* 2 octets */ + const u8 *encr_type_flags; /* 2 octets */ + const u8 *conn_type_flags; /* 1 octet */ + const u8 *config_methods; /* 2 octets */ + const u8 *sel_reg_config_methods; /* 2 octets */ + const u8 *primary_dev_type; /* 8 octets */ + const u8 *rf_bands; /* 1 octet */ + const u8 *assoc_state; /* 2 octets */ + const u8 *config_error; /* 2 octets */ + const u8 *dev_password_id; /* 2 octets */ + const u8 *os_version; /* 4 octets */ + const u8 *wps_state; /* 1 octet */ + const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */ + const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */ + const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */ + const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */ + const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */ + const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ + const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */ + const u8 *auth_type; /* 2 octets */ + const u8 *encr_type; /* 2 octets */ + const u8 *network_idx; /* 1 octet */ + const u8 *network_key_idx; /* 1 octet */ + const u8 *mac_addr; /* ETH_ALEN (6) octets */ + const u8 *key_prov_auto; /* 1 octet (Bool) */ + const u8 *dot1x_enabled; /* 1 octet (Bool) */ + const u8 *selected_registrar; /* 1 octet (Bool) */ + const u8 *request_type; /* 1 octet */ + const u8 *response_type; /* 1 octet */ + const u8 *ap_setup_locked; /* 1 octet */ + const u8 *settings_delay_time; /* 1 octet */ + const u8 *network_key_shareable; /* 1 octet (Bool) */ + const u8 *request_to_enroll; /* 1 octet (Bool) */ + + /* variable length fields */ + const u8 *manufacturer; + size_t manufacturer_len; + const u8 *model_name; + size_t model_name_len; + const u8 *model_number; + size_t model_number_len; + const u8 *serial_number; + size_t serial_number_len; + const u8 *dev_name; + size_t dev_name_len; + const u8 *public_key; + size_t public_key_len; + const u8 *encr_settings; + size_t encr_settings_len; + const u8 *ssid; /* <= 32 octets */ + size_t ssid_len; + const u8 *network_key; /* <= 64 octets */ + size_t network_key_len; + const u8 *eap_type; /* <= 8 octets */ + size_t eap_type_len; + const u8 *eap_identity; /* <= 64 octets */ + size_t eap_identity_len; + const u8 *authorized_macs; /* <= 30 octets */ + size_t authorized_macs_len; + const u8 *sec_dev_type_list; /* <= 128 octets */ + size_t sec_dev_type_list_len; + const u8 *oob_dev_password; /* 38..54 octets */ + size_t oob_dev_password_len; + + /* attributes that can occur multiple times */ +#define MAX_CRED_COUNT 10 + const u8 *cred[MAX_CRED_COUNT]; + size_t cred_len[MAX_CRED_COUNT]; + size_t num_cred; + +#define MAX_REQ_DEV_TYPE_COUNT 10 + const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; + size_t num_req_dev_type; + + const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT]; + size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT]; + size_t num_vendor_ext; +}; + +int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); + +#endif /* WPS_ATTR_PARSE_H */ diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index 2dff4b25..5a8817f2 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - common functionality - * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -249,6 +249,22 @@ unsigned int wps_generate_pin(void) } +int wps_pin_str_valid(const char *pin) +{ + const char *p; + size_t len; + + p = pin; + while (*p >= '0' && *p <= '9') + p++; + if (*p != '\0') + return 0; + + len = p - pin; + return len == 4 || len == 8; +} + + void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, u16 config_error, u16 error_indication) { @@ -308,7 +324,7 @@ void wps_pbc_timeout_event(struct wps_context *wps) #ifdef CONFIG_WPS_OOB -static struct wpabuf * wps_get_oob_cred(struct wps_context *wps) +struct wpabuf * wps_get_oob_cred(struct wps_context *wps) { struct wps_data data; struct wpabuf *plain; @@ -335,11 +351,35 @@ static struct wpabuf * wps_get_oob_cred(struct wps_context *wps) } +struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, + const struct wpabuf *pubkey, + const struct wpabuf *dev_pw) +{ + struct wpabuf *data; + + data = wpabuf_alloc(200); + if (data == NULL) + return NULL; + + if (wps_build_version(data) || + wps_build_oob_dev_pw(data, dev_pw_id, pubkey, + wpabuf_head(dev_pw), wpabuf_len(dev_pw)) || + wps_build_wfa_ext(data, 0, NULL, 0)) { + wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password " + "token"); + wpabuf_free(data); + return NULL; + } + + return data; +} + + static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps) { struct wpabuf *data; - data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN); + data = wpabuf_alloc(200); if (data == NULL) { wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " "device password attribute"); @@ -375,6 +415,7 @@ static int wps_parse_oob_dev_pwd(struct wps_context *wps, struct oob_conf_data *oob_conf = &wps->oob_conf; struct wps_parse_attr attr; const u8 *pos; + size_t pw_len; if (wps_parse_msg(data, &attr) < 0 || attr.oob_dev_password == NULL) { @@ -384,6 +425,7 @@ static int wps_parse_oob_dev_pwd(struct wps_context *wps, pos = attr.oob_dev_password; + wpabuf_free(oob_conf->pubkey_hash); oob_conf->pubkey_hash = wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN); if (oob_conf->pubkey_hash == NULL) { @@ -396,39 +438,32 @@ static int wps_parse_oob_dev_pwd(struct wps_context *wps, wps->oob_dev_pw_id = WPA_GET_BE16(pos); pos += sizeof(wps->oob_dev_pw_id); - oob_conf->dev_password = - wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1); + pw_len = attr.oob_dev_password_len - WPS_OOB_PUBKEY_HASH_LEN - 2; + oob_conf->dev_password = wpabuf_alloc(pw_len * 2 + 1); if (oob_conf->dev_password == NULL) { wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " "device password"); return -1; } wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password, - wpabuf_size(oob_conf->dev_password)), - wpabuf_size(oob_conf->dev_password), pos, - WPS_OOB_DEVICE_PASSWORD_LEN); + pw_len * 2 + 1), + pw_len * 2 + 1, pos, pw_len); return 0; } -static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data) +int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr) { struct wpabuf msg; - struct wps_parse_attr attr; size_t i; - if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) { - wpa_printf(MSG_ERROR, "WPS: OOB credential not found"); - return -1; - } - - for (i = 0; i < attr.num_cred; i++) { + for (i = 0; i < attr->num_cred; i++) { struct wps_credential local_cred; struct wps_parse_attr cattr; os_memset(&local_cred, 0, sizeof(local_cred)); - wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]); + wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]); if (wps_parse_msg(&msg, &cattr) < 0 || wps_process_cred(&cattr, &local_cred)) { wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB " @@ -442,6 +477,19 @@ static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data) } +static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data) +{ + struct wps_parse_attr attr; + + if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) { + wpa_printf(MSG_ERROR, "WPS: OOB credential not found"); + return -1; + } + + return wps_oob_use_cred(wps, &attr); +} + + int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev, int registrar) { @@ -695,3 +743,53 @@ struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) return msg; } + + +#ifdef CONFIG_WPS_NFC +struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, + struct wpabuf **privkey, + struct wpabuf **dev_pw) +{ + struct wpabuf *priv = NULL, *pub = NULL, *pw, *ret; + void *dh_ctx; + u16 val; + + pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN); + if (pw == NULL) + return NULL; + + if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN), + WPS_OOB_DEVICE_PASSWORD_LEN) || + random_get_bytes((u8 *) &val, sizeof(val))) { + wpabuf_free(pw); + return NULL; + } + + dh_ctx = dh5_init(&priv, &pub); + if (dh_ctx == NULL) { + wpabuf_free(pw); + return NULL; + } + dh5_free(dh_ctx); + + *id = 0x10 + val % 0xfff0; + wpabuf_free(*pubkey); + *pubkey = pub; + wpabuf_free(*privkey); + *privkey = priv; + wpabuf_free(*dev_pw); + *dev_pw = pw; + + ret = wps_build_nfc_pw_token(*id, *pubkey, *dev_pw); + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} +#endif /* CONFIG_WPS_NFC */ diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h index e128a195..2f42603a 100644 --- a/src/wps/wps_defs.h +++ b/src/wps/wps_defs.h @@ -41,7 +41,7 @@ extern int wps_testing_dummy_cred; #define WPS_MGMTAUTHKEY_LEN 32 #define WPS_MGMTENCKEY_LEN 16 #define WPS_MGMT_KEY_ID_LEN 16 -#define WPS_OOB_DEVICE_PASSWORD_ATTR_LEN 54 +#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16 #define WPS_OOB_DEVICE_PASSWORD_LEN 32 #define WPS_OOB_PUBKEY_HASH_LEN 20 diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c index 559582dd..3c94a434 100644 --- a/src/wps/wps_dev_attr.c +++ b/src/wps/wps_dev_attr.c @@ -203,6 +203,20 @@ int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg) } +int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg) +{ + if (dev->vendor_ext_m1 != NULL) { + wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension M1", + wpabuf_head_u8(dev->vendor_ext_m1), + wpabuf_len(dev->vendor_ext_m1)); + wpabuf_put_be16(msg, ATTR_VENDOR_EXT); + wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1)); + wpabuf_put_buf(msg, dev->vendor_ext_m1); + } + return 0; +} + + int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg) { wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands); diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h index 7ca81ad6..200c9c45 100644 --- a/src/wps/wps_dev_attr.h +++ b/src/wps/wps_dev_attr.h @@ -17,6 +17,7 @@ int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg); +int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg); diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index 85d2e958..da0c101b 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -163,7 +163,8 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_config_error(msg, WPS_CFG_NO_ERROR) || wps_build_os_version(&wps->wps->dev, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_vendor_ext_m1(&wps->wps->dev, msg)) { wpabuf_free(msg); return NULL; } diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index 0655a3a4..95a0dec0 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - External Registrar - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -1996,3 +1996,41 @@ int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, return 0; } + + +#ifdef CONFIG_WPS_NFC +struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid) +{ + struct wps_er_ap *ap; + struct wpabuf *ret; + struct wps_data data; + + if (er == NULL) + return NULL; + + ap = wps_er_ap_get(er, NULL, uuid); + if (ap == NULL) + return NULL; + if (ap->ap_settings == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the " + "selected AP"); + return NULL; + } + + ret = wpabuf_alloc(500); + if (ret == NULL) + return NULL; + + os_memset(&data, 0, sizeof(data)); + data.wps = er->wps; + data.use_cred = ap->ap_settings; + if (wps_build_version(ret) || + wps_build_cred(&data, ret) || + wps_build_wfa_ext(ret, 0, NULL, 0)) { + wpabuf_free(ret); + return NULL; + } + + return ret; +} +#endif /* CONFIG_WPS_NFC */ diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index 1297f658..86ad248d 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - internal definitions - * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,9 @@ #define WPS_I_H #include "wps.h" +#include "wps_attr_parse.h" + +struct wps_nfc_pw_token; /** * struct wps_data - WPS registration protocol data @@ -114,100 +117,11 @@ struct wps_data { u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or * 00:00:00:00:00:00 if not a P2p client */ int pbc_in_m1; -}; - -struct wps_parse_attr { - /* fixed length fields */ - const u8 *version; /* 1 octet */ - const u8 *version2; /* 1 octet */ - const u8 *msg_type; /* 1 octet */ - const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */ - const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */ - const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */ - const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */ - const u8 *auth_type_flags; /* 2 octets */ - const u8 *encr_type_flags; /* 2 octets */ - const u8 *conn_type_flags; /* 1 octet */ - const u8 *config_methods; /* 2 octets */ - const u8 *sel_reg_config_methods; /* 2 octets */ - const u8 *primary_dev_type; /* 8 octets */ - const u8 *rf_bands; /* 1 octet */ - const u8 *assoc_state; /* 2 octets */ - const u8 *config_error; /* 2 octets */ - const u8 *dev_password_id; /* 2 octets */ - const u8 *oob_dev_password; /* WPS_OOB_DEVICE_PASSWORD_ATTR_LEN (54) - * octets */ - const u8 *os_version; /* 4 octets */ - const u8 *wps_state; /* 1 octet */ - const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */ - const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */ - const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */ - const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */ - const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */ - const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ - const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ - const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */ - const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */ - const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */ - const u8 *auth_type; /* 2 octets */ - const u8 *encr_type; /* 2 octets */ - const u8 *network_idx; /* 1 octet */ - const u8 *network_key_idx; /* 1 octet */ - const u8 *mac_addr; /* ETH_ALEN (6) octets */ - const u8 *key_prov_auto; /* 1 octet (Bool) */ - const u8 *dot1x_enabled; /* 1 octet (Bool) */ - const u8 *selected_registrar; /* 1 octet (Bool) */ - const u8 *request_type; /* 1 octet */ - const u8 *response_type; /* 1 octet */ - const u8 *ap_setup_locked; /* 1 octet */ - const u8 *settings_delay_time; /* 1 octet */ - const u8 *network_key_shareable; /* 1 octet (Bool) */ - const u8 *request_to_enroll; /* 1 octet (Bool) */ - - /* variable length fields */ - const u8 *manufacturer; - size_t manufacturer_len; - const u8 *model_name; - size_t model_name_len; - const u8 *model_number; - size_t model_number_len; - const u8 *serial_number; - size_t serial_number_len; - const u8 *dev_name; - size_t dev_name_len; - const u8 *public_key; - size_t public_key_len; - const u8 *encr_settings; - size_t encr_settings_len; - const u8 *ssid; /* <= 32 octets */ - size_t ssid_len; - const u8 *network_key; /* <= 64 octets */ - size_t network_key_len; - const u8 *eap_type; /* <= 8 octets */ - size_t eap_type_len; - const u8 *eap_identity; /* <= 64 octets */ - size_t eap_identity_len; - const u8 *authorized_macs; /* <= 30 octets */ - size_t authorized_macs_len; - const u8 *sec_dev_type_list; /* <= 128 octets */ - size_t sec_dev_type_list_len; - - /* attributes that can occur multiple times */ -#define MAX_CRED_COUNT 10 - const u8 *cred[MAX_CRED_COUNT]; - size_t cred_len[MAX_CRED_COUNT]; - size_t num_cred; - -#define MAX_REQ_DEV_TYPE_COUNT 10 - const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; - size_t num_req_dev_type; - - const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT]; - size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT]; - size_t num_vendor_ext; + struct wps_nfc_pw_token *nfc_pw_token; }; + /* wps_common.c */ void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len, const char *label, u8 *res, size_t res_len); @@ -230,9 +144,6 @@ extern struct oob_nfc_device_data oob_nfc_pn531_device_data; struct wpabuf * wps_build_wsc_ack(struct wps_data *wps); struct wpabuf * wps_build_wsc_nack(struct wps_data *wps); -/* wps_attr_parse.c */ -int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); - /* wps_attr_build.c */ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg); int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type); @@ -255,6 +166,9 @@ int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg); +int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, + const struct wpabuf *pubkey, const u8 *dev_pw, + size_t dev_pw_len); int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps); struct wpabuf * wps_ie_encapsulate(struct wpabuf *data); @@ -288,9 +202,7 @@ void wps_registrar_selected_registrar_changed(struct wps_registrar *reg); const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count); int wps_registrar_pbc_overlap(struct wps_registrar *reg, const u8 *addr, const u8 *uuid_e); - -/* ndef.c */ -struct wpabuf * ndef_parse_wifi(struct wpabuf *buf); -struct wpabuf * ndef_build_wifi(struct wpabuf *buf); +void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg, + struct wps_nfc_pw_token *token); #endif /* WPS_I_H */ diff --git a/src/wps/wps_nfc.c b/src/wps/wps_nfc.c index ff120002..68043503 100644 --- a/src/wps/wps_nfc.c +++ b/src/wps/wps_nfc.c @@ -1,15 +1,9 @@ /* * NFC routines for Wi-Fi Protected Setup - * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> + * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/wps/wps_nfc_pn531.c b/src/wps/wps_nfc_pn531.c index 7e05e4dd..c2bf4570 100644 --- a/src/wps/wps_nfc_pn531.c +++ b/src/wps/wps_nfc_pn531.c @@ -1,15 +1,9 @@ /* * NFC PN531 routines for Wi-Fi Protected Setup - * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> + * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 5ed7ea4a..44bb0063 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - Registrar - * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -26,6 +26,53 @@ #define WPS_WORKAROUNDS #endif /* CONFIG_WPS_STRICT */ +#ifdef CONFIG_WPS_NFC + +struct wps_nfc_pw_token { + struct dl_list list; + u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; + u16 pw_id; + u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN]; + size_t dev_pw_len; +}; + + +static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token) +{ + dl_list_del(&token->list); + os_free(token); +} + + +static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id) +{ + struct wps_nfc_pw_token *token, *prev; + dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token, + list) { + if (pw_id == 0 || pw_id == token->pw_id) + wps_remove_nfc_pw_token(token); + } +} + + +static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens, + u16 pw_id) +{ + struct wps_nfc_pw_token *token; + dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) { + if (pw_id == token->pw_id) + return token; + } + return NULL; +} + +#else /* CONFIG_WPS_NFC */ + +#define wps_free_nfc_pw_tokens(t, p) do { } while (0) + +#endif /* CONFIG_WPS_NFC */ + + struct wps_uuid_pin { struct dl_list list; u8 uuid[WPS_UUID_LEN]; @@ -102,7 +149,8 @@ struct wps_registrar { void (*pin_needed_cb)(void *ctx, const u8 *uuid_e, const struct wps_device_data *dev); void (*reg_success_cb)(void *ctx, const u8 *mac_addr, - const u8 *uuid_e); + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len); void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods); void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e, @@ -112,6 +160,7 @@ struct wps_registrar { void *cb_ctx; struct dl_list pins; + struct dl_list nfc_pw_tokens; struct wps_pbc_session *pbc_sessions; int skip_cred_build; @@ -484,12 +533,16 @@ static void wps_set_pushbutton(u16 *methods, u16 conf_methods) { *methods |= WPS_CONFIG_PUSHBUTTON; #ifdef CONFIG_WPS2 - if (conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) + if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) == + WPS_CONFIG_VIRT_PUSHBUTTON) *methods |= WPS_CONFIG_VIRT_PUSHBUTTON; - if (conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) + if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) == + WPS_CONFIG_PHY_PUSHBUTTON) *methods |= WPS_CONFIG_PHY_PUSHBUTTON; - if (!(*methods & (WPS_CONFIG_VIRT_PUSHBUTTON | - WPS_CONFIG_PHY_PUSHBUTTON))) { + if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) != + WPS_CONFIG_VIRT_PUSHBUTTON && + (*methods & WPS_CONFIG_PHY_PUSHBUTTON) != + WPS_CONFIG_PHY_PUSHBUTTON) { /* * Required to include virtual/physical flag, but we were not * configured with push button type, so have to default to one @@ -591,6 +644,7 @@ wps_registrar_init(struct wps_context *wps, return NULL; dl_list_init(®->pins); + dl_list_init(®->nfc_pw_tokens); reg->wps = wps; reg->new_psk_cb = cfg->new_psk_cb; reg->set_ie_cb = cfg->set_ie_cb; @@ -634,6 +688,7 @@ void wps_registrar_deinit(struct wps_registrar *reg) eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); wps_free_pins(®->pins); + wps_free_nfc_pw_tokens(®->nfc_pw_tokens, 0); wps_free_pbc_sessions(reg->pbc_sessions); wpabuf_free(reg->extra_cred); wps_free_devices(reg->devices); @@ -740,14 +795,22 @@ static void wps_registrar_expire_pins(struct wps_registrar *reg) /** * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN * @reg: Registrar data from wps_registrar_init() + * @dev_pw: PIN to search for or %NULL to match any + * @dev_pw_len: Length of dev_pw in octets * Returns: 0 on success, -1 if not wildcard PIN is enabled */ -static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg) +static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg, + const u8 *dev_pw, + size_t dev_pw_len) { struct wps_uuid_pin *pin, *prev; dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) { + if (dev_pw && pin->pin && + (dev_pw_len != pin->pin_len || + os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0)) + continue; /* different PIN */ if (pin->wildcard_uuid) { wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID", pin->uuid, WPS_UUID_LEN); @@ -945,7 +1008,8 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg) } -void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e) +void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e, + const u8 *dev_pw, size_t dev_pw_len) { if (registrar->pbc) { wps_registrar_remove_pbc_session(registrar, @@ -954,6 +1018,13 @@ void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e) } else { wps_registrar_pin_completed(registrar); } + + if (dev_pw && + wps_registrar_invalidate_wildcard_pin(registrar, dev_pw, + dev_pw_len) == 0) { + wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN", + dev_pw, dev_pw_len); + } } @@ -968,7 +1039,7 @@ int wps_registrar_wps_cancel(struct wps_registrar *reg) /* PIN Method */ wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it"); wps_registrar_pin_completed(reg); - wps_registrar_invalidate_wildcard_pin(reg); + wps_registrar_invalidate_wildcard_pin(reg, NULL, 0); return 1; } return 0; @@ -1071,12 +1142,13 @@ static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e, static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr, - const u8 *uuid_e) + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len) { if (reg->reg_success_cb == NULL) return; - reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e); + reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len); } @@ -1238,6 +1310,13 @@ static int wps_get_dev_password(struct wps_data *wps) wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC"); pin = (const u8 *) "00000000"; pin_len = 8; +#ifdef CONFIG_WPS_NFC + } else if (wps->nfc_pw_token) { + wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC " + "Password Token"); + pin = wps->nfc_pw_token->dev_pw; + pin_len = wps->nfc_pw_token->dev_pw_len; +#endif /* CONFIG_WPS_NFC */ } else { pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e, &pin_len); @@ -2424,8 +2503,34 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, return WPS_CONTINUE; } +#ifdef CONFIG_WPS_NFC + if (wps->dev_pw_id >= 0x10) { + struct wps_nfc_pw_token *token; + const u8 *addr[1]; + u8 hash[WPS_HASH_LEN]; + + token = wps_get_nfc_pw_token( + &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id); + if (token) { + wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " + "Password Token"); + dl_list_del(&token->list); + wps->nfc_pw_token = token; + + addr[0] = attr->public_key; + sha256_vector(1, addr, &attr->public_key_len, hash); + if (os_memcmp(hash, wps->nfc_pw_token->pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN) != 0) { + wpa_printf(MSG_ERROR, "WPS: Public Key hash " + "mismatch"); + return WPS_FAILURE; + } + } + } +#endif /* CONFIG_WPS_NFC */ + #ifdef CONFIG_WPS_OOB - if (wps->dev_pw_id >= 0x10 && + if (wps->dev_pw_id >= 0x10 && wps->nfc_pw_token == NULL && wps->dev_pw_id != wps->wps->oob_dev_pw_id) { wpa_printf(MSG_DEBUG, "WPS: OOB Device Password ID " "%d mismatch", wps->dev_pw_id); @@ -3055,7 +3160,8 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, wps->new_psk = NULL; } - wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e); + wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e, + wps->dev_password, wps->dev_password_len); if (wps->pbc) { wps_registrar_remove_pbc_session(wps->wps->registrar, @@ -3348,3 +3454,84 @@ int wps_registrar_config_ap(struct wps_registrar *reg, return -1; } + + +#ifdef CONFIG_WPS_NFC + +int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, + const u8 *pubkey_hash, u16 pw_id, + const u8 *dev_pw, size_t dev_pw_len) +{ + struct wps_nfc_pw_token *token; + + if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN) + return -1; + + wps_free_nfc_pw_tokens(®->nfc_pw_tokens, pw_id); + + token = os_zalloc(sizeof(*token)); + if (token == NULL) + return -1; + + os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); + token->pw_id = pw_id; + os_memcpy(token->dev_pw, dev_pw, dev_pw_len); + token->dev_pw_len = dev_pw_len; + + dl_list_add(®->nfc_pw_tokens, &token->list); + + reg->selected_registrar = 1; + reg->pbc = 0; + wps_registrar_add_authorized_mac(reg, + (u8 *) "\xff\xff\xff\xff\xff\xff"); + wps_registrar_selected_registrar_changed(reg); + eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, + wps_registrar_set_selected_timeout, + reg, NULL); + + return 0; +} + + +int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, + const u8 *oob_dev_pw, + size_t oob_dev_pw_len) +{ + const u8 *pos, *hash, *dev_pw; + u16 id; + size_t dev_pw_len; + + if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_MIN_LEN || + oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN) + return -1; + + hash = oob_dev_pw; + pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN; + id = WPA_GET_BE16(pos); + dev_pw = pos + 2; + dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw; + + wpa_printf(MSG_DEBUG, "WPS: Add NFC Password Token for Password ID %u", + id); + + wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash", + hash, WPS_OOB_PUBKEY_HASH_LEN); + wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len); + + return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw, + dev_pw_len); +} + + +void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg, + struct wps_nfc_pw_token *token) +{ + wps_registrar_remove_authorized_mac(reg, + (u8 *) "\xff\xff\xff\xff\xff\xff"); + wps_registrar_selected_registrar_changed(reg); +} + +#endif /* CONFIG_WPS_NFC */ diff --git a/src/wps/wps_ufd.c b/src/wps/wps_ufd.c index 61f65530..f83bdf47 100644 --- a/src/wps/wps_ufd.c +++ b/src/wps/wps_ufd.c @@ -1,15 +1,9 @@ /* * UFD routines for Wi-Fi Protected Setup - * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> + * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -165,8 +159,10 @@ static void * init_ufd(struct wps_context *wps, } data = os_zalloc(sizeof(*data)); - if (data == NULL) + if (data == NULL) { + close(ufd_fd); return NULL; + } data->ufd_fd = ufd_fd; return data; } diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index 766cac49..09a46a21 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -305,15 +305,15 @@ static void subscr_addr_add_url(struct subscription *s, const char *url, int alloc_len; char *scratch_mem = NULL; char *mem; - char *domain_and_port; + char *host; char *delim; char *path; - char *domain; int port = 80; /* port to send to (default is port 80) */ struct addrinfo hints; struct addrinfo *result = NULL; struct addrinfo *rp; int rerr; + size_t host_len, path_len; /* url MUST begin with http: */ if (url_len < 7 || os_strncasecmp(url, "http://", 7)) @@ -321,30 +321,24 @@ static void subscr_addr_add_url(struct subscription *s, const char *url, url += 7; url_len -= 7; - /* allocate memory for the extra stuff we need */ - alloc_len = 2 * (url_len + 1); - scratch_mem = os_zalloc(alloc_len); + /* Make a copy of the string to allow modification during parsing */ + scratch_mem = os_malloc(url_len + 1); if (scratch_mem == NULL) goto fail; - mem = scratch_mem; - os_strncpy(mem, url, url_len); - wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", mem); - domain_and_port = mem; - mem += 1 + os_strlen(mem); - delim = os_strchr(domain_and_port, '/'); + os_memcpy(scratch_mem, url, url_len); + scratch_mem[url_len] = '\0'; + wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem); + host = scratch_mem; + path = os_strchr(host, '/'); + if (path) + *path++ = '\0'; /* null terminate host */ + + /* Process and remove optional port component */ + delim = os_strchr(host, ':'); if (delim) { - *delim++ = 0; /* null terminate domain and port */ - path = delim; - } else { - path = domain_and_port + os_strlen(domain_and_port); - } - domain = mem; - strcpy(domain, domain_and_port); - delim = os_strchr(domain, ':'); - if (delim) { - *delim++ = 0; /* null terminate domain */ - if (isdigit(*delim)) - port = atol(delim); + *delim = '\0'; /* null terminate host name for now */ + if (isdigit(delim[1])) + port = atol(delim + 1); } /* @@ -367,13 +361,21 @@ static void subscr_addr_add_url(struct subscription *s, const char *url, hints.ai_flags = 0; #endif hints.ai_protocol = 0; /* Any protocol? */ - rerr = getaddrinfo(domain, NULL /* fill in port ourselves */, + rerr = getaddrinfo(host, NULL /* fill in port ourselves */, &hints, &result); if (rerr) { wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s", - rerr, gai_strerror(rerr), domain); + rerr, gai_strerror(rerr), host); goto fail; } + + if (delim) + *delim = ':'; /* Restore port */ + + host_len = os_strlen(host); + path_len = path ? os_strlen(path) : 0; + alloc_len = host_len + 1 + 1 + path_len + 1; + for (rp = result; rp; rp = rp->ai_next) { struct subscr_addr *a; @@ -386,16 +388,16 @@ static void subscr_addr_add_url(struct subscription *s, const char *url, a = os_zalloc(sizeof(*a) + alloc_len); if (a == NULL) - continue; - mem = (void *) (a + 1); + break; + mem = (char *) (a + 1); a->domain_and_port = mem; - strcpy(mem, domain_and_port); - mem += 1 + strlen(mem); + os_memcpy(mem, host, host_len); + mem += host_len + 1; a->path = mem; - if (path[0] != '/') + if (path == NULL || path[0] != '/') *mem++ = '/'; - strcpy(mem, path); - mem += 1 + os_strlen(mem); + if (path) + os_memcpy(mem, path, path_len); os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr)); a->saddr.sin_port = htons(port); diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c index 4c4aebf6..17a82074 100644 --- a/src/wps/wps_upnp_ssdp.c +++ b/src/wps/wps_upnp_ssdp.c @@ -866,20 +866,24 @@ int ssdp_open_multicast_sock(u32 ip_addr) return -1; #if 0 /* maybe ok if we sometimes block on writes */ - if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) + if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) { + close(sd); return -1; + } #endif if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &ip_addr, sizeof(ip_addr))) { wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: " "%d (%s)", ip_addr, errno, strerror(errno)); + close(sd); return -1; } if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) { wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): " "%d (%s)", errno, strerror(errno)); + close(sd); return -1; } @@ -898,6 +902,7 @@ int ssdp_open_multicast_sock(u32 ip_addr) "WPS UPnP: setsockopt " "IP_ADD_MEMBERSHIP errno %d (%s)", errno, strerror(errno)); + close(sd); return -1; } } |
